Are photoshop-like blend modes possible in HTML5?

If you’re a user of our design applications such as Photoshop and Illustrator, you know how you can create very cool effects with blend modes. An Amazon search returns many books and a Google search on ‘photoshop blending tutorial’ returns more than 200,000 results. This points to a very widely known and used feature.

Adobe: http://blogs.adobe.com/webplatform/2012/04/04/bringing-blending-to-the-web/

Capture

To be able to blend to images together like Photoshop does, we will need to use the HTML5 canvas feature. The images are loaded dynamically using Javascript.

The easy way to blend two images together can be summarized into

// Might be an 'offscreen' canvas
var over  = someCanvas.getContext('2d');
var under = anotherCanvas.getContext('2d');

over.blendOnto( under, 'screen', {destX:30,destY:15} );

The full example is featured below:

The HTML

Blending mode
Opacity
</div> <canvas id="canvas2" width="300" height="300"></canvas> </section> <section class="result"> <canvas id="result" width="300" height="300"></canvas> </section> </div>

The Javascript Code

At first we define the blending modes:

var blendingModes = {
	normal: function(a, b) {
		return a;
	},

	lighten: function(a, b) {
		return (b > a) ? b : a;
	},

	darken: function(a, b) {
		return (b > a) ? a : b;
	},

	multiply: function(a, b) {
		return (a * b) / 255;
	},

	average: function(a, b) {
		return (a + b) / 2;
	},

	add: function(a, b) {
		return Math.min(255, a + b);
	},

	substract: function(a, b) {
		return (a + b < 255) ? 0 : a + b - 255; 	}, 	difference: function(a, b) { 		return Math.abs(a - b); 	}, 	negation: function(a, b) { 		return 255 - Math.abs(255 - a - b); 	}, 	screen: function(a, b) { 		return 255 - (((255 - a) * (255 - b)) >> 8);
	},

	exclusion: function(a, b) {
		return a + b - 2 * a * b / 255;
	},

	overlay: function(a, b) {
		return b < 128
			? (2 * a * b / 255)
			: (255 - 2 * (255 - a) * (255 - b) / 255);
	},

	softLight: function(a, b) {
		return b < 128 			? (2 * ((a >> 1) + 64)) * (b / 255)
			: 255 - (2 * (255 - (( a >> 1) + 64)) * (255 - b) / 255);
	},

	hardLight: function(a, b) {
		return blendingModes.overlay(b, a);
	},

	colorDodge: function(a, b) {
		return b == 255 ? b : Math.min(255, ((a << 8 ) / (255 - b)));
	},

	colorBurn: function(a, b) {
		return b == 0 ? b : Math.max(0, (255 - ((255 - a) << 8 ) / b));
	},

	linearDodge: function(a, b) {
		return blendingModes.add(a, b);
	},

	linearBurn: function(a, b) {
		return blendingModes.substract(a, b);
	},

	linearLight: function(a, b) {
		return b < 128
			? blendingModes.linearBurn(a, 2 * b)
			: blendingModes.linearDodge(a, (2 * (b - 128)));
	},

	vividLight: function(a, b) {
		return b < 128
			? blendingModes.colorBurn(a, 2 * b)
			: blendingModes.colorDodge(a, (2 * (b - 128)));
	},

	pinLight: function(a, b) {
		return b < 128
			? blendingModes.darken(a, 2 * b)
			: blendingModes.lighten(a, (2 * (b - 128)));
	},

	hardMix: function(a, b) {
		return blendingModes.vividLight(a, b) < 128 ? 0 : 255;
	},

	reflect: function(a, b) {
		return b == 255 ? b : Math.min(255, (a * a / (255 - b)));
	},

	glow: function(a, b) {
		return blendingModes.reflect(b, a);
	},

	phoenix: function(a, b) {
		return Math.min(a, b) - Math.max(a, b) + 255;
	}
};

The Blending Code

/** @type CanvasRenderingContext2D */
var ctx1 = null;

/** @type CanvasRenderingContext2D */
var ctx2 = null;

/** @type CanvasRenderingContext2D */
var ctx3 = null;

/** Current blending mode */
var mode = 'normal';

/** Current blending opacity */
var alpha = 1;

var totalImages = 2;
var img1 = new Image;
var img2 = new Image;

img1.onload = img2.onload = imageReady;

function imageReady() {
	if (--totalImages == 0) {
		setupScene();
	}
}

function setupScene() {
	ctx1 = document.getElementById('canvas1').getContext('2d');
	ctx2 = document.getElementById('canvas2').getContext('2d');
	ctx3 = document.getElementById('result').getContext('2d');

	drawImage(img1, ctx1);
	drawImage(img2, ctx2);
	drawImage(img1, ctx3);
}

/**
 * Draw image on specified canvas context
 * @param {Image} img
 * @param {CanvasRenderingContext2D} ctx
 */
function drawImage(img, ctx) {
	ctx.canvas.width = img.width;
	ctx.canvas.height = img.height;
	ctx.drawImage(img, 0, 0);
	updateScene();
}

