--- a/js/flotr2/lib/canvas2image.js +++ b/js/flotr2/lib/canvas2image.js @@ -1,1 +1,198 @@ +/* + * Canvas2Image v0.1 + * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com + * MIT License [http://www.opensource.org/licenses/mit-license.php] + */ +var Canvas2Image = (function() { + // check if we have canvas support + var oCanvas = document.createElement("canvas"), + sc = String.fromCharCode, + strDownloadMime = "image/octet-stream", + bReplaceDownloadMime = false; + + // no canvas, bail out. + if (!oCanvas.getContext) { + return { + saveAsBMP : function(){}, + saveAsPNG : function(){}, + saveAsJPEG : function(){} + } + } + + var bHasImageData = !!(oCanvas.getContext("2d").getImageData), + bHasDataURL = !!(oCanvas.toDataURL), + bHasBase64 = !!(window.btoa); + + // ok, we're good + var readCanvasData = function(oCanvas) { + var iWidth = parseInt(oCanvas.width), + 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 i, aData, strData = ""; + + if (typeof data == "string") { + strData = data; + } else { + aData = data; + for (i = 0; i < aData.length; i++) { + strData += sc(aData[i]); + } + } + return btoa(strData); + } + + // creates a base64 encoded string containing BMP data takes an imagedata object as argument + var createBMP = function(oData) { + var strHeader = '', + iWidth = oData.width, + iHeight = oData.height; + + strHeader += 'BM'; + + var iFileSize = iWidth*iHeight*4 + 54; // total header size = 54 bytes + strHeader += sc(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256); + strHeader += sc(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256); + strHeader += sc(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256); + strHeader += sc(iFileSize % 256); + + strHeader += sc(0, 0, 0, 0, 54, 0, 0, 0); // data offset + strHeader += sc(40, 0, 0, 0); // info header size + + var iImageWidth = iWidth; + strHeader += sc(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256); + strHeader += sc(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256); + strHeader += sc(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256); + strHeader += sc(iImageWidth % 256); + + var iImageHeight = iHeight; + strHeader += sc(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256); + strHeader += sc(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256); + strHeader += sc(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256); + strHeader += sc(iImageHeight % 256); + + strHeader += sc(1, 0, 32, 0); // num of planes & num of bits per pixel + strHeader += sc(0, 0, 0, 0); // compression = none + + var iDataSize = iWidth*iHeight*4; + strHeader += sc(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256); + strHeader += sc(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256); + strHeader += sc(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256); + strHeader += sc(iDataSize % 256); + + strHeader += sc(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); // these bytes are not used + + var aImgData = oData.data, + strPixelData = "", + c, x, y = iHeight, + iOffsetX, iOffsetY, strPixelRow; + + do { + iOffsetY = iWidth*(y-1)*4; + strPixelRow = ""; + for (x = 0; x < iWidth; x++) { + iOffsetX = 4*x; + strPixelRow += sc( + aImgData[iOffsetY + iOffsetX + 2], // B + aImgData[iOffsetY + iOffsetX + 1], // G + aImgData[iOffsetY + iOffsetX], // R + aImgData[iOffsetY + iOffsetX + 3] // A + ); + } + strPixelData += strPixelRow; + } while (--y); + + return encodeData(strHeader + strPixelData); + } + + // sends the generated file to the client + var saveFile = function(strData) { + if (!window.open(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, iWidth); + + return oSaveCanvas; + } + return oCanvas; + } + + return { + saveAsPNG : function(oCanvas, bReturnImg, iWidth, iHeight) { + if (!bHasDataURL) return false; + + var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight), + strMime = "image/png", + strData = oScaledCanvas.toDataURL(strMime); + + if (bReturnImg) { + return makeImageObject(strData); + } else { + saveFile(bReplaceDownloadMime ? strData.replace(strMime, strDownloadMime) : strData); + } + return true; + }, + + saveAsJPEG : function(oCanvas, bReturnImg, iWidth, iHeight) { + if (!bHasDataURL) return false; + + var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight), + strMime = "image/jpeg", + 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(bReplaceDownloadMime ? strData.replace(strMime, strDownloadMime) : strData); + } + return true; + }, + + saveAsBMP : function(oCanvas, bReturnImg, iWidth, iHeight) { + if (!(bHasDataURL && bHasImageData && bHasBase64)) return false; + + var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight), + strMime = "image/bmp", + oData = readCanvasData(oScaledCanvas), + strImgData = createBMP(oData); + + if (bReturnImg) { + return makeImageObject(makeDataURI(strImgData, strMime)); + } else { + saveFile(makeDataURI(strImgData, strMime)); + } + return true; + } + }; +})();