Пример #1
0
    public static unsafe byte[] GetBytes(BitmapSource bitmap, uint bcrFlags)
    {
        uint stride = StructUtil.CalcStride((ushort)bitmap.Format.BitsPerPixel, bitmap.PixelWidth);

        byte[] buffer = new byte[stride * bitmap.PixelHeight];
        bitmap.CopyPixels(buffer, (int)stride, 0);

        var clrs = bitmap.Palette == null ? null : bitmap.Palette.Colors.Select(c => new RGBQUAD {
            rgbRed = c.R, rgbBlue = c.B, rgbGreen = c.G
        }).ToArray();

        BITMAP_WRITE_REQUEST req = new BITMAP_WRITE_REQUEST
        {
            dpiX          = bitmap.DpiX,
            dpiY          = bitmap.DpiY,
            imgWidth      = bitmap.PixelWidth,
            imgHeight     = bitmap.PixelHeight,
            imgStride     = stride,
            imgTopDown    = true,
            imgColorTable = clrs,
        };

        BITMASKS masks = default;

        uint getBitmask(IList <byte> mask)
        {
            uint result = 0;
            int  shift  = 0;

            for (int i = 0; i < mask.Count; i++)
            {
                result = result | (uint)(mask[i] << shift);
                shift += 8;
            }
            return(result);
        }

        if (bitmap.Format.Masks != null && bitmap.Format.Masks.Count == 3)
        {
            var wpfmasks = bitmap.Format.Masks;
            masks.maskBlue  = getBitmask(wpfmasks[0].Mask);
            masks.maskGreen = getBitmask(wpfmasks[1].Mask);
            masks.maskRed   = getBitmask(wpfmasks[2].Mask);
        }
        else if (bitmap.Format.Masks != null && bitmap.Format.Masks.Count == 4)
        {
            var wpfmasks = bitmap.Format.Masks;
            masks.maskBlue  = getBitmask(wpfmasks[0].Mask);
            masks.maskGreen = getBitmask(wpfmasks[1].Mask);
            masks.maskRed   = getBitmask(wpfmasks[2].Mask);
            masks.maskAlpha = getBitmask(wpfmasks[3].Mask);
        }

        fixed(byte *ptr = buffer)
        return(BitmapCore.WriteToBMP(ref req, ptr, masks, (ushort)bitmap.Format.BitsPerPixel, bcrFlags));
    }
Пример #2
0
    /// <inheritdoc/>
    public override byte[] GetBytes(Bitmap bitmap, BitmapWriterFlags wFlags)
    {
        // default - this will cause GDI to convert the pixel format to bgra32 if we don't know the format directly
        var gdiFmt  = PixelFormat.Format32bppArgb;
        var coreFmt = BitmapCorePixelFormat.Bgra32;

        var pxarr = Formats.Where(f => f.gdiFmt == bitmap.PixelFormat).ToArray();

        if (pxarr.Length > 0)
        {
            var px = pxarr.First();
            gdiFmt  = px.gdiFmt;
            coreFmt = px.coreFmt;
        }

        var colorTable = bitmap.Palette.Entries.Select(e => new RGBQUAD {
            rgbBlue = e.B, rgbGreen = e.G, rgbRed = e.R
        }).ToArray();

        var dlock = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, gdiFmt);
        var buf   = (byte *)dlock.Scan0;

        BITMAP_WRITE_REQUEST req = new BITMAP_WRITE_REQUEST
        {
            dpiX          = 0,
            dpiY          = 0,
            imgWidth      = bitmap.Width,
            imgHeight     = bitmap.Height,
            imgStride     = (uint)dlock.Stride,
            imgTopDown    = true,
            imgColorTable = colorTable,
        };

        var bytes = BitmapCore.WriteToBMP(ref req, buf, coreFmt, (uint)wFlags);

        bitmap.UnlockBits(dlock);
        return(bytes);
    }
