在RichTextBox中插入图像而不会丢失质量(WMF) [英] Insert image in RichTextBox without quality loss (WMF)

查看:146
本文介绍了在RichTextBox中插入图像而不会丢失质量(WMF)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述





我连续两天搜索整个互联网,在这个问题上几乎没有成功:



附加图像。 windows.forms.richtextbox.aspx> System.Windows.Forms.RichTextBox 类没有复制到剪贴板或任何其他技巧,我偶然发现了ExRichTextBox 课程由Khendys Gordon在http://www.codeproject.com



这个类可以转换图像键入相应的 RTF 字符串,用于在RichTextBox中显示它。



RTF的图片控件必须 .WMF 以二进制或十六进制表示的数据(这也是写字板插入图像时写字板的功能)。写字板和RichTextBox忽略RTF中图片的所有其他控制字(例如PNG,JPG等)。 请参阅 RTF规范中的第148页



现在出现问题: http:// puu .sh / 1XnKL.jpg (图片是19x25所以我放大了一点)



- 左边的一个使用ExRichTextBox从PNG转换为WMF



- 正确的一个直接添加到写字板(所以写字板完成转换)



现在,在ExRichTextBox类中,转换如下:



1.创建EMF
2.然后使用非托管代码函数转换为WMF(在GDI+ API)

3。最后将字节转换为十六进制格式



这是代码。此方法位于ExRichTextBox类中,该类继承自System.Windows.Forms.RichTextBox:



Hi,

I've searched the entire internet for two consecutive days, with little to no success on this issue:

To append images in the System.Windows.Forms.RichTextBox class without copying to clipboard or any other dirty tricks, I have stumbled upon the ExRichTextBox class by Khendys Gordon here on http://www.codeproject.com

This class can convert an Image type to the corresponding RTF string which is needed to display it in the RichTextBox.

The RTF's picture control MUST be .WMF data either in binary or hexadecimals (this is also what WordPad does when you insert an image). WordPad and the RichTextBox ignores every other control word (e.g. PNG, JPG etc.) for pictures in RTF. See page 148 in the RTF Specification

Now here is the problem: http://puu.sh/1XnKL.jpg (image was 19x25 so I zoomed a little bit)

- The left one is converted from PNG to WMF using ExRichTextBox

- The right one is directly added to WordPad (so WordPad did the conversion)

Now, in the ExRichTextBox class the conversion goes by the following:

1. Create EMF from the Bitmap/Image,
2. then convert to WMF by using unmanaged code functions (in GDI+ API)
3. finally convert the bytes to hexadecimals

Here is the code of the method which returns the WMF data when you pass an Image type. This method is in the ExRichTextBox class which inherits from System.Windows.Forms.RichTextBox:

[DllImportAttribute("gdiplus.dll")]
private static extern uint GdipEmfToWmfBits (IntPtr _hEmf, uint _bufferSize,
    byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);


