canvas2image.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /**
  2. * covert canvas to image
  3. * and save the image file
  4. */
  5. var Canvas2Image = function () {
  6. // check if support sth.
  7. var $support = function () {
  8. var canvas = document.createElement('canvas'),
  9. ctx = canvas.getContext('2d');
  10. return {
  11. canvas: !!ctx,
  12. imageData: !!ctx.getImageData,
  13. dataURL: !!canvas.toDataURL,
  14. btoa: !!window.btoa
  15. };
  16. }();
  17. var downloadMime = 'image/octet-stream';
  18. function scaleCanvas (canvas, width, height) {
  19. var w = canvas.width,
  20. h = canvas.height;
  21. if (width == undefined) {
  22. width = w;
  23. }
  24. if (height == undefined) {
  25. height = h;
  26. }
  27. var retCanvas = document.createElement('canvas');
  28. var retCtx = retCanvas.getContext('2d');
  29. retCanvas.width = width;
  30. retCanvas.height = height;
  31. retCtx.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);
  32. return retCanvas;
  33. }
  34. function getDataURL (canvas, type, width, height) {
  35. canvas = scaleCanvas(canvas, width, height);
  36. return canvas.toDataURL(type);
  37. }
  38. function saveFile (strData) {
  39. document.location.href = strData;
  40. }
  41. function genImage(strData) {
  42. var img = document.createElement('img');
  43. img.src = strData;
  44. return img;
  45. }
  46. function fixType (type) {
  47. type = type.toLowerCase().replace(/jpg/i, 'jpeg');
  48. var r = type.match(/png|jpeg|bmp|gif/)[0];
  49. return 'image/' + r;
  50. }
  51. function encodeData (data) {
  52. if (!window.btoa) { throw 'btoa undefined' }
  53. var str = '';
  54. if (typeof data == 'string') {
  55. str = data;
  56. } else {
  57. for (var i = 0; i < data.length; i ++) {
  58. str += String.fromCharCode(data[i]);
  59. }
  60. }
  61. return btoa(str);
  62. }
  63. function getImageData (canvas) {
  64. var w = canvas.width,
  65. h = canvas.height;
  66. return canvas.getContext('2d').getImageData(0, 0, w, h);
  67. }
  68. function makeURI (strData, type) {
  69. return 'data:' + type + ';base64,' + strData;
  70. }
  71. /**
  72. * create bitmap image
  73. * 按照规则生成图片响应头和响应体
  74. */
  75. var genBitmapImage = function (oData) {
  76. //
  77. // BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx
  78. // BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx
  79. //
  80. var biWidth = oData.width;
  81. var biHeight = oData.height;
  82. var biSizeImage = biWidth * biHeight * 3;
  83. var bfSize = biSizeImage + 54; // total header size = 54 bytes
  84. //
  85. // typedef struct tagBITMAPFILEHEADER {
  86. // WORD bfType;
  87. // DWORD bfSize;
  88. // WORD bfReserved1;
  89. // WORD bfReserved2;
  90. // DWORD bfOffBits;
  91. // } BITMAPFILEHEADER;
  92. //
  93. var BITMAPFILEHEADER = [
  94. // WORD bfType -- The file type signature; must be "BM"
  95. 0x42, 0x4D,
  96. // DWORD bfSize -- The size, in bytes, of the bitmap file
  97. bfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff,
  98. // WORD bfReserved1 -- Reserved; must be zero
  99. 0, 0,
  100. // WORD bfReserved2 -- Reserved; must be zero
  101. 0, 0,
  102. // DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.
  103. 54, 0, 0, 0
  104. ];
  105. //
  106. // typedef struct tagBITMAPINFOHEADER {
  107. // DWORD biSize;
  108. // LONG biWidth;
  109. // LONG biHeight;
  110. // WORD biPlanes;
  111. // WORD biBitCount;
  112. // DWORD biCompression;
  113. // DWORD biSizeImage;
  114. // LONG biXPelsPerMeter;
  115. // LONG biYPelsPerMeter;
  116. // DWORD biClrUsed;
  117. // DWORD biClrImportant;
  118. // } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
  119. //
  120. var BITMAPINFOHEADER = [
  121. // DWORD biSize -- The number of bytes required by the structure
  122. 40, 0, 0, 0,
  123. // LONG biWidth -- The width of the bitmap, in pixels
  124. biWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff,
  125. // LONG biHeight -- The height of the bitmap, in pixels
  126. biHeight & 0xff, biHeight >> 8 & 0xff, biHeight >> 16 & 0xff, biHeight >> 24 & 0xff,
  127. // WORD biPlanes -- The number of planes for the target device. This value must be set to 1
  128. 1, 0,
  129. // WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap
  130. // has a maximum of 2^24 colors (16777216, Truecolor)
  131. 24, 0,
  132. // DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed
  133. 0, 0, 0, 0,
  134. // DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps
  135. biSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff,
  136. // LONG biXPelsPerMeter, unused
  137. 0,0,0,0,
  138. // LONG biYPelsPerMeter, unused
  139. 0,0,0,0,
  140. // DWORD biClrUsed, the number of color indexes of palette, unused
  141. 0,0,0,0,
  142. // DWORD biClrImportant, unused
  143. 0,0,0,0
  144. ];
  145. var iPadding = (4 - ((biWidth * 3) % 4)) % 4;
  146. var aImgData = oData.data;
  147. var strPixelData = '';
  148. var biWidth4 = biWidth<<2;
  149. var y = biHeight;
  150. var fromCharCode = String.fromCharCode;
  151. do {
  152. var iOffsetY = biWidth4*(y-1);
  153. var strPixelRow = '';
  154. for (var x = 0; x < biWidth; x++) {
  155. var iOffsetX = x<<2;
  156. strPixelRow += fromCharCode(aImgData[iOffsetY+iOffsetX+2]) +
  157. fromCharCode(aImgData[iOffsetY+iOffsetX+1]) +
  158. fromCharCode(aImgData[iOffsetY+iOffsetX]);
  159. }
  160. for (var c = 0; c < iPadding; c++) {
  161. strPixelRow += String.fromCharCode(0);
  162. }
  163. strPixelData += strPixelRow;
  164. } while (--y);
  165. var strEncoded = encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData);
  166. return strEncoded;
  167. };
  168. /**
  169. * saveAsImage
  170. * @param canvasElement
  171. * @param {String} image type
  172. * @param {Number} [optional] png width
  173. * @param {Number} [optional] png height
  174. */
  175. var saveAsImage = function (canvas, width, height, type) {
  176. if ($support.canvas && $support.dataURL) {
  177. if (typeof canvas == "string") { canvas = document.getElementById(canvas); }
  178. if (type == undefined) { type = 'png'; }
  179. type = fixType(type);
  180. if (/bmp/.test(type)) {
  181. var data = getImageData(scaleCanvas(canvas, width, height));
  182. var strData = genBitmapImage(data);
  183. saveFile(makeURI(strData, downloadMime));
  184. } else {
  185. var strData = getDataURL(canvas, type, width, height);
  186. saveFile(strData.replace(type, downloadMime));
  187. }
  188. }
  189. };
  190. var convertToImage = function (canvas, width, height, type) {
  191. if ($support.canvas && $support.dataURL) {
  192. if (typeof canvas == "string") { canvas = document.getElementById(canvas); }
  193. if (type == undefined) { type = 'png'; }
  194. type = fixType(type);
  195. if (/bmp/.test(type)) {
  196. var data = getImageData(scaleCanvas(canvas, width, height));
  197. var strData = genBitmapImage(data);
  198. return genImage(makeURI(strData, 'image/bmp'));
  199. } else {
  200. var strData = getDataURL(canvas, type, width, height);
  201. return genImage(strData);
  202. }
  203. }
  204. };
  205. return {
  206. saveAsImage: saveAsImage,
  207. saveAsPNG: function (canvas, width, height) {
  208. return saveAsImage(canvas, width, height, 'png');
  209. },
  210. saveAsJPEG: function (canvas, width, height) {
  211. return saveAsImage(canvas, width, height, 'jpeg');
  212. },
  213. saveAsGIF: function (canvas, width, height) {
  214. return saveAsImage(canvas, width, height, 'gif');
  215. },
  216. saveAsBMP: function (canvas, width, height) {
  217. return saveAsImage(canvas, width, height, 'bmp');
  218. },
  219. convertToImage: convertToImage,
  220. convertToPNG: function (canvas, width, height) {
  221. return convertToImage(canvas, width, height, 'png');
  222. },
  223. convertToJPEG: function (canvas, width, height) {
  224. return convertToImage(canvas, width, height, 'jpeg');
  225. },
  226. convertToGIF: function (canvas, width, height) {
  227. return convertToImage(canvas, width, height, 'gif');
  228. },
  229. convertToBMP: function (canvas, width, height) {
  230. return convertToImage(canvas, width, height, 'bmp');
  231. }
  232. };
  233. }();