Save canvas as data: Implementation of the image Extension function

Source: Internet
Author: User

[Known]
Canvas provides the todataurl interface to easily convert the canvas to a base64-encoded image. Currently, the best supported formats are PNG. modern browsers in JPEG format are also supported, but not very well.

[Desired]
Generally, such simple and direct interfaces cannot meet the requirements. What I want is not only to generate a PNG through the canvas, I don't want to open a new tab, but also to right-click and save it...

I also need to configure the size and proportion of the generated image more conveniently and freely.

What if I want other image formats, such as bitmap BMP and gif...

[Solution]
A) if you want to directly generate the image and download it to the local device, the solution is also very simple. Directly change the mimetype of the image to the steam stream type. For example, 'image/octet-stream', the browser will automatically help us save it ..

B) The image size and proportion can be controlled. We can create a canvas that we want to resize and repeat the original canvas according to the expected ratio, and the size of draw to the new canvas, and then use the new canvas to todataurl.

C) It may be difficult to create a BMP bitmap... there is no direct interface and we need to generate it ourselves. Generating image response headers and response bodies has certain rules, which is a little troublesome. But it is acceptable. The rest is the performance problem. operations are performed at the pixel level, which puts a lot of computing pressure on a large image.

[Implementation]

 /*  *
* Covert canvas to image
* And save the image file
*/