private string GetRtfImage(Image _image) {

    StringBuilder _rtf = null;

    // Used to store the enhanced metafile
    MemoryStream _stream = null;

    // Used to create the metafile and draw the image
    Graphics _graphics = null;

    // The enhanced metafile
    Metafile _metaFile = null;

    // Handle to the device context used to create the metafile
    IntPtr _hdc;

    try {
        _rtf = new StringBuilder();
        _stream = new MemoryStream();

        // Get a graphics context from the RichTextBox
        using(_graphics = this.CreateGraphics()) {

            // Get the device context from the graphics context
            _hdc = _graphics.GetHdc();

            // Create a new Enhanced Metafile from the device context
            _metaFile = new Metafile(_stream, _hdc);

            // Release the device context
            _graphics.ReleaseHdc(_hdc);
        }

        // Get a graphics context from the Enhanced Metafile
        using(_graphics = Graphics.FromImage(_metaFile)) {

            // Draw the image on the Enhanced Metafile
            _graphics.DrawImage(_image, new Rectangle(0, 0, _image.Width,
                _image.Height));

        }

        // Get the handle of the Enhanced Metafile
        IntPtr _hEmf = _metaFile.GetHenhmetafile();

        // A call to EmfToWmfBits with a null buffer return the size of the
        // buffer need to store the WMF bits.  Use this to get the buffer
        // size.
        uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, MM_ANISOTROPIC,
            EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);

        // Create an array to hold the bits
        byte[] _buffer = new byte[_bufferSize];

        // A call to EmfToWmfBits with a valid buffer copies the bits into the
        // buffer an returns the number of bits in the WMF.  
        uint _convertedSize = GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer,
            MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);

        // Append the bits to the RTF string
        for(int i = 0; i < _buffer.Length; ++i) {
            _rtf.Append(String.Format("{0:X2}", _buffer[i]));
        }

        return _rtf.ToString();
    }
    finally {
        if(_graphics != null)
            _graphics.Dispose();
        if(_metaFile != null)
            _metaFile.Dispose();
        if(_stream != null)
            _stream.Close();
    }
}





问题为什么 WHERE 图像是否会失去质量?



Question: WHY and WHERE does the image lose its quality?

推荐答案

当你从EMF转换为WMF时,你是从32位文件转换为16位文件 - 什么是WMF? [ ^ ]。

因此,质量损失是不可避免的。
When you convert from EMF to WMF, you are converting from a 32bit file to 16bit file - What is WMF?[^].
As a result, loss in quality is unavoidable.


这是我用来将图像编码成RTF的代码。

这不是最好的代码,但它完成了工作。





This is the code I use to encode images into RTF.
It's not the nicest code, but it gets the job done.


public enum FormatHexStyles
{
    LittleEndian2Bytes,
    LittleEndian4Bytes,
    BigEndian2Bytes,
    BigEndian4Bytes
}

public static class ImageUtil
{
    public const int    HMM_PER_INCH    = 2540;
    public const int    TWIPS_PER_INCH  = 1440;
    public const float  DEFAULT_DPIX    = 96.0F;
    public const float  DEFAULT_DPIY    = 96.0F;

    public static readonly string[] HEX_X2_TABLE =
    {
        "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
        "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
        "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
        "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
        "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
        "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
        "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
        "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
        "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
        "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
        "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
        "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
        "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
        "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
        "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
        "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"
    };

    public static int RoundTo4ByteBoundary( int aValue )
    {
        // Check sign
        bool negative = false;
        if( aValue < 0 )
        {
            // Set negative flag and invert value
            negative = true;
            aValue = -aValue;
        }

        // Declare variables
        int real = aValue & ~0x3;
        int mod  = aValue & 0x3;

        // Check if we must increment real with boundary base
        if( mod != 0 )
            real += 0x4;

        // Check if we must apply sign
        if( negative )
            return -real;
        else
            return real;
    }

