PNGCodec.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. //
  2. // System.Drawing.Imaging.PNGCodec.cs
  3. //
  4. // Author:
  5. // Alexandre Pigolkine ([email protected])
  6. //
  7. namespace System.Drawing.Imaging
  8. {
  9. using System;
  10. using System.IO;
  11. using System.Drawing.Imaging;
  12. using System.Runtime.InteropServices;
  13. using cdeclCallback;
  14. /// <summary>
  15. /// Summary description for PNGCodec.
  16. /// </summary>
  17. internal class PNGCodec
  18. {
  19. enum PNG_LIB : int {
  20. PNG_COLOR_MASK_PALETTE =1,
  21. PNG_COLOR_MASK_COLOR =2,
  22. PNG_COLOR_MASK_ALPHA =4,
  23. PNG_COLOR_TYPE_GRAY =0,
  24. PNG_COLOR_TYPE_PALETTE =(PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE),
  25. PNG_COLOR_TYPE_RGB =(PNG_COLOR_MASK_COLOR),
  26. PNG_COLOR_TYPE_RGB_ALPHA =(PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA),
  27. PNG_COLOR_TYPE_GRAY_ALPHA =(PNG_COLOR_MASK_ALPHA),
  28. PNG_COLOR_TYPE_RGBA =PNG_COLOR_TYPE_RGB_ALPHA,
  29. PNG_COLOR_TYPE_GA =PNG_COLOR_TYPE_GRAY_ALPHA,
  30. PNG_COMPRESSION_TYPE_BASE =0, /* Deflate method 8, 32K window */
  31. PNG_COMPRESSION_TYPE_DEFAULT =PNG_COMPRESSION_TYPE_BASE,
  32. PNG_FILTER_TYPE_BASE =0, /* Single row per-byte filtering */
  33. PNG_INTRAPIXEL_DIFFERENCING =64, /* Used only in MNG datastreams */
  34. PNG_FILTER_TYPE_DEFAULT =PNG_FILTER_TYPE_BASE,
  35. PNG_INTERLACE_NONE =0, /* Non-interlaced image */
  36. PNG_INTERLACE_ADAM7 =1, /* Adam7 interlacing */
  37. PNG_INTERLACE_LAST =2, /* Not a valid value */
  38. PNG_OFFSET_PIXEL =0, /* Offset in pixels */
  39. PNG_OFFSET_MICROMETER =1, /* Offset in micrometers (1/10^6 meter) */
  40. PNG_OFFSET_LAST =2, /* Not a valid value */
  41. PNG_EQUATION_LINEAR =0, /* Linear transformation */
  42. PNG_EQUATION_BASE_E =1, /* Exponential base e transform */
  43. PNG_EQUATION_ARBITRARY =2, /* Arbitrary base exponential transform */
  44. PNG_EQUATION_HYPERBOLIC =3, /* Hyperbolic sine transformation */
  45. PNG_EQUATION_LAST =4, /* Not a valid value */
  46. PNG_SCALE_UNKNOWN =0, /* unknown unit (image scale) */
  47. PNG_SCALE_METER =1, /* meters per pixel */
  48. PNG_SCALE_RADIAN =2, /* radians per pixel */
  49. PNG_SCALE_LAST =3, /* Not a valid value */
  50. PNG_RESOLUTION_UNKNOWN =0, /* pixels/unknown unit (aspect ratio) */
  51. PNG_RESOLUTION_METER =1, /* pixels/meter */
  52. PNG_RESOLUTION_LAST =2 /* Not a valid value */
  53. }
  54. const string PNG_LIBPNG_VER_STRING = "1.2.2";
  55. const string PNGLibrary = "png";
  56. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  57. internal static extern IntPtr png_create_read_struct (string user_png_ver, IntPtr error_ptr,
  58. cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtr error_fn,
  59. cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtr warn_fn);
  60. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  61. internal static extern IntPtr png_create_write_struct (string user_png_ver, IntPtr error_ptr,
  62. cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtr error_fn,
  63. cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtr warn_fn);
  64. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  65. internal static extern IntPtr png_create_info_struct (IntPtr png_ptr);
  66. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  67. internal static extern void png_destroy_read_struct (ref IntPtr png_ptr, ref IntPtr info_ptr_ptr, ref IntPtr end_info_ptr_ptr);
  68. [DllImport(PNGLibrary, EntryPoint="png_destroy_read_struct", CallingConvention=CallingConvention.Cdecl)]
  69. internal static extern void png_destroy_read_struct1 (ref IntPtr png_ptr, IntPtr info_ptr_ptr, IntPtr end_info_ptr_ptr);
  70. [DllImport(PNGLibrary, EntryPoint="png_destroy_read_struct", CallingConvention=CallingConvention.Cdecl)]
  71. internal static extern void png_destroy_read_struct2 (ref IntPtr png_ptr, ref IntPtr info_ptr_ptr, IntPtr end_info_ptr_ptr);
  72. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  73. internal static extern void png_destroy_write_struct (ref IntPtr png_ptr, ref IntPtr info_ptr_ptr);
  74. [DllImport(PNGLibrary, EntryPoint="png_destroy_write_struct", CallingConvention=CallingConvention.Cdecl)]
  75. internal static extern void png_destroy_write_struct1 (ref IntPtr png_ptr, IntPtr info_ptr_ptr);
  76. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  77. internal static extern void png_set_read_fn (IntPtr png_ptr, IntPtr io_ptr, cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtrInt read_data_fn);
  78. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  79. internal static extern void png_set_write_fn (IntPtr png_ptr, IntPtr io_ptr,
  80. cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtrInt write_data_fn,
  81. cdeclCallback.cdeclRedirector.MethodVoidIntPtr output_flush_fn);
  82. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  83. internal static extern void png_read_info (IntPtr png_ptr, IntPtr info_ptr);
  84. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  85. internal static extern void png_write_info (IntPtr png_ptr, IntPtr info_ptr);
  86. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  87. internal static extern void png_read_end (IntPtr png_ptr, IntPtr end_info);
  88. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  89. internal static extern void png_write_end (IntPtr png_ptr, IntPtr end_info);
  90. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  91. internal static extern int png_get_rowbytes (IntPtr png_ptr, IntPtr info_ptr);
  92. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  93. internal static extern int png_get_image_width (IntPtr png_ptr, IntPtr info_ptr);
  94. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  95. internal static extern int png_get_image_height (IntPtr png_ptr, IntPtr info_ptr);
  96. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  97. internal static extern byte png_get_bit_depth (IntPtr png_ptr, IntPtr info_ptr);
  98. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  99. internal static extern byte png_get_color_type (IntPtr png_ptr, IntPtr info_ptr);
  100. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  101. internal static extern void png_set_IHDR (IntPtr png_ptr, IntPtr info_ptr, int width, int height,
  102. int bit_depth, int color_type, int interlace_method, int compression_method,
  103. int filter_method);
  104. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  105. internal static extern void png_read_row (IntPtr png_ptr, IntPtr row_data, IntPtr display_row);
  106. [DllImport(PNGLibrary, CallingConvention=CallingConvention.Cdecl)]
  107. internal static extern void png_write_row (IntPtr png_ptr, IntPtr row_data);
  108. internal PNGCodec() {
  109. }
  110. internal static ImageCodecInfo CodecInfo {
  111. get {
  112. ImageCodecInfo info = new ImageCodecInfo();
  113. info.Flags = ImageCodecFlags.Encoder | ImageCodecFlags.Decoder | ImageCodecFlags.Builtin | ImageCodecFlags.SupportBitmap;
  114. info.FormatDescription = "PNG file format";
  115. info.FormatID = System.Drawing.Imaging.ImageFormat.Png.Guid;
  116. info.MimeType = "image/png";
  117. info.Version = 1;
  118. byte[][] signaturePatterns = new byte[1][];
  119. signaturePatterns[0] = new byte[]{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a};
  120. info.SignaturePatterns = signaturePatterns;
  121. byte[][] signatureMasks = new byte[1][];
  122. signatureMasks[0] = new byte[]{0xff,0xff,0xff,0xff,0xff,0xff};
  123. info.SignatureMasks = signatureMasks;
  124. info.decode += new ImageCodecInfo.DecodeFromStream(PNGCodec.DecodeDelegate);
  125. info.encode += new ImageCodecInfo.EncodeToStream(PNGCodec.EncodeDelegate);
  126. return info;
  127. }
  128. }
  129. void error_function (IntPtr png_structp, IntPtr png_const_charp)
  130. {
  131. // FIXME: set exception parameters
  132. throw new Exception();
  133. }
  134. void warning_function (IntPtr png_structp, IntPtr png_const_charp)
  135. {
  136. // FIXME: dump error somewhere
  137. }
  138. Stream fs;
  139. void read_data_fn (IntPtr png_structp, IntPtr bytep, int size)
  140. {
  141. byte[] result = new byte[size];
  142. int readed = fs.Read(result, 0, size);
  143. Marshal.Copy(result, 0, bytep, readed);
  144. }
  145. void write_data_fn (IntPtr png_structp, IntPtr bytep, int size)
  146. {
  147. // FIXME: shall we have a buffer as a member variable here ?
  148. byte[] result = new byte[size];
  149. Marshal.Copy(bytep, result, 0, size);
  150. fs.Write(result, 0, size);
  151. }
  152. void output_flush_fn (IntPtr png_structp)
  153. {
  154. fs.Flush();
  155. }
  156. internal static void DecodeDelegate (Image image, Stream stream, BitmapData info)
  157. {
  158. PNGCodec png = new PNGCodec();
  159. png.Decode (image, stream, info);
  160. }
  161. internal static void EncodeDelegate (Image image, Stream stream)
  162. {
  163. PNGCodec png = new PNGCodec();
  164. BitmapData info = ((Bitmap)image).LockBits (new Rectangle (new Point (0,0), image.Size),
  165. ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
  166. png.Encode (image, stream, info);
  167. ((Bitmap)image).UnlockBits (info);
  168. }
  169. internal unsafe bool Decode (Image image, Stream stream, BitmapData info)
  170. {
  171. fs = stream;
  172. IntPtr png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, IntPtr.Zero,
  173. new cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtr(this.error_function),
  174. new cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtr(this.warning_function));
  175. if (png_ptr == IntPtr.Zero) return false;
  176. IntPtr info_ptr = png_create_info_struct (png_ptr);
  177. if (info_ptr == IntPtr.Zero) {
  178. IntPtr dummy = IntPtr.Zero;
  179. png_destroy_read_struct1 (ref png_ptr, IntPtr.Zero, IntPtr.Zero);
  180. return false;
  181. }
  182. IntPtr end_info = png_create_info_struct (png_ptr);
  183. if (end_info == IntPtr.Zero) {
  184. IntPtr dummy = IntPtr.Zero;
  185. png_destroy_read_struct2 (ref png_ptr, ref info_ptr, IntPtr.Zero);
  186. return false;
  187. }
  188. png_set_read_fn (png_ptr, IntPtr.Zero, new cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtrInt(this.read_data_fn));
  189. png_read_info (png_ptr, info_ptr);
  190. int height = png_get_image_height (png_ptr, info_ptr);
  191. int stride = png_get_rowbytes (png_ptr, info_ptr);
  192. stride = (stride + 3) & ~3;
  193. info.Width = png_get_image_width (png_ptr, info_ptr);
  194. info.Height = height;
  195. info.Stride = stride;
  196. // FIXME: do a real palette processing
  197. //info.Palette = new ColorPalette(1, cinfo.ColorMap);
  198. // FIXME: get color information from png info structure
  199. info.PixelFormat = PixelFormat.Format24bppRgb;
  200. info.Scan0 = Marshal.AllocHGlobal (height * stride);
  201. byte *start = (byte *) (void *) info.Scan0;
  202. IntPtr scanline;
  203. for (int row = 0; row < height; row++) {
  204. scanline = (IntPtr) start;
  205. png_read_row (png_ptr, scanline, IntPtr.Zero);
  206. start += stride;
  207. }
  208. png_read_end (png_ptr, end_info);
  209. png_destroy_read_struct (ref png_ptr, ref info_ptr, ref end_info);
  210. info.swap_red_blue_bytes();
  211. return true;
  212. }
  213. internal unsafe bool Encode (Image image, Stream stream, BitmapData info)
  214. {
  215. int bpp = Image.GetPixelFormatSize(info.PixelFormat) / 8;
  216. if (bpp != 3 && bpp != 4) {
  217. throw new ArgumentException(String.Format("Supplied pixel format is not yet supported: {0}, {1} bpp", info.PixelFormat, Image.GetPixelFormatSize(info.PixelFormat)));
  218. }
  219. fs = stream;
  220. IntPtr png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, IntPtr.Zero,
  221. new cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtr(this.error_function),
  222. new cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtr(this.warning_function));
  223. if (png_ptr == IntPtr.Zero) return false;
  224. IntPtr info_ptr = png_create_info_struct (png_ptr);
  225. if (info_ptr == IntPtr.Zero) {
  226. IntPtr dummy = IntPtr.Zero;
  227. png_destroy_write_struct1 (ref png_ptr, IntPtr.Zero);
  228. return false;
  229. }
  230. png_set_write_fn (png_ptr, IntPtr.Zero,
  231. new cdeclCallback.cdeclRedirector.MethodVoidIntPtrIntPtrInt(this.write_data_fn),
  232. new cdeclCallback.cdeclRedirector.MethodVoidIntPtr(this.output_flush_fn));
  233. png_set_IHDR (png_ptr, info_ptr, info.Width, info.Height, 8,
  234. (int)PNG_LIB.PNG_COLOR_TYPE_RGB/*(Image.IsAlphaPixelFormat(info.Format) ? (int)PNG_LIB.PNG_COLOR_TYPE_RGB_ALPHA : (int)PNG_LIB.PNG_COLOR_TYPE_RGB)*/,
  235. (int)PNG_LIB.PNG_INTERLACE_NONE, (int)PNG_LIB.PNG_COMPRESSION_TYPE_DEFAULT, (int)PNG_LIB.PNG_FILTER_TYPE_DEFAULT);
  236. png_write_info (png_ptr, info_ptr);
  237. info.swap_red_blue_bytes ();
  238. byte *start = (byte *) (void *) info.Scan0;
  239. IntPtr scanline;
  240. int stride = info.Stride;
  241. for (int row = 0; row < info.Height; row++) {
  242. scanline = (IntPtr) start;
  243. png_write_row (png_ptr, scanline);
  244. start += stride;
  245. }
  246. png_write_end (png_ptr, info_ptr);
  247. png_destroy_write_struct (ref png_ptr, ref info_ptr);
  248. info.swap_red_blue_bytes ();
  249. return true;
  250. }
  251. }
  252. }