VaR Canvas2image = Function (){

// Check if support.
VaR $ Support = Function (){
VaR Canvas = Document. createelement ('canvas '),
CTX = canvas. getcontext ('2d ');

Return {
Canvas :!! CTX,
Imagedata :!! CTX. getimagedata,
Dataurl :!! Canvas. todataurl,
Btoa :!! Window. btoa
};
}();

VaR Downloadmime = 'image/octet-stream ';

Function Scalecanvas (canvas, width, height ){
VaR W = canvas. Width,
H = canvas. height;
If (Width = undefined ){
Width = W;
}
If (Height = undefined ){
Height = h;
}

VaR Retcanvas = Document. createelement ('canvas ');
VaR Retctx = retcanvas. getcontext ('2d ');
Retcanvas. width = width;
Retcanvas. Height = height;
Retctx. drawimage (canvas, 0, 0, W, H, 0, 0, width, height );
Return Retcanvas;
}

Function Getdataurl (canvas, type, width, height ){
Canvas = scalecanvas (canvas, width, height );
Return Canvas. todataurl (type );
}

Function SaveFile (strdata ){
Document. Location. href = strdata;
}

Function Genimage (strdata ){
VaR IMG = Document. createelement ('img ');
IMG. src = strdata;
Return IMG;
}
Function Fixtype (type ){
Type = type. tolowercase (). Replace (/jpg/I, 'jpeg ');
VaR R = type. Match (/PNG | JPEG | BMP | GIF/) [0];
Return 'Image/'+ R;
}
Function Encodedata (data ){
If (! Window. btoa ){ Throw 'Btoa undefined '}
VaR STR = '';
If ( Typeof Data = 'string '){
STR = data;
} Else {
For ( VaR I = 0; I <data. length; I ++ ){
STR + = string. fromcharcode (data [I]);
}
}

Return Btoa (STR );
}
Function Getimagedata (canvas ){
VaR W = canvas. Width,
H = canvas. height;
Return Canvas. getcontext ('2d '). getimagedata (0, 0, W, H );
}
Function Makeuri (strdata, type ){
Return 'Data: '+ Type +'; base64, '+ strdata;
}


/* *
* Create bitmap image
* Generate the image response header and response body according to the rules.
*/
VaR Genbitmapimage = Function (Data ){
VaR Imgheader = [],
Imginfoheader = [];

VaR Width = data. Width,
Height = data. height;

Imgheader. Push (0x42 ); // 66-> B
Imgheader. Push (0x4d ); // 77-> m

VaR Fsize = width * height * 3 + 54; // Header size: 54 bytes
Imgheader. Push (fsize % 256 ); // R
Fsize = math. Floor (fsize/256 );
Imgheader. Push (fsize % 256 ); // G
Fsize = math. Floor (fsize/256 );
Imgheader. Push (fsize % 256 ); // B
Fsize = math. Floor (fsize/256 );
Imgheader. Push (fsize % 256 );// A

Imgheader. Push (0 );
Imgheader. Push (0 );
Imgheader. Push (0 );
Imgheader. Push (0 );

Imgheader. Push (54 ); // Offset-> 6
Imgheader. Push (0 );
Imgheader. Push (0 );
Imgheader. Push (0 );

// Info Header
Imginfoheader. Push (40 ); // Info header size
Imginfoheader. Push (0 );
Imginfoheader. Push (0 );
Imginfoheader. Push (0 );

// Lateral info
VaR _ Width = width;
Imginfoheader. Push (_ width % 256 );
_ Width = math. Floor (_ width/256 );
Imginfoheader. Push (_ width % 256 );
_ Width = math. Floor (_ width/256 );
Imginfoheader. Push (_ width % 256 );
_ Width = math. Floor (_ width/256 );
Imginfoheader. Push (_ width % 256 );

// Vertical info
VaR _ Height = height;
Imginfoheader. Push (_ height % 256 );
_ Height = math. Floor (_ height/256 );
Imginfoheader. Push (_ height % 256 );
_ Height = math. Floor (_ height/256 );
Imginfoheader. Push (_ height % 256 );
_ Height = math. Floor (_ height/256 );
Imginfoheader. Push (_ height % 256 );

Imginfoheader. Push (1 );
Imginfoheader. Push (0 );
Imginfoheader. Push (24 ); // 24-Bit Bitmap
Imginfoheader. Push (0 );

// No compression
Imginfoheader. Push (0 );
Imginfoheader. Push (0 );
Imginfoheader. Push (0 );
Imginfoheader. Push (0 );

// Pixel data
VaR Datasize = width * height * 3;
Imginfoheader. Push (datasize % 256 );
Datasize = math. Floor (datasize/256 );
Imginfoheader. Push (datasize % 256 );
Datasize = math. Floor (datasize/256 );
Imginfoheader. Push (datasize % 256 );
Datasize = math. Floor (datasize/256 );
Imginfoheader. Push (datasize % 256 );

// Blank Space
For ( VaR I = 0; I <16; I ++ ){
Imginfoheader. Push (0 );
}

VaR Padding = (4-(width * 3) % 4) % 4;
VaR Imgdata = data. Data;
VaR Strpixeldata = '';
VaR Y = height;
Do {
VaR Offsety = width * (Y-1) * 4;
VaR Strpixelrow = '';
For ( VaR X = 0; x <width; X ++ ){
VaR Offsetx = 4 * X;
Strpixelrow + = string. fromcharcode (imgdata [offsety + offsetx + 2]);
Strpixelrow + = string. fromcharcode (imgdata [offsety + offsetx + 1]);
Strpixelrow + = string. fromcharcode (imgdata [offsety + offsetx]);
}
For (VaR N = 0; n <padding; n ++ ){
Strpixelrow + = string. fromcharcode (0 );
}

Strpixeldata + = strpixelrow;
} While (-- Y );

Return (Encodedata (imgheader. Concat (imginfoheader) + encodedata (strpixeldata ));

};

/* *
* Saveasimage
* @ Param canvaselement
* @ Param {string} image type
* @ Param {number} [Optional] PNG width
* @ Param {number} [Optional] PNG height
*/
VaR Saveasimage = Function (Canvas, width, height, type ){
If ($ Support. Canvas & $ support. dataurl ){
If (Type = undefined) {type = 'png ';}
Type = fixtype (type );
If (/BMP/. Test (type )){
VaR Data = getimagedata (scalecanvas (canvas, width, height ));
VaR Strdata = genbitmapimage (data );
SaveFile (makeuri (strdata, downloadmime ));
} Else {
VaR Strdata = getdataurl (canvas, type, width, height );
SaveFile (strdata. Replace (type, downloadmime ));
}

}
}

VaR Converttoimage =Function (Canvas, width, height, type ){
If ($ Support. Canvas & $ support. dataurl ){
If (Type = undefined) {type = 'png ';}
Type = fixtype (type );

If (/BMP/. Test (type )){
VaR Data = getimagedata (scalecanvas (canvas, width, height ));
VaR Strdata = genbitmapimage (data );
Return Genimage (makeuri (strdata, 'image/BMP '));
}Else {
VaR Strdata = getdataurl (canvas, type, width, height );
Return Genimage (strdata );
}
}
}



Return {
Saveasimage: saveasimage,
Saveaspng: Function (Canvas, width, height ){
Return Saveasimage (canvas, width, height, 'png ');
},
Saveasjpeg: Function (Canvas, width, height ){
Return Saveasimage (canvas, width, height, 'jpeg ');
},
Saveasgif: Function (Canvas, width, height ){
Return Saveasimage (canvas, width, height, 'gif ')
},
Saveasbmp: Function (Canvas, width, height ){
Return Saveasimage (canvas, width, height, 'bmp ');
},

Converttoimage: converttoimage,
Converttopng: Function (Canvas, width, height ){
Return Converttoimage (canvas, width, height, 'png ');
},
Converttojpeg: Function (Canvas, width, height ){
Return Converttoimage (canvas, width, height, 'jpeg ');
},
Converttogif: Function (Canvas, width, height ){
Return Converttoimage (canvas, width, height, 'gif ');
},
Converttobmp: Function (Canvas, width, height ){
Return Converttoimage (canvas, width, height, 'bmp ');
}
};

}();

[Demo]
Http://hongru.github.com/proj/canvas2image/index.html
You can try painting on the canvas and save it. If the BMP format is used, base64 encoding of btoa is required. For base64 encoding rules, see the previous blog.

[Imperfections]
1) The JPEG interface is imperfect. When the canvas is not filled with colors or images, the saved JPEG is directly converted by the alpha channel of PNG, therefore, the transparent part of PNG is black in JPEG.

2) There are too many GIF restrictions. And the availability is not big. PNG is enough.

3) The BMP bitmap is generated, and the calculation amount is slightly greater.

4) the file type is not automatically recognized during download because it is automatically implemented by mimetype.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.