    public static string ConvertToHex( int aValue, FormatHexStyles aHexFormat )
    {
        // Check lower range
        if( aValue < 0 )
            throw new ArgumentOutOfRangeException( string.Format(
                "Invalid value '{0}' specified; negative values are not supported",
                aValue ));

        // Declare variables
        string hex;

        // Check required format
        switch( aHexFormat )
        {
            case FormatHexStyles.LittleEndian2Bytes:
                // Check upper range
                if( aValue > 0xFFFF )
                    throw new ArgumentOutOfRangeException( string.Format(
                        "Invalid value '{0}' specified; allowed range [0..{1}]",
                        aValue,
                        0xFFFF ));

                // Compute little endian 2 byte format
                hex =
                    HEX_X2_TABLE[aValue & 0xFF] +
                    HEX_X2_TABLE[(aValue & 0xFF00) >> 8];
                break;

            case FormatHexStyles.LittleEndian4Bytes:
                // Check upper range
                if( aValue > 0x7FFFFFFF )
                    throw new ArgumentOutOfRangeException( string.Format(
                        "Invalid value '{0}' specified; allowed range [0..{1}]",
                        aValue,
                        0x7FFFFFFF ));

                // Compute little endian 4 byte format
                hex =
                    HEX_X2_TABLE[aValue & 0xFF] +
                    HEX_X2_TABLE[(aValue & 0xFF00) >> 8] +
                    HEX_X2_TABLE[(aValue & 0xFF0000) >> 16] +
                    HEX_X2_TABLE[(aValue & 0x7F000000) >> 24];
                break;

            case FormatHexStyles.BigEndian2Bytes:
                // Check upper range
                if( aValue > 0xFFFF )
                    throw new ArgumentOutOfRangeException( string.Format(
                        "Invalid value '{0}' specified; allowed range [0..{1}]",
                        aValue,
                        0xFFFF ));

                // Compute big endian 2 byte format
                hex =
                    HEX_X2_TABLE[(aValue & 0xFF00) >> 8] +
                    HEX_X2_TABLE[aValue & 0xFF];
                break;

            case FormatHexStyles.BigEndian4Bytes:
                // Check upper range
                if( aValue > 0x7FFFFFFF )
                    throw new ArgumentOutOfRangeException( string.Format(
                        "Invalid value '{0}' specified; allowed range [0..{1}]",
                        aValue,
                        0x7FFFFFFF ));

                // Compute big endian 4 byte format
                hex =
                    HEX_X2_TABLE[(aValue & 0x7F000000) >> 24] +
                    HEX_X2_TABLE[(aValue & 0xFF0000) >> 16] +
                    HEX_X2_TABLE[(aValue & 0xFF00) >> 8] +
                    HEX_X2_TABLE[aValue & 0xFF];
                break;

            default:
                throw new InvalidOperationException( string.Format(
                    "Invalid FormatHexStyles encountered; {0} not supported",
                    aHexFormat ));
        }

        // Return converted hex
        return hex;
    }

    public static Size CalcSize( Image anImage, Size aNewSize )
    {
        // Check input
        if( anImage == null )
            return Size.Empty;

        // Declar variables
        Size size;

        // Determine resize with or without aspect
        if( aNewSize.Width <= 0 &&
            aNewSize.Height <= 0 )
        {
            // Keep original size
            size = new Size( anImage.Width, anImage.Height );
        }
        else if(
            aNewSize.Width > 0 &&
            aNewSize.Height > 0 )
        {
            // Resize without aspect
            size = aNewSize;
        }
        else if( aNewSize.Height <= 0 )
        {
            // Compute new height with aspect
            float f = (float)aNewSize.Width / (float)anImage.Width;
            int   h = (int)((float)anImage.Height * f);
            size = new Size(  aNewSize.Width, h );
        }
        else
        {
            // Compute new with with aspect
            float f = (float)aNewSize.Height / (float)anImage.Height;
            int   w = (int)((float)anImage.Width * f);
            size = new Size( w, aNewSize.Height );
        }

        // Return computed size
        return size;
    }

    public static string ConvertToRtf( Bitmap anImage, Color anImageBackcolor )
    {
        return ConvertToRtf(
            anImage,
            anImageBackcolor,
            anImage.Width,
            anImage.Height,
            DEFAULT_DPIX,
            DEFAULT_DPIY );
    }

