新建一个Silverlight应用程序,添加下面两个控件:
image控件:image1;
Button控件:Click="Button1_Click";
code-Behind代码如下:
1 private void Button1_Click(object sender, RoutedEventArgs e) 2 { 3 ElementToPNG eTP = new ElementToPNG(); 4 eTP.ShowSaveDialog(image1); 5 }
并添加下面三个类:
1 //* ------------------------------------------------------------------------------------------------- 2 //* 下面的三个类是处理图片导出的 3 //* ------------------------------------------------------------------------------------------------- 4 # region Class ElementToPNG 5 public class ElementToPNG 6 { 7 public void ShowSaveDialog(UIElement elementToExport) 8 { 9 SaveFileDialog sfd = new SaveFileDialog() 10 { 11 DefaultExt = "png", 12 Filter = "Png files (*.png)|*.png|All files (*.*)|*.*", 13 FilterIndex = 1 14 }; 15 16 if (sfd.ShowDialog() == true) 17 { 18 SaveAsPNG(sfd, elementToExport); 19 } 20 } 21 private void SaveAsPNG(SaveFileDialog sfd, UIElement elementToExport) 22 { 23 WriteableBitmap bitmap = new WriteableBitmap(elementToExport, new TranslateTransform()); 24 EditableImage imageData = new EditableImage(bitmap.PixelWidth, bitmap.PixelHeight); 25 try 26 { 27 for (int y = 0; y < bitmap.PixelHeight; ++y) 28 { 29 for (int x = 0; x < bitmap.PixelWidth; ++x) 30 { 31 int pixel = bitmap.Pixels[bitmap.PixelWidth * y + x]; 32 imageData.SetPixel(x, y, 33 (byte)((pixel >> 16) & 0xFF), 34 (byte)((pixel >> 8) & 0xFF), 35 (byte)(pixel & 0xFF), (byte)((pixel >> 24) & 0xFF) 36 ); 37 } 38 } 39 } 40 catch (System.Security.SecurityException) 41 { 42 throw new Exception("Cannot print images from other domains"); 43 } 44 Stream pngStream = imageData.GetStream(); 45 byte[] binaryData = new Byte[pngStream.Length]; 46 long bytesRead = pngStream.Read(binaryData, 0, (int)pngStream.Length); 47 Stream stream = sfd.OpenFile(); 48 stream.Write(binaryData, 0, binaryData.Length); 49 stream.Close(); 50 } 51 } 52 # endregion 53 54 # region Class EditableImage 55 public class EditableImage 56 { 57 private int _width = 0; 58 private int _height = 0; 59 private bool _init = false; 60 private byte[] _buffer; 61 private int _rowLength; 62 63 public event EventHandler<EditableImageErrorEventArgs> ImageError; 64 65 public EditableImage(int width, int height) 66 { 67 this.Width = width; 68 this.Height = height; 69 } 70 71 public int Width 72 { 73 get 74 { 75 return _width; 76 } 77 set 78 { 79 if (_init) 80 { 81 OnImageError("Error: Cannot change Width after the EditableImage has been initialized"); 82 } 83 else if ((value <= 0) || (value > 2047)) 84 { 85 OnImageError("Error: Width must be between 0 and 2047"); 86 } 87 else 88 { 89 _width = value; 90 } 91 } 92 } 93 94 public int Height 95 { 96 get 97 { 98 return _height; 99 } 100 set 101 { 102 if (_init) 103 { 104 OnImageError("Error: Cannot change Height after the EditableImage has been initialized"); 105 } 106 else if ((value <= 0) || (value > 2047)) 107 { 108 OnImageError("Error: Height must be between 0 and 2047"); 109 } 110 else 111 { 112 _height = value; 113 } 114 } 115 } 116 117 public void SetPixel(int col, int row, Color color) 118 { 119 SetPixel(col, row, color.R, color.G, color.B, color.A); 120 } 121 122 public void SetPixel(int col, int row, byte red, byte green, byte blue, byte alpha) 123 { 124 if (!_init) 125 { 126 _rowLength = _width * 4 + 1; 127 _buffer = new byte[_rowLength * _height]; 128 129 // Initialize 130 for (int idx = 0; idx < _height; idx++) 131 { 132 _buffer[idx * _rowLength] = 0; // Filter bit 133 } 134 135 _init = true; 136 } 137 138 if ((col > _width) || (col < 0)) 139 { 140 OnImageError("Error: Column must be greater than 0 and less than the Width"); 141 } 142 else if ((row > _height) || (row < 0)) 143 { 144 OnImageError("Error: Row must be greater than 0 and less than the Height"); 145 } 146 147 // Set the pixel 148 int start = _rowLength * row + col * 4 + 1; 149 _buffer[start] = red; 150 _buffer[start + 1] = green; 151 _buffer[start + 2] = blue; 152 _buffer[start + 3] = alpha; 153 } 154 155 public Color GetPixel(int col, int row) 156 { 157 if ((col > _width) || (col < 0)) 158 { 159 OnImageError("Error: Column must be greater than 0 and less than the Width"); 160 } 161 else if ((row > _height) || (row < 0)) 162 { 163 OnImageError("Error: Row must be greater than 0 and less than the Height"); 164 } 165 166 Color color = new Color(); 167 int _base = _rowLength * row + col + 1; 168 169 color.R = _buffer[_base]; 170 color.G = _buffer[_base + 1]; 171 color.B = _buffer[_base + 2]; 172 color.A = _buffer[_base + 3]; 173 174 return color; 175 } 176 177 public Stream GetStream() 178 { 179 Stream stream; 180 181 if (!_init) 182 { 183 OnImageError("Error: Image has not been initialized"); 184 stream = null; 185 } 186 else 187 { 188 stream = PngEncoder.Encode(_buffer, _width, _height); 189 } 190 191 return stream; 192 } 193 194 private void OnImageError(string msg) 195 { 196 if (null != ImageError) 197 { 198 EditableImageErrorEventArgs args = new EditableImageErrorEventArgs(); 199 args.ErrorMessage = msg; 200 ImageError(this, args); 201 } 202 } 203 204 public class EditableImageErrorEventArgs : EventArgs 205 { 206 private string _errorMessage = string.Empty; 207 208 public string ErrorMessage 209 { 210 get { return _errorMessage; } 211 set { _errorMessage = value; } 212 } 213 } 214 215 } 216 # endregion 217 218 # region Class PngEncoder 219 public class PngEncoder 220 { 221 private const int _ADLER32_BASE = 65521; 222 private const int _MAXBLOCK = 0xFFFF; 223 private static byte[] _HEADER = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; 224 private static byte[] _IHDR = { (byte)'I', (byte)'H', (byte)'D', (byte)'R' }; 225 private static byte[] _GAMA = { (byte)'g', (byte)'A', (byte)'M', (byte)'A' }; 226 private static byte[] _IDAT = { (byte)'I', (byte)'D', (byte)'A', (byte)'T' }; 227 private static byte[] _IEND = { (byte)'I', (byte)'E', (byte)'N', (byte)'D' }; 228 private static byte[] _4BYTEDATA = { 0, 0, 0, 0 }; 229 private static byte[] _ARGB = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 6, 0, 0, 0 }; 230 231 232 public static Stream Encode(byte[] data, int width, int height) 233 { 234 MemoryStream ms = new MemoryStream(); 235 byte[] size; 236 237 // Write PNG header 238 ms.Write(_HEADER, 0, _HEADER.Length); 239 240 size = BitConverter.GetBytes(width); 241 _ARGB[0] = size[3]; _ARGB[1] = size[2]; _ARGB[2] = size[1]; _ARGB[3] = size[0]; 242 243 size = BitConverter.GetBytes(height); 244 _ARGB[4] = size[3]; _ARGB[5] = size[2]; _ARGB[6] = size[1]; _ARGB[7] = size[0]; 245 246 // Write IHDR chunk 247 WriteChunk(ms, _IHDR, _ARGB); 248 249 // Set gamma = 1 250 size = BitConverter.GetBytes(1 * 100000); 251 _4BYTEDATA[0] = size[3]; _4BYTEDATA[1] = size[2]; _4BYTEDATA[2] = size[1]; _4BYTEDATA[3] = size[0]; 252 253 // Write gAMA chunk 254 WriteChunk(ms, _GAMA, _4BYTEDATA); 255 256 // Write IDAT chunk 257 uint widthLength = (uint)(width * 4) + 1; 258 uint dcSize = widthLength * (uint)height; 259 260 uint adler = ComputeAdler32(data); 261 MemoryStream comp = new MemoryStream(); 262 263 // Calculate number of 64K blocks 264 uint rowsPerBlock = _MAXBLOCK / widthLength; 265 uint blockSize = rowsPerBlock * widthLength; 266 uint blockCount; 267 ushort length; 268 uint remainder = dcSize; 269 270 if ((dcSize % blockSize) == 0) 271 { 272 blockCount = dcSize / blockSize; 273 } 274 else 275 { 276 blockCount = (dcSize / blockSize) + 1; 277 } 278 279 // Write headers 280 comp.WriteByte(0x78); 281 comp.WriteByte(0xDA); 282 283 for (uint blocks = 0; blocks < blockCount; blocks++) 284 { 285 // Write LEN 286 length = (ushort)((remainder < blockSize) ? remainder : blockSize); 287 288 if (length == remainder) 289 { 290 comp.WriteByte(0x01); 291 } 292 else 293 { 294 comp.WriteByte(0x00); 295 } 296 297 comp.Write(BitConverter.GetBytes(length), 0, 2); 298 299 // Write one's compliment of LEN 300 comp.Write(BitConverter.GetBytes((ushort)~length), 0, 2); 301 302 // Write blocks 303 comp.Write(data, (int)(blocks * blockSize), length); 304 305 // Next block 306 remainder -= blockSize; 307 } 308 309 WriteReversedBuffer(comp, BitConverter.GetBytes(adler)); 310 comp.Seek(0, SeekOrigin.Begin); 311 312 byte[] dat = new byte[comp.Length]; 313 comp.Read(dat, 0, (int)comp.Length); 314 315 WriteChunk(ms, _IDAT, dat); 316 317 // Write IEND chunk 318 WriteChunk(ms, _IEND, new byte[0]); 319 320 // Reset stream 321 ms.Seek(0, SeekOrigin.Begin); 322 323 return ms; 324 } 325 326 private static void WriteReversedBuffer(Stream stream, byte[] data) 327 { 328 int size = data.Length; 329 byte[] reorder = new byte[size]; 330 331 for (int idx = 0; idx < size; idx++) 332 { 333 reorder[idx] = data[size - idx - 1]; 334 } 335 stream.Write(reorder, 0, size); 336 } 337 338 private static void WriteChunk(Stream stream, byte[] type, byte[] data) 339 { 340 int idx; 341 int size = type.Length; 342 byte[] buffer = new byte[type.Length + data.Length]; 343 344 // Initialize buffer 345 for (idx = 0; idx < type.Length; idx++) 346 { 347 buffer[idx] = type[idx]; 348 } 349 350 for (idx = 0; idx < data.Length; idx++) 351 { 352 buffer[idx + size] = data[idx]; 353 } 354 355 // Write length 356 WriteReversedBuffer(stream, BitConverter.GetBytes(data.Length)); 357 358 // Write type and data 359 stream.Write(buffer, 0, buffer.Length); // Should always be 4 bytes 360 361 // Compute and write the CRC 362 WriteReversedBuffer(stream, BitConverter.GetBytes(GetCRC(buffer))); 363 } 364 private static uint[] _crcTable = new uint[256]; 365 private static bool _crcTableComputed = false; 366 private static void MakeCRCTable() 367 { 368 uint c; 369 for (int n = 0; n < 256; n++) 370 { 371 c = (uint)n; 372 for (int k = 0; k < 8; k++) 373 { 374 if ((c & (0x00000001)) > 0) 375 c = 0xEDB88320 ^ (c >> 1); 376 else 377 c = c >> 1; 378 } 379 _crcTable[n] = c; 380 } 381 382 _crcTableComputed = true; 383 } 384 private static uint UpdateCRC(uint crc, byte[] buf, int len) 385 { 386 uint c = crc; 387 388 if (!_crcTableComputed) 389 { 390 MakeCRCTable(); 391 } 392 393 for (int n = 0; n < len; n++) 394 { 395 c = _crcTable[(c ^ buf[n]) & 0xFF] ^ (c >> 8); 396 } 397 398 return c; 399 } 400 private static uint GetCRC(byte[] buf) 401 { 402 return UpdateCRC(0xFFFFFFFF, buf, buf.Length) ^ 0xFFFFFFFF; 403 } 404 private static uint ComputeAdler32(byte[] buf) 405 { 406 uint s1 = 1; 407 uint s2 = 0; 408 int length = buf.Length; 409 410 for (int idx = 0; idx < length; idx++) 411 { 412 s1 = (s1 + (uint)buf[idx]) % _ADLER32_BASE; 413 s2 = (s2 + s1) % _ADLER32_BASE; 414 } 415 return (s2 << 16) + s1; 416 } 417 } 418 # endregion 419 //* ------------------------------------------------------------------------------------------------- 420 //* -------------------------------------------------------------------------------------------------