function updateScene() {
	// create rect for smallest image
	var width =  Math.min(ctx1.canvas.width, ctx2.canvas.width);
	var height = Math.min(ctx1.canvas.height, ctx2.canvas.height);

	var imageData1 = ctx1.getImageData(0, 0, width, height);
	var imageData2 = ctx2.getImageData(0, 0, width, height);

	/** @type Array */
	var pixels1 = imageData1.data;
	/** @type Array */
	var pixels2 = imageData2.data;

	var r, g, b, oR, oG, oB, alpha1 = 1 - alpha;

	var blendingMode = blendingModes[mode];

	// blend images
	for (var i = 0, il = pixels1.length; i < il; i += 4) {
		oR = pixels1[i];
		oG = pixels1[i + 1];
		oB = pixels1[i + 2];

		// calculate blended color
		r = blendingMode(pixels2[i], oR);
		g = blendingMode(pixels2[i + 1], oG);
		b = blendingMode(pixels2[i + 2], oB);

		// alpha compositing
		pixels1[i] =     r * alpha + oR * alpha1;
		pixels1[i + 1] = g * alpha + oG * alpha1;
		pixels1[i + 2] = b * alpha + oB * alpha1;
	}

	ctx3.putImageData(imageData1, 0, 0);
}

/**
 * @param {String} name
 */
function humanizeName(name) {
	return name.charAt(0).toUpperCase() + name.substring(1).replace(/[A-Z]/g, function(s) {
		return ' ' + s.toLowerCase();
	});
}

$(function() {
	// setup page:

	// fill blending mode select box
	var options = [];
	for (var name in blendingModes) if (blendingModes.hasOwnProperty(name)) {
		options.push('' + humanizeName(name) + '');
	}

	$('select[name=blending]').html(options.join('')).change(function() {
		mode = $(this).val();
		if (!totalImages)
			updateScene();
	});

	$('input[name=opacity]').bind('change keyup keypress', function() {
		var value = parseInt($(this).val(), 10);
		if ($(this).data('prev-value') != value) {
			$(this).data('prev-value', value);
			alpha = Math.min(1, Math.max(0, value / 100));

			if (!totalImages)
				updateScene();
		}
	}).val(Math.floor(alpha * 100));

	// load images
	img1.src = 'image1.jpg';
	img2.src = 'image2.jpg';
});

The CSS

body {
			padding: 40px 10px;
			font-family: arial, sans-serif;
			font-size: 12px;
		}

		.blending-demo {
			margin-top: 5em;
			white-space: nowrap;
		}

		section {
			position: relative;
			display: inline-block;
		}

		section:after {
			font-weight: bold;
			font-size: 60px;
			position:absolute;
			top: 50%;
			margin-left: 0.15em;
			left: 100%;
			margin-top: -0.6em;
			line-height: 1.2;
		}

		.image1, .image2 {
			margin-right: 4em;
		}

		.image1:after {
			content: '+';
		}

		.image2:after {
			content: '=';
		}

		.controls {
			position: absolute;
			bottom: 100%;
			padding-bottom: 1em;
		}

		select[name=blending] {
			margin-bottom: 1em;
		}

		label {
			display: inline-block;
			line-height: 1.5;
			vertical-align: middle;
			margin-right: 1em;
			width: 7em;
		}

		input[name=opacity] {
			vertical-align: middle;
		}

The Result

http://media.chikuyonok.ru/canvas-blending/

Advertisements

Digital photo recovery and backup

Bird holding a cheetos
Do not lose your precious photos

Digital photographs are something that are important to all of us. Most of the pictures we take happen once in a lifetime, which is why they are so very important. From your child’s first steps to pictures of your family, photographs are very important. As important as they are, nothing is worse than losing them. This can be very traumatic and frustrating, especially knowing that you’ll never to capture the picture again.

Even though it may appear that the camera malfunctioned, all hope isn’t completely lost. There are ways that you can recover your digital photographs, even though you may not be aware of it. Most digital camera’s for instance, use smart cards that will store the information. To be on the safe side, you should always safe your photographs to your card, and transfer them to your computer the first chance you get – then back them up to a CD or DVD.

Sometimes, when you have your photographs on your computer, you may move them to the recycle bin and not even realize it. You can always correct this, by right mouse clicking the recycle bin then choosing to open it up. If the pictures are there, simply drag them to your desktop or right click them and choose restore. This will put them back in the location they were in before they were moved to the recycle bin.

There are other instances where your photographs aren’t this easy to recover. If the card in your camera has become corrupted or if your camera has experienced hardware problems, then it won’t be so easy to recover your pictures. If this is the case, you should always look towards software or professional repairmen. There is software out there that is designed for most types of digital camera problems, and it can normally recover your pictures in the case of malfunction.

Most services and software can recover almost all files that you have on your camera, from JPEG pictures to video files. Most people transfer their pictures to their computer as soon as they can, which can be recovered using data recovery methods. On the other hand, those who don’t, will need a professional to take a look at the camera. If you don’t waste any time and seek a professional immediately, your pictures can normally be recovered.

Digital cameras are something that most of us own these days, as they take professional quality photos. Anytime that it appears you have lost your pictures, you can turn to software and professional recovery services to get your pictures back. Your digital photographs are very important, which is why you’ll want to take care of them. Mistakes and disasters do happen though – which is why there are recovery services.