    public static string ConvertToRtf(
        Bitmap anImage,
        Color aBackgroundColor,
        int aWidth,
        int aHeight,
        float aDpiX,
        float aDpiY )
    {
        // Check input
        if( anImage == null )
            return "<No image available>";

        // Declare variables
        int   intWidth  = anImage.Width;
        int   intHeight = anImage.Height;
        float w         = intWidth;
        float h         = intHeight;
        float hmm       = HMM_PER_INCH;
        float twip      = TWIPS_PER_INCH;

        // Calculate effective image dimensions (in 0.01mm)
        int picw        = (int)Math.Round((w/aDpiX)*hmm);
        int pich        = (int)Math.Round((h/aDpiY)*hmm);

        // Compute new desired size of image
        SizeF size      = CalcSize(anImage, new Size(aWidth,aHeight));

        // Calculate requested image dimensions (in twips)
        int picwgoal    = (int)Math.Round((size.Width/aDpiX)*twip);
        int pichgoal    = (int)Math.Round((size.Height/aDpiY)*twip);

        // Calculate bytesize (DWORD aligned)
        int sizeA       = ((RoundTo4ByteBoundary(intWidth*3)*intHeight)/2)+56;
        int sizeB       = sizeA - 0x16;
        int sizeC       = RoundTo4ByteBoundary(intWidth*3)*intHeight;

        // Declare RTF masks
        string maskRtfPicHeader = @"\pict\wmetafile8\picw{0}\pich{1}\picwgoal{2}\pichgoal{3} ";
        string maskRtfWmfHeader = @"010009000003{0}0000{1}0000050000000b0200000000050000000c02{2}{3}{1}430f2000cc000000{4}{5}00000000{2}{3}00000000";
        string maskRtfBmpHeader = @"28000000{0}{1}0100180000000000{2}c40e0000c40e00000000000000000000";
        string maskRtf          = @"{0}{1}{2}{3}{4}030000000000{5}";

        // Create headers
        string rtfPicHeader     = string.Format(
            maskRtfPicHeader,
            picw,
            pich,
            picwgoal,
            pichgoal );
        string rtfWmfHeader     = string.Format(
            maskRtfWmfHeader,
            ConvertToHex( sizeA, FormatHexStyles.LittleEndian4Bytes ),
            ConvertToHex( sizeB, FormatHexStyles.LittleEndian4Bytes ),
            ConvertToHex( pich, FormatHexStyles.LittleEndian2Bytes ),
            ConvertToHex( picw, FormatHexStyles.LittleEndian2Bytes ),
            ConvertToHex( intHeight, FormatHexStyles.LittleEndian2Bytes ),
            ConvertToHex( intWidth, FormatHexStyles.LittleEndian2Bytes ));
        string rtfBmpHeader     = string.Format(
            maskRtfBmpHeader,
            ConvertToHex( intWidth, FormatHexStyles.LittleEndian4Bytes ),
            ConvertToHex( intHeight, FormatHexStyles.LittleEndian4Bytes ),
            ConvertToHex( sizeC, FormatHexStyles.LittleEndian4Bytes ));

        // Obtain bitmap data
        BitmapData bmpData = anImage.LockBits(
            new Rectangle(0, 0, intWidth, intHeight),
            ImageLockMode.ReadOnly,
            anImage.PixelFormat );

        // Declare an array to hold the pixels of the bitmap.
        uint[] pixelsARGB   = new uint[intWidth*intHeight];

        unsafe
        {
            // Read bitmap pixels
            switch( anImage.PixelFormat )
            {
                case PixelFormat.Format32bppArgb:
                case PixelFormat.Format32bppPArgb:
                case PixelFormat.Format32bppRgb:
                    // Simply copy ARGB bitmap data into buffer
                    uint* ptrARGB = (uint*)bmpData.Scan0;
                    for( int y = 0; y < intHeight; y++ )
                    {
                        int yOffset = y*intWidth;
                        for( int x = 0; x < intWidth; x++ )
                            pixelsARGB[yOffset+x] = ptrARGB[yOffset+x];
                    }

                    // Release lock
                    anImage.UnlockBits( bmpData );
                    break;

                case PixelFormat.Format8bppIndexed:
                    // Use palette to map ARGB values
                    byte*   ptrPalette      = (byte*)bmpData.Scan0;
                    uint[]  colorsPalette   = new uint[anImage.Palette.Entries.Length];
                    for( int col=0; col < anImage.Palette.Entries.Length; col++ )
                        colorsPalette[col] = (uint)anImage.Palette.Entries[col].ToArgb();

                    // Use color palette as source to create ARGB array
                    for( int y = 0; y < intHeight; y++ )
                    {
                        int yOffset = y*intWidth;
                        for( int x = 0; x < intWidth; x++ )
                            pixelsARGB[yOffset+x] = colorsPalette[ptrPalette[yOffset+x]];
                    }

                    // Release lock
                    anImage.UnlockBits( bmpData );
                    break;

                default:
                    // Release lock first
                    anImage.UnlockBits( bmpData );

                    // In this case we default to the slow GetPixel method.
                    for( int y = 0; y < intHeight; y++ )
                    {
                        int yOffset = y*intWidth;
                        for( int x = 0; x < intWidth; x++ )
                            pixelsARGB[yOffset+x] = (uint)anImage.GetPixel(x,y).ToArgb();
                    }
                    break;
            }
        }

        // Determine if we need to blend colors
        bool    doBlend = aBackgroundColor != Color.Empty;
        float   backR   = aBackgroundColor.R;
        float   backG   = aBackgroundColor.G;
        float   backB   = aBackgroundColor.B;

        // Compute stride (i.e. 4byte alignment)
        int alignRGB    = intWidth*3;
        int alignDWORD  = RoundTo4ByteBoundary(alignRGB);
        int alignStride = alignDWORD-alignRGB;

        // Check if stride is required
        bool    hasStride   = alignStride > 0;
        string  stride      = null;
        if( hasStride )
        {
            // Align on DWORD boundary
            StringBuilder sbStride = new StringBuilder();
            for( int i = 0; i < alignStride; i++ )
                sbStride.Append( HEX_X2_TABLE[0] );
            stride = sbStride.ToString();
        }

        // Create data buffer
        StringBuilder sbData = new StringBuilder();
        for( int y = intHeight-1; y >= 0; y-- )
        {
            // Iterate image from last row up to first row
            int yOffset = y*intWidth;

            // Iterate row from left pixel to right pixel
            for( int x = 0; x < intWidth; x++ )
            {
                // Get ARGB pixel values
                uint    pix = pixelsARGB[yOffset+x];
                float   dr  = (pix & 0x00FF0000) >> 16;
                float   dg  = (pix & 0x0000FF00) >> 8;
                float   db  = (pix & 0x000000FF);

                // Blend pixel with background?
                if( doBlend )
                {
                    // Get A value and normaize (i.e. compute range [0..1])
                    float t = ((float)((pix & 0xFF000000) >> 24))/255F;

                    // Convert RGB into delta's
                    dr  -= backR;
                    dg  -= backG;
                    db  -= backB;

                    // Mix backcolor with forecolor
                    dr  = (dr*t) + backR;
                    dg  = (dg*t) + backG;
                    db  = (db*t) + backB;
                }

                // Write hex RGB to buffer
                sbData.Append( HEX_X2_TABLE[(int)db] );
                sbData.Append( HEX_X2_TABLE[(int)dg] );
                sbData.Append( HEX_X2_TABLE[(int)dr] );
            }

            // Align on DWORD boundary
            if( hasStride )
                sbData.Append( stride );
        }

        // Flush stringbuilder
        string rtfBmpData = sbData.ToString();

        // Compose entire chunk
        string rtfChunk = string.Format(
            maskRtf,
            '{',
            rtfPicHeader,
            rtfWmfHeader,
            rtfBmpHeader,
            rtfBmpData,
            '}' );

        // Return result
        return rtfChunk;
    }
}


Yes. I agree with you. It definitely does lose quality. Good luck solving your problem.
Yes. I agree with you. It definitely does lose quality. Good luck solving your problem.


这篇关于在RichTextBox中插入图像而不会丢失质量(WMF)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