corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) 2006-2014, openmetaverse.org
3 * All rights reserved.
4 *
5 * - Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * - Neither the name of the openmetaverse.org nor the names
11 * of its contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26  
27 using System;
28 using System.IO;
29 using System.Drawing;
30 using System.Drawing.Imaging;
31 using System.Runtime.InteropServices;
32  
33 namespace OpenMetaverse.Imaging
34 {
35 #if !NO_UNSAFE
36 /// <summary>
37 /// A Wrapper around openjpeg to encode and decode images to and from byte arrays
38 /// </summary>
39 public class OpenJPEG
40 {
41 /// <summary>TGA Header size</summary>
42 public const int TGA_HEADER_SIZE = 32;
43  
44 #region JPEG2000 Structs
45  
46 /// <summary>
47 /// Defines the beginning and ending file positions of a layer in an
48 /// LRCP-progression JPEG2000 file
49 /// </summary>
50 [System.Diagnostics.DebuggerDisplay("Start = {Start} End = {End} Size = {End - Start}")]
51 [StructLayout(LayoutKind.Sequential, Pack = 4)]
52 public struct J2KLayerInfo
53 {
54 public int Start;
55 public int End;
56 }
57  
58 /// <summary>
59 /// This structure is used to marshal both encoded and decoded images.
60 /// MUST MATCH THE STRUCT IN dotnet.h!
61 /// </summary>
62 [StructLayout(LayoutKind.Sequential, Pack = 4)]
63 private struct MarshalledImage
64 {
65 public IntPtr encoded; // encoded image data
66 public int length; // encoded image length
67 public int dummy; // padding for 64-bit alignment
68  
69 public IntPtr decoded; // decoded image, contiguous components
70  
71 public int width; // width of decoded image
72 public int height; // height of decoded image
73 public int layers; // layer count
74 public int resolutions; // resolution count
75 public int components; // component count
76 public int packet_count; // packet count
77 public IntPtr packets; // pointer to the packets array
78 }
79  
80 /// <summary>
81 /// Information about a single packet in a JPEG2000 stream
82 /// </summary>
83 [StructLayout(LayoutKind.Sequential, Pack = 4)]
84 private struct MarshalledPacket
85 {
86 /// <summary>Packet start position</summary>
87 public int start_pos;
88 /// <summary>Packet header end position</summary>
89 public int end_ph_pos;
90 /// <summary>Packet end position</summary>
91 public int end_pos;
92  
93 public override string ToString()
94 {
95 return String.Format("start_pos: {0} end_ph_pos: {1} end_pos: {2}",
96 start_pos, end_ph_pos, end_pos);
97 }
98 }
99  
100 #endregion JPEG2000 Structs
101  
102 #region Unmanaged Function Declarations
103  
104  
105 // allocate encoded buffer based on length field
106 [System.Security.SuppressUnmanagedCodeSecurity]
107 [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
108 private static extern bool DotNetAllocEncoded(ref MarshalledImage image);
109  
110 // allocate decoded buffer based on width and height fields
111 [System.Security.SuppressUnmanagedCodeSecurity]
112 [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
113 private static extern bool DotNetAllocDecoded(ref MarshalledImage image);
114  
115 // free buffers
116 [System.Security.SuppressUnmanagedCodeSecurity]
117 [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
118 private static extern bool DotNetFree(ref MarshalledImage image);
119  
120 // encode raw to jpeg2000
121 [System.Security.SuppressUnmanagedCodeSecurity]
122 [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
123 private static extern bool DotNetEncode(ref MarshalledImage image, bool lossless);
124  
125 // decode jpeg2000 to raw
126 [System.Security.SuppressUnmanagedCodeSecurity]
127 [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
128 private static extern bool DotNetDecode(ref MarshalledImage image);
129  
130 // decode jpeg2000 to raw, get jpeg2000 file info
131 [System.Security.SuppressUnmanagedCodeSecurity]
132 [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)]
133 private static extern bool DotNetDecodeWithInfo(ref MarshalledImage image);
134  
135 // invoke 64 bit openjpeg calls
136 [System.Security.SuppressUnmanagedCodeSecurity]
137 [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
138 private static extern bool DotNetAllocEncoded64(ref MarshalledImage image);
139  
140 // allocate decoded buffer based on width and height fields
141 [System.Security.SuppressUnmanagedCodeSecurity]
142 [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
143 private static extern bool DotNetAllocDecoded64(ref MarshalledImage image);
144  
145 // free buffers
146 [System.Security.SuppressUnmanagedCodeSecurity]
147 [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
148 private static extern bool DotNetFree64(ref MarshalledImage image);
149  
150 // encode raw to jpeg2000
151 [System.Security.SuppressUnmanagedCodeSecurity]
152 [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
153 private static extern bool DotNetEncode64(ref MarshalledImage image, bool lossless);
154  
155 // decode jpeg2000 to raw
156 [System.Security.SuppressUnmanagedCodeSecurity]
157 [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
158 private static extern bool DotNetDecode64(ref MarshalledImage image);
159  
160 // decode jpeg2000 to raw, get jpeg2000 file info
161 [System.Security.SuppressUnmanagedCodeSecurity]
162 [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)]
163 private static extern bool DotNetDecodeWithInfo64(ref MarshalledImage image);
164 #endregion Unmanaged Function Declarations
165  
166 /// <summary>OpenJPEG is not threadsafe, so this object is used to lock
167 /// during calls into unmanaged code</summary>
168 private static object OpenJPEGLock = new object();
169  
170 /// <summary>
171 /// Encode a <seealso cref="ManagedImage"/> object into a byte array
172 /// </summary>
173 /// <param name="image">The <seealso cref="ManagedImage"/> object to encode</param>
174 /// <param name="lossless">true to enable lossless conversion, only useful for small images ie: sculptmaps</param>
175 /// <returns>A byte array containing the encoded Image object</returns>
176 public static byte[] Encode(ManagedImage image, bool lossless)
177 {
178 if ((image.Channels & ManagedImage.ImageChannels.Color) == 0 ||
179 ((image.Channels & ManagedImage.ImageChannels.Bump) != 0 && (image.Channels & ManagedImage.ImageChannels.Alpha) == 0))
180 throw new ArgumentException("JPEG2000 encoding is not supported for this channel combination");
181  
182 byte[] encoded = null;
183 MarshalledImage marshalled = new MarshalledImage();
184  
185 // allocate and copy to input buffer
186 marshalled.width = image.Width;
187 marshalled.height = image.Height;
188 marshalled.components = 3;
189 if ((image.Channels & ManagedImage.ImageChannels.Alpha) != 0) marshalled.components++;
190 if ((image.Channels & ManagedImage.ImageChannels.Bump) != 0) marshalled.components++;
191  
192 lock (OpenJPEGLock)
193 {
194  
195 bool allocSuccess = (IntPtr.Size == 8) ? DotNetAllocDecoded64(ref marshalled) : DotNetAllocDecoded(ref marshalled);
196  
197 if (!allocSuccess)
198 throw new Exception("DotNetAllocDecoded failed");
199  
200 int n = image.Width * image.Height;
201  
202 if ((image.Channels & ManagedImage.ImageChannels.Color) != 0)
203 {
204 Marshal.Copy(image.Red, 0, marshalled.decoded, n);
205 Marshal.Copy(image.Green, 0, (IntPtr)(marshalled.decoded.ToInt64() + n), n);
206 Marshal.Copy(image.Blue, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 2), n);
207 }
208  
209 if ((image.Channels & ManagedImage.ImageChannels.Alpha) != 0) Marshal.Copy(image.Alpha, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 3), n);
210 if ((image.Channels & ManagedImage.ImageChannels.Bump) != 0) Marshal.Copy(image.Bump, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 4), n);
211  
212 // codec will allocate output buffer
213 bool encodeSuccess = (IntPtr.Size == 8) ? DotNetEncode64(ref marshalled, lossless) : DotNetEncode(ref marshalled, lossless);
214 if (!encodeSuccess)
215 throw new Exception("DotNetEncode failed");
216  
217 // copy output buffer
218 encoded = new byte[marshalled.length];
219 Marshal.Copy(marshalled.encoded, encoded, 0, marshalled.length);
220  
221 // free buffers
222 if (IntPtr.Size == 8)
223 DotNetFree64(ref marshalled);
224 else
225 DotNetFree(ref marshalled);
226 }
227  
228 return encoded;
229 }
230  
231 /// <summary>
232 /// Encode a <seealso cref="ManagedImage"/> object into a byte array
233 /// </summary>
234 /// <param name="image">The <seealso cref="ManagedImage"/> object to encode</param>
235 /// <returns>a byte array of the encoded image</returns>
236 public static byte[] Encode(ManagedImage image)
237 {
238 return Encode(image, false);
239 }
240  
241 /// <summary>
242 /// Decode JPEG2000 data to an <seealso cref="System.Drawing.Image"/> and
243 /// <seealso cref="ManagedImage"/>
244 /// </summary>
245 /// <param name="encoded">JPEG2000 encoded data</param>
246 /// <param name="managedImage">ManagedImage object to decode to</param>
247 /// <param name="image">Image object to decode to</param>
248 /// <returns>True if the decode succeeds, otherwise false</returns>
249 public static bool DecodeToImage(byte[] encoded, out ManagedImage managedImage, out Image image)
250 {
251 managedImage = null;
252 image = null;
253  
254 if (DecodeToImage(encoded, out managedImage))
255 {
256 try
257 {
258 image = managedImage.ExportBitmap();
259 return true;
260 }
261 catch (Exception ex)
262 {
263 Logger.Log("Failed to export and load TGA data from decoded image", Helpers.LogLevel.Error, ex);
264 return false;
265 }
266 }
267 else
268 {
269 return false;
270 }
271 }
272  
273 /// <summary>
274 ///
275 /// </summary>
276 /// <param name="encoded"></param>
277 /// <param name="managedImage"></param>
278 /// <returns></returns>
279 public static bool DecodeToImage(byte[] encoded, out ManagedImage managedImage)
280 {
281 MarshalledImage marshalled = new MarshalledImage();
282  
283 // Allocate and copy to input buffer
284 marshalled.length = encoded.Length;
285  
286 lock (OpenJPEGLock)
287 {
288 if (IntPtr.Size == 8)
289 DotNetAllocEncoded64(ref marshalled);
290 else
291 DotNetAllocEncoded(ref marshalled);
292  
293 Marshal.Copy(encoded, 0, marshalled.encoded, encoded.Length);
294  
295 // Codec will allocate output buffer
296 if (IntPtr.Size == 8)
297 DotNetDecode64(ref marshalled);
298 else
299 DotNetDecode(ref marshalled);
300  
301 int n = marshalled.width * marshalled.height;
302  
303 switch (marshalled.components)
304 {
305 case 1: // Grayscale
306 managedImage = new ManagedImage(marshalled.width, marshalled.height,
307 ManagedImage.ImageChannels.Color);
308 Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
309 Buffer.BlockCopy(managedImage.Red, 0, managedImage.Green, 0, n);
310 Buffer.BlockCopy(managedImage.Red, 0, managedImage.Blue, 0, n);
311 break;
312  
313 case 2: // Grayscale + alpha
314 managedImage = new ManagedImage(marshalled.width, marshalled.height,
315 ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha);
316 Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
317 Buffer.BlockCopy(managedImage.Red, 0, managedImage.Green, 0, n);
318 Buffer.BlockCopy(managedImage.Red, 0, managedImage.Blue, 0, n);
319 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Alpha, 0, n);
320 break;
321  
322 case 3: // RGB
323 managedImage = new ManagedImage(marshalled.width, marshalled.height,
324 ManagedImage.ImageChannels.Color);
325 Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
326 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n);
327 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n);
328 break;
329  
330 case 4: // RGBA
331 managedImage = new ManagedImage(marshalled.width, marshalled.height,
332 ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha);
333 Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
334 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n);
335 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n);
336 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 3)), managedImage.Alpha, 0, n);
337 break;
338  
339 case 5: // RGBAB
340 managedImage = new ManagedImage(marshalled.width, marshalled.height,
341 ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha | ManagedImage.ImageChannels.Bump);
342 Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n);
343 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n);
344 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n);
345 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 3)), managedImage.Alpha, 0, n);
346 Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 4)), managedImage.Bump, 0, n);
347 break;
348  
349 default:
350 Logger.Log("Decoded image with unhandled number of components: " + marshalled.components,
351 Helpers.LogLevel.Error);
352  
353 if (IntPtr.Size == 8)
354 DotNetFree64(ref marshalled);
355 else
356 DotNetFree(ref marshalled);
357  
358 managedImage = null;
359 return false;
360 }
361  
362 if (IntPtr.Size == 8)
363 DotNetFree64(ref marshalled);
364 else
365 DotNetFree(ref marshalled);
366 }
367  
368 return true;
369 }
370  
371 /// <summary>
372 ///
373 /// </summary>
374 /// <param name="encoded"></param>
375 /// <param name="layerInfo"></param>
376 /// <param name="components"></param>
377 /// <returns></returns>
378 public static bool DecodeLayerBoundaries(byte[] encoded, out J2KLayerInfo[] layerInfo, out int components)
379 {
380 bool success = false;
381 layerInfo = null;
382 components = 0;
383 MarshalledImage marshalled = new MarshalledImage();
384  
385 // Allocate and copy to input buffer
386 marshalled.length = encoded.Length;
387  
388 lock (OpenJPEGLock)
389 {
390 if (IntPtr.Size == 8)
391 DotNetAllocEncoded64(ref marshalled);
392 else
393 DotNetAllocEncoded(ref marshalled);
394  
395 Marshal.Copy(encoded, 0, marshalled.encoded, encoded.Length);
396  
397 // Run the decode
398 bool decodeSuccess = (IntPtr.Size == 8) ? DotNetDecodeWithInfo64(ref marshalled) : DotNetDecodeWithInfo(ref marshalled);
399 if (decodeSuccess)
400 {
401 components = marshalled.components;
402  
403 // Sanity check
404 if (marshalled.layers * marshalled.resolutions * marshalled.components == marshalled.packet_count)
405 {
406 // Manually marshal the array of opj_packet_info structs
407 MarshalledPacket[] packets = new MarshalledPacket[marshalled.packet_count];
408 int offset = 0;
409  
410 for (int i = 0; i < marshalled.packet_count; i++)
411 {
412 MarshalledPacket packet;
413 packet.start_pos = Marshal.ReadInt32(marshalled.packets, offset);
414 offset += 4;
415 packet.end_ph_pos = Marshal.ReadInt32(marshalled.packets, offset);
416 offset += 4;
417 packet.end_pos = Marshal.ReadInt32(marshalled.packets, offset);
418 offset += 4;
419 //double distortion = (double)Marshal.ReadInt64(marshalled.packets, offset);
420 offset += 8;
421  
422 packets[i] = packet;
423 }
424  
425 layerInfo = new J2KLayerInfo[marshalled.layers];
426  
427 for (int i = 0; i < marshalled.layers; i++)
428 {
429 int packetsPerLayer = marshalled.packet_count / marshalled.layers;
430 MarshalledPacket startPacket = packets[packetsPerLayer * i];
431 MarshalledPacket endPacket = packets[(packetsPerLayer * (i + 1)) - 1];
432 layerInfo[i].Start = startPacket.start_pos;
433 layerInfo[i].End = endPacket.end_pos;
434 }
435  
436 // More sanity checking
437 if (layerInfo.Length == 0 || layerInfo[layerInfo.Length - 1].End <= encoded.Length - 1)
438 {
439 success = true;
440  
441 for (int i = 0; i < layerInfo.Length; i++)
442 {
443 if (layerInfo[i].Start >= layerInfo[i].End ||
444 (i > 0 && layerInfo[i].Start <= layerInfo[i - 1].End))
445 {
446 System.Text.StringBuilder output = new System.Text.StringBuilder(
447 "Inconsistent packet data in JPEG2000 stream:\n");
448 for (int j = 0; j < layerInfo.Length; j++)
449 output.AppendFormat("Layer {0}: Start: {1} End: {2}\n", j, layerInfo[j].Start, layerInfo[j].End);
450 Logger.DebugLog(output.ToString());
451  
452 success = false;
453 break;
454 }
455 }
456  
457 if (!success)
458 {
459 for (int i = 0; i < layerInfo.Length; i++)
460 {
461 if (i < layerInfo.Length - 1)
462 layerInfo[i].End = layerInfo[i + 1].Start - 1;
463 else
464 layerInfo[i].End = marshalled.length;
465 }
466  
467 Logger.DebugLog("Corrected JPEG2000 packet data");
468 success = true;
469  
470 for (int i = 0; i < layerInfo.Length; i++)
471 {
472 if (layerInfo[i].Start >= layerInfo[i].End ||
473 (i > 0 && layerInfo[i].Start <= layerInfo[i - 1].End))
474 {
475 System.Text.StringBuilder output = new System.Text.StringBuilder(
476 "Still inconsistent packet data in JPEG2000 stream, giving up:\n");
477 for (int j = 0; j < layerInfo.Length; j++)
478 output.AppendFormat("Layer {0}: Start: {1} End: {2}\n", j, layerInfo[j].Start, layerInfo[j].End);
479 Logger.DebugLog(output.ToString());
480  
481 success = false;
482 break;
483 }
484 }
485 }
486 }
487 else
488 {
489 Logger.Log(String.Format(
490 "Last packet end in JPEG2000 stream extends beyond the end of the file. filesize={0} layerend={1}",
491 encoded.Length, layerInfo[layerInfo.Length - 1].End), Helpers.LogLevel.Warning);
492 }
493 }
494 else
495 {
496 Logger.Log(String.Format(
497 "Packet count mismatch in JPEG2000 stream. layers={0} resolutions={1} components={2} packets={3}",
498 marshalled.layers, marshalled.resolutions, marshalled.components, marshalled.packet_count),
499 Helpers.LogLevel.Warning);
500 }
501 }
502  
503 if (IntPtr.Size == 8)
504 DotNetFree64(ref marshalled);
505 else
506 DotNetFree(ref marshalled);
507 }
508  
509 return success;
510 }
511  
512 /// <summary>
513 /// Encode a <seealso cref="System.Drawing.Bitmap"/> object into a byte array
514 /// </summary>
515 /// <param name="bitmap">The source <seealso cref="System.Drawing.Bitmap"/> object to encode</param>
516 /// <param name="lossless">true to enable lossless decoding</param>
517 /// <returns>A byte array containing the source Bitmap object</returns>
518 public unsafe static byte[] EncodeFromImage(Bitmap bitmap, bool lossless)
519 {
520 BitmapData bd;
521 ManagedImage decoded;
522  
523 int bitmapWidth = bitmap.Width;
524 int bitmapHeight = bitmap.Height;
525 int pixelCount = bitmapWidth * bitmapHeight;
526 int i;
527  
528 if ((bitmap.PixelFormat & PixelFormat.Alpha) != 0 || (bitmap.PixelFormat & PixelFormat.PAlpha) != 0)
529 {
530 // Four layers, RGBA
531 decoded = new ManagedImage(bitmapWidth, bitmapHeight,
532 ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha);
533 bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight),
534 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
535 byte* pixel = (byte*)bd.Scan0;
536  
537 for (i = 0; i < pixelCount; i++)
538 {
539 // GDI+ gives us BGRA and we need to turn that in to RGBA
540 decoded.Blue[i] = *(pixel++);
541 decoded.Green[i] = *(pixel++);
542 decoded.Red[i] = *(pixel++);
543 decoded.Alpha[i] = *(pixel++);
544 }
545 }
546 else if (bitmap.PixelFormat == PixelFormat.Format16bppGrayScale)
547 {
548 // One layer
549 decoded = new ManagedImage(bitmapWidth, bitmapHeight,
550 ManagedImage.ImageChannels.Color);
551 bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight),
552 ImageLockMode.ReadOnly, PixelFormat.Format16bppGrayScale);
553 byte* pixel = (byte*)bd.Scan0;
554  
555 for (i = 0; i < pixelCount; i++)
556 {
557 // Normalize 16-bit data down to 8-bit
558 ushort origVal = (byte)(*(pixel) + (*(pixel + 1) << 8));
559 byte val = (byte)(((double)origVal / (double)UInt32.MaxValue) * (double)Byte.MaxValue);
560  
561 decoded.Red[i] = val;
562 decoded.Green[i] = val;
563 decoded.Blue[i] = val;
564 pixel += 2;
565 }
566 }
567 else
568 {
569 // Three layers, RGB
570 decoded = new ManagedImage(bitmapWidth, bitmapHeight,
571 ManagedImage.ImageChannels.Color);
572 bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight),
573 ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
574 byte* pixel = (byte*)bd.Scan0;
575  
576 for (i = 0; i < pixelCount; i++)
577 {
578 decoded.Blue[i] = *(pixel++);
579 decoded.Green[i] = *(pixel++);
580 decoded.Red[i] = *(pixel++);
581 }
582 }
583  
584 bitmap.UnlockBits(bd);
585 byte[] encoded = Encode(decoded, lossless);
586 return encoded;
587 }
588 }
589 #endif
590 }