Ensuring our Canvas Visuals Look Good on Retina/High-DPI Screens

This is a companion discussion topic for the original entry at https://www.kirupa.com/canvas/canvas_high_dpi_retina.htm

This is the way. But you will need to write a hack on drawImage not to copy and paste to the double every time.

Here is some sort of my code

    /********************************************************
	CANVAS.js — microbians.com
	Create canvas and all the s**t
********************************************************/

// Create a new Layer (canvas)
layer = function(mainElementID, canvasWidth, canvasHeight, isBuffer, RETINA, FAKEAA) {
	if (document.getElementById(mainElementID)!=undefined) {
		this.main = document.getElementById(mainElementID); 			// Inside a DIV
	} else {
		this.main = document.body; 										// Directly on the body
	}


	this.canvas = document.createElement("canvas");						// Create de canvas
	this.canvas.id = "layer["+(layer.UID)+"]"; 							// Set a unique UID
	layer.UID++;

	this.canvas.style.position="absolute";
	this.canvas.display="block";
	this.canvas.width  = canvasWidth  * RETINA;							// Do a bigger canvas multiplied by REATINA value (1,2,3)
	this.canvas.height = canvasHeight * RETINA;
	this.canvas.style.zoom=( 100 / RETINA )+"%";						// Made the canvas zoom inveser to the size scaled by the RETINA parameter

//	this.hitCanvas = document.createElement("canvas");

	//this.canvas.style.filter="url(#turbnoise)"; 						// SVG Filters -> investigate...

	if (FAKEAA == undefined) { 											// If Not FAKEAA defined set it as half of RETINA paramater
        var FAKEAA = RETINA / 4;
	}
	if (FAKEAA > 0 ) this.canvas.style.filter="blur("+ FAKEAA +"px)"; 	// Add a subttle bur as a Fake blur

//	this.canvas.oncontextmenu = this.hitCanvas.oncontextmenu = function (e) { e.preventDefault(); };	// Prevent using secondary menu over canvas

	if (!isBuffer) this.main.appendChild(this.canvas); 					// If if set as buffer create it but not append (off screen canvas)

	this.ctx = this.canvas.getContext("2d");							// Set the canvas context
	this.ctx.scale(RETINA,RETINA);										// Set the scale of the contet as the RETINA

//	this.hitCanvas.getContext("2d").scale(RETINA,RETINA);				// Set the scale of the contet as the RETINA

	this.ctx._drawImage = this.ctx.drawImage; 													// Backup function
	this.ctx.drawImage = function(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) { 	// Overdrive the drawImage function, to take in consideratino the RETINA parameter, so it no ovrscale when copying from one canvas to another
		this.scale(1/RETINA,1/RETINA);
		sWidth = sWidth || this.canvas.width;
		sHeight = sHeight || this.canvas.height;
		dx = dx || 0;
		dy = dy || 0;
		dWidth = dWidth || this.canvas.width;
		dHeight = dHeight || this.canvas.height;
		this._drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
		this.scale(RETINA,RETINA);
	}
	
	return this;
}
layer.UID = 0; 															// Unique ID for each layer

This code also had an option to add a fake antialiasing if needed by using a little little blur fx - but recently this fix I found works better for non retina displays and get smooth lines.

Enjoy
microbians.com

1 Like

Cool! Thanks for the tip. I will be sure to call this out when discussing drawImage :+1:

1 Like