在RichTextBox中插入图像而不会丢失质量(WMF) [英] Insert image in RichTextBox without quality loss (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屋!