Пример #3
0
    public static byte[] WriteToBMP(ref BITMAP_WRITE_REQUEST info, byte *sourceBufferStart, BITMASKS masks, ushort nbits, uint bcwFlags)
    {
        bool skipFileHeader = (bcwFlags & BC_WRITE_SKIP_FH) > 0;
        bool forceV5        = (bcwFlags & BC_WRITE_V5) > 0;
        bool forceInfo      = (bcwFlags & BC_WRITE_VINFO) > 0;

        if (forceV5 && forceInfo)
        {
            throw new ArgumentException("ForceV5 and ForceInfo flags can not be used at the same time.");
        }

        // NOT SUPPORTED RIGHT NOW
        bool iccEmbed = false;

        byte[] iccProfileData = new byte[0];

        var hasAlpha = masks.maskAlpha != 0;

        if (nbits < 16 && (info.imgColorTable == null || info.imgColorTable.Length == 0))
        {
            throw new InvalidOperationException("A indexed bitmap must have a color table / palette.");
        }

        //int dpiToPelsPM(double dpi)
        //{
        //    if (Math.Round(dpi) == 96d) return 0;
        //    return (int)Math.Round(dpi / 0.0254d);
        //}

        uint paletteSize = info.imgColorTable == null ? 0 : (uint)info.imgColorTable.Length;

        var fhSize   = (uint)Marshal.SizeOf <BITMAPFILEHEADER>();
        var quadSize = (uint)Marshal.SizeOf <RGBQUAD>();

        byte[] buffer;
        uint   pxOffset, pxSize;

        // BI_BITFIELDS is not valid for 24bpp, so if the masks are not RGB we need to use a V5 header.
        //var nonStandard24bpp = nbits == 24 && !BitmapCorePixelFormat2.Bgr24.IsMatch(24, masks);

        BitmapCompressionMode compr = BitmapCompressionMode.BI_RGB;

        // some parsers do not respect the v5 header masks unless BI_BITFIELDS is used...
        // this is true of Chrome (only for 16bpp) and is also true of FireFox (16 and 32bpp)
        if (nbits == 16 || nbits == 32)
        {
            compr = BitmapCompressionMode.BI_BITFIELDS;
        }

        // write V5 header if embedded color profile or has alpha data
        if (forceV5 || hasAlpha || iccEmbed)
        {
            var v5Size = (uint)Marshal.SizeOf <BITMAPV5HEADER>();
            // Typical structure:
            // - BITMAPFILEHEADER (Optional)
            // - BITMAPV5HEADER
            // - * Note, never write BI_BITFIELDS at the end of a V5 header, these masks are contained within the header itself
            // - Color Table (Optional)
            // - Pixel Data
            // - Embedded Color Profile (Optional)

            var fh = new BITMAPFILEHEADER
            {
                bfType = BFH_BM,
            };

            var v5 = new BITMAPV5HEADER
            {
                bV5Size          = v5Size,
                bV5Planes        = 1,
                bV5BitCount      = nbits,
                bV5Height        = info.imgTopDown ? -info.imgHeight : info.imgHeight,
                bV5Width         = info.imgWidth,
                bV5Compression   = compr,
                bV5XPelsPerMeter = 0,
                bV5YPelsPerMeter = 0,

                bV5RedMask   = masks.maskRed,
                bV5GreenMask = masks.maskGreen,
                bV5BlueMask  = masks.maskBlue,
                bV5AlphaMask = masks.maskAlpha,

                bV5ClrImportant = paletteSize,
                bV5ClrUsed      = paletteSize,
                bV5SizeImage    = (uint)(info.imgStride * info.imgHeight),

                bV5CSType = ColorSpaceType.LCS_sRGB,
                bV5Intent = Bv5Intent.LCS_GM_IMAGES,
            };

            uint offset = skipFileHeader ? 0 : fhSize;
            offset += v5Size;
            offset += paletteSize * quadSize;

            // fh offset points to beginning of pixel data
            fh.bfOffBits = pxOffset = offset;
            pxSize       = v5.bV5SizeImage;

            offset += v5.bV5SizeImage;

            if (iccEmbed)
            {
                v5.bV5CSType      = ColorSpaceType.PROFILE_EMBEDDED;
                v5.bV5ProfileData = offset;
                v5.bV5ProfileSize = (uint)iccProfileData.Length;
                offset           += v5.bV5ProfileSize;
            }

            // fh size must be total file size
            fh.bfSize = offset;

            buffer = new byte[offset];
            offset = 0;

            if (!skipFileHeader)
            {
                StructUtil.SerializeTo(fh, buffer, ref offset);
            }

            StructUtil.SerializeTo(v5, buffer, ref offset);

            if (info.imgColorTable != null)
            {
                foreach (var p in info.imgColorTable)
                {
                    StructUtil.SerializeTo(p, buffer, ref offset);
                }
            }

            Marshal.Copy((IntPtr)sourceBufferStart, buffer, (int)offset, (int)v5.bV5SizeImage);
            offset += v5.bV5SizeImage;

            if (iccEmbed)
            {
                Buffer.BlockCopy(iccProfileData, 0, buffer, (int)offset, iccProfileData.Length);
            }
        }
        else
        {
            var infoSize = (uint)Marshal.SizeOf <BITMAPINFOHEADER>();
            // Typical structure:
            // - BITMAPFILEHEADER (Optional)
            // - BITMAPINFOHEADER
            // - BI_BITFIELDS (Optional)
            // - Color Table (Optional)
            // - Pixel Data

            // this would be ideal, we can specify transparency in VINFO headers... but many applications incl FireFox do not support this.
            // if (hasAlpha) compr = BitmapCompressionMode.BI_ALPHABITFIELDS;

            var fh = new BITMAPFILEHEADER
            {
                bfType = BFH_BM,
            };

            var vinfo = new BITMAPINFOHEADER
            {
                bV5Size          = infoSize,
                bV5Planes        = 1,
                bV5BitCount      = nbits,
                bV5Height        = info.imgTopDown ? -info.imgHeight : info.imgHeight,
                bV5Width         = info.imgWidth,
                bV5Compression   = compr,
                bV5XPelsPerMeter = 0,
                bV5YPelsPerMeter = 0,

                bV5ClrImportant = paletteSize,
                bV5ClrUsed      = paletteSize,
                bV5SizeImage    = (uint)(info.imgStride * info.imgHeight),
            };

            uint offset = skipFileHeader ? 0 : fhSize;
            offset += infoSize;

            if (compr == BitmapCompressionMode.BI_BITFIELDS)
            {
                offset += sizeof(uint) * 3;
            }

            offset += paletteSize * quadSize;

            // fh offset points to beginning of pixel data
            fh.bfOffBits = pxOffset = offset;
            pxSize       = vinfo.bV5SizeImage;

            offset += vinfo.bV5SizeImage;

            // fh size must be total file size
            fh.bfSize = offset;

            buffer = new byte[offset];
            offset = 0;

            if (!skipFileHeader)
            {
                StructUtil.SerializeTo(fh, buffer, ref offset);
            }

            StructUtil.SerializeTo(vinfo, buffer, ref offset);

            if (compr == BitmapCompressionMode.BI_BITFIELDS)
            {
                Buffer.BlockCopy(masks.BITFIELDS(), 0, buffer, (int)offset, sizeof(uint) * 3);
                offset += sizeof(uint) * 3;
            }

            if (info.imgColorTable != null)
            {
                foreach (var p in info.imgColorTable)
                {
                    StructUtil.SerializeTo(p, buffer, ref offset);
                }
            }

            Marshal.Copy((IntPtr)sourceBufferStart, buffer, (int)offset, (int)vinfo.bV5SizeImage);
        }

        return(buffer);
    }
Пример #4
0
 public static byte[] WriteToBMP(ref BITMAP_WRITE_REQUEST info, byte *sourceBufferStart, BitmapCorePixelFormat fmt, uint bcwFlags)
 {
     return(WriteToBMP(ref info, sourceBufferStart, fmt.Masks, fmt.BitsPerPixel, bcwFlags));
 }