|
@@ -0,0 +1,235 @@
|
|
|
+/*
|
|
|
+ * Canvas2Image v0.1
|
|
|
+ * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk
|
|
|
+ * MIT License [http://www.opensource.org/licenses/mit-license.php]
|
|
|
+ */
|
|
|
+
|
|
|
+var Canvas2Image = (function() {
|
|
|
+
|
|
|
+ // check if we have canvas support
|
|
|
+ var bHasCanvas = false;
|
|
|
+ var oCanvas = document.createElement("canvas");
|
|
|
+ if (oCanvas.getContext("2d")) {
|
|
|
+ bHasCanvas = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // no canvas, bail out.
|
|
|
+ if (!bHasCanvas) {
|
|
|
+ return {
|
|
|
+ saveAsBMP : function(){},
|
|
|
+ saveAsPNG : function(){},
|
|
|
+ saveAsJPEG : function(){}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var bHasImageData = !!(oCanvas.getContext("2d").getImageData);
|
|
|
+ var bHasDataURL = !!(oCanvas.toDataURL);
|
|
|
+ var bHasBase64 = !!(window.btoa);
|
|
|
+
|
|
|
+ var strDownloadMime = "image/octet-stream";
|
|
|
+
|
|
|
+ // ok, we're good
|
|
|
+ var readCanvasData = function(oCanvas) {
|
|
|
+ var iWidth = parseInt(oCanvas.width);
|
|
|
+ var iHeight = parseInt(oCanvas.height);
|
|
|
+ return oCanvas.getContext("2d").getImageData(0,0,iWidth,iHeight);
|
|
|
+ }
|
|
|
+
|
|
|
+ // base64 encodes either a string or an array of charcodes
|
|
|
+ var encodeData = function(data) {
|
|
|
+ var strData = "";
|
|
|
+ if (typeof data == "string") {
|
|
|
+ strData = data;
|
|
|
+ } else {
|
|
|
+ var aData = data;
|
|
|
+ for (var i=0;i<aData.length;i++) {
|
|
|
+ strData += String.fromCharCode(aData[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return btoa(strData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // creates a base64 encoded string containing BMP data
|
|
|
+ // takes an imagedata object as argument
|
|
|
+ var createBMP = function(oData) {
|
|
|
+ var aHeader = [];
|
|
|
+
|
|
|
+ var iWidth = oData.width;
|
|
|
+ var iHeight = oData.height;
|
|
|
+
|
|
|
+ aHeader.push(0x42); // magic 1
|
|
|
+ aHeader.push(0x4D);
|
|
|
+
|
|
|
+ var iFileSize = iWidth*iHeight*3 + 54; // total header size = 54 bytes
|
|
|
+ aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
|
|
+ aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
|
|
+ aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
|
|
+ aHeader.push(iFileSize % 256);
|
|
|
+
|
|
|
+ aHeader.push(0); // reserved
|
|
|
+ aHeader.push(0);
|
|
|
+ aHeader.push(0); // reserved
|
|
|
+ aHeader.push(0);
|
|
|
+
|
|
|
+ aHeader.push(54); // dataoffset
|
|
|
+ aHeader.push(0);
|
|
|
+ aHeader.push(0);
|
|
|
+ aHeader.push(0);
|
|
|
+
|
|
|
+ var aInfoHeader = [];
|
|
|
+ aInfoHeader.push(40); // info header size
|
|
|
+ aInfoHeader.push(0);
|
|
|
+ aInfoHeader.push(0);
|
|
|
+ aInfoHeader.push(0);
|
|
|
+
|
|
|
+ var iImageWidth = iWidth;
|
|
|
+ aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
|
|
+ aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
|
|
+ aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
|
|
+ aInfoHeader.push(iImageWidth % 256);
|
|
|
+
|
|
|
+ var iImageHeight = iHeight;
|
|
|
+ aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
|
|
+ aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
|
|
+ aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
|
|
+ aInfoHeader.push(iImageHeight % 256);
|
|
|
+
|
|
|
+ aInfoHeader.push(1); // num of planes
|
|
|
+ aInfoHeader.push(0);
|
|
|
+
|
|
|
+ aInfoHeader.push(24); // num of bits per pixel
|
|
|
+ aInfoHeader.push(0);
|
|
|
+
|
|
|
+ aInfoHeader.push(0); // compression = none
|
|
|
+ aInfoHeader.push(0);
|
|
|
+ aInfoHeader.push(0);
|
|
|
+ aInfoHeader.push(0);
|
|
|
+
|
|
|
+ var iDataSize = iWidth*iHeight*3;
|
|
|
+ aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
|
|
+ aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
|
|
+ aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
|
|
+ aInfoHeader.push(iDataSize % 256);
|
|
|
+
|
|
|
+ for (var i=0;i<16;i++) {
|
|
|
+ aInfoHeader.push(0); // these bytes not used
|
|
|
+ }
|
|
|
+
|
|
|
+ var iPadding = (4 - ((iWidth * 3) % 4)) % 4;
|
|
|
+
|
|
|
+ var aImgData = oData.data;
|
|
|
+
|
|
|
+ var strPixelData = "";
|
|
|
+ var y = iHeight;
|
|
|
+ do {
|
|
|
+ var iOffsetY = iWidth*(y-1)*4;
|
|
|
+ var strPixelRow = "";
|
|
|
+ for (var x=0;x<iWidth;x++) {
|
|
|
+ var iOffsetX = 4*x;
|
|
|
+
|
|
|
+ strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX+2]);
|
|
|
+ strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX+1]);
|
|
|
+ strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX]);
|
|
|
+ }
|
|
|
+ for (var c=0;c<iPadding;c++) {
|
|
|
+ strPixelRow += String.fromCharCode(0);
|
|
|
+ }
|
|
|
+ strPixelData += strPixelRow;
|
|
|
+ } while (--y);
|
|
|
+
|
|
|
+ var strEncoded = encodeData(aHeader.concat(aInfoHeader)) + encodeData(strPixelData);
|
|
|
+
|
|
|
+ return strEncoded;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // sends the generated file to the client
|
|
|
+ var saveFile = function(strData) {
|
|
|
+ document.location.href = strData;
|
|
|
+ }
|
|
|
+
|
|
|
+ var makeDataURI = function(strData, strMime) {
|
|
|
+ return "data:" + strMime + ";base64," + strData;
|
|
|
+ }
|
|
|
+
|
|
|
+ // generates a <img> object containing the imagedata
|
|
|
+ var makeImageObject = function(strSource) {
|
|
|
+ var oImgElement = document.createElement("img");
|
|
|
+ oImgElement.src = strSource;
|
|
|
+ return oImgElement;
|
|
|
+ }
|
|
|
+
|
|
|
+ var scaleCanvas = function(oCanvas, iWidth, iHeight) {
|
|
|
+ if (iWidth && iHeight) {
|
|
|
+ var oSaveCanvas = document.createElement("canvas");
|
|
|
+ oSaveCanvas.width = iWidth;
|
|
|
+ oSaveCanvas.height = iHeight;
|
|
|
+ oSaveCanvas.style.width = iWidth+"px";
|
|
|
+ oSaveCanvas.style.height = iHeight+"px";
|
|
|
+
|
|
|
+ var oSaveCtx = oSaveCanvas.getContext("2d");
|
|
|
+
|
|
|
+ oSaveCtx.drawImage(oCanvas, 0, 0, oCanvas.width, oCanvas.height, 0, 0, iWidth, iHeight);
|
|
|
+ return oSaveCanvas;
|
|
|
+ }
|
|
|
+ return oCanvas;
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+
|
|
|
+ saveAsPNG : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
|
|
+ if (!bHasDataURL) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
|
|
|
+ var strData = oScaledCanvas.toDataURL("image/png");
|
|
|
+ if (bReturnImg) {
|
|
|
+ return makeImageObject(strData);
|
|
|
+ } else {
|
|
|
+ saveFile(strData.replace("image/png", strDownloadMime));
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+
|
|
|
+ saveAsJPEG : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
|
|
+ if (!bHasDataURL) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
|
|
|
+ var strMime = "image/jpeg";
|
|
|
+ var strData = oScaledCanvas.toDataURL(strMime);
|
|
|
+
|
|
|
+ // check if browser actually supports jpeg by looking for the mime type in the data uri.
|
|
|
+ // if not, return false
|
|
|
+ if (strData.indexOf(strMime) != 5) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bReturnImg) {
|
|
|
+ return makeImageObject(strData);
|
|
|
+ } else {
|
|
|
+ saveFile(strData.replace(strMime, strDownloadMime));
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+
|
|
|
+ saveAsBMP : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
|
|
+ if (!(bHasImageData && bHasBase64)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
|
|
|
+
|
|
|
+ var oData = readCanvasData(oScaledCanvas);
|
|
|
+ var strImgData = createBMP(oData);
|
|
|
+ if (bReturnImg) {
|
|
|
+ return makeImageObject(makeDataURI(strImgData, "image/bmp"));
|
|
|
+ } else {
|
|
|
+ saveFile(makeDataURI(strImgData, strDownloadMime));
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+})();
|