HTML5 Image Effects – Emboss

Today we continue our HTML5 canvas image filter examples, today I would like to share with you a method of applying a Emboss effect to images. This is pretty difficult method, but I sure that you can repeat it. In our demo we can play with different images by adding a emboss effect to them, as well as we can ‘export’ our result on the image element (<img>).

Here are our demo and downloadable package:

Live Demo
download in package

Ok, download the example files and lets start coding !


Step 1. HTML Markup

This is markup of our demo page. Here it is:

index.html

01 <!DOCTYPE html>
02 <html lang="en" >
03     <head>
04         <meta charset="utf-8" />
05         <title>HTML5 Image Effects - Emboss | Script Tutorials</title>
06         <link href="css/main.css" rel="stylesheet" type="text/css" />
07         <script src="https://www.google.com/jsapi"></script>
08         <script>
09             google.load("jquery", "1.7.1");
10         </script>
11         <script src="js/script.js"></script>
12     </head>
13     <body>
14         <header>
15             <h2>HTML5 Image Effects - Emboss</h2>
16             <a href="https://www.script-tutorials.com/html5-image-effects-emboss/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
17         </header>
18         <div id="container" class="container">
19             <table cellspacing=0>
20                 <tr>
21                     <td><p>Canvas Object &lt;CANVAS&gt;</p></td>
22                     <td><p>Image Object &lt;IMG&gt;</p></td>
23                 </tr>
24                 <tr>
25                     <td><canvas id="source" width="500" height="500"></canvas><div id="next" class="button">Next image</div></td>
26                     <td><img id="img" src="images/button.png" /></td>
27                 </tr>
28                 <tr>
29                     <td><div id="emboss" class="button">Apply Emboss Effect</div></td>
30                     <td><div id="toImage" class="button">To Image</div></td>
31                 </tr>
32             </table>
33         </div>
34     </body>
35 </html>

Basically – it contain just one canvas object, one image, and three ‘buttons’ (div elements).

Step 2. CSS

Here are our stylesheets (not so important, but anyway):

css/main.css

01 *{
02     margin:0;
03     padding:0;
04 }
05 body {
06     background-image:url(../images/bg.png);
07     color:#fff;
08     font:14px/1.3 Arial,sans-serif;
09 }
10 header {
11     background-color:#212121;
12     box-shadow: 0 -1px 2px #111111;
13     display:block;
14     height:70px;
15     position:relative;
16     width:100%;
17     z-index:100;
18 }
19 header h2{
20     font-size:22px;
21     font-weight:normal;
22     left:50%;
23     margin-left:-400px;
24     padding:22px 0;
25     position:absolute;
26     width:540px;
27 }
28 header a.stuts,a.stuts:visited {
29     border:none;
30     text-decoration:none;
31     color:#fcfcfc;
32     font-size:14px;
33     left:50%;
34     line-height:31px;
35     margin:23px 0 0 110px;
36     position:absolute;
37     top:0;
38 }
39 header .stuts span {
40     font-size:22px;
41     font-weight:bold;
42     margin-left:5px;
43 }
44 .container {
45     color#000000;
46     margin20px auto;
47     overflowhidden;
48     positionrelative;
49     width1005px;
50 }
51 table {
52     background-color: rgba(2552552550.7);
53 }
54 table td {
55     border1px inset #888888;
56     positionrelative;
57     text-aligncenter;
58 }
59 table td p {
60     displayblock;
61     padding10px 0;
62 }
63 .button {
64     cursorpointer;
65     height20px;
66     padding15px 0;
67     positionrelative;
68     text-aligncenter;
69     width500px;
70    -moz-user-select: none;
71    -khtml-user-select: none;
72    user-select: none;
73 }

Step 3. JS

Finally – our javascript code of Emboss effect:

js/script.js

001 // variables
002 var canvas, ctx;
003 var imgObj;
004 // filter strength
005 var strength = 0.5;
006 // shifting matrix
007 var matrix = [-2, -1, 0, -1, 1, 1, 0, 1, 2];
008 // normalize matrix
009 function normalizeMatrix(m) {
010     var j = 0;
011     for (var i = 0; i < m.length; i++) {
012         j += m[i];
013     }
014     for (var i = 0; i < m.length; i++) {
015         m[i] /= j;
016     }
017     return m;
018 }
019 // convert x-y coordinates into pixel position
020 function convertCoordinates(x, y, w) {
021     return x + (y * w);
022 }
023 // find a specified distance between two colours
024 function findColorDiff(dif, dest, src) {
025     return dif * dest + (1 - dif) * src;
026 }
027 // transform matrix
028 function transformMatrix(img, pixels) {
029     // create a second canvas and context to keep temp results
030     var canvas2 = document.createElement('canvas');
031     var ctx2 = canvas2.getContext('2d');
032     ctx2.width = canvas2.width = img.width;
033     ctx2.height = canvas2.height = img.height;
034     // draw image
035     ctx2.drawImage(img, 0, 0, img.width , img.height);
036     var buffImageData = ctx2.getImageData(0, 0, canvas.width, canvas.height);
037     var data = pixels.data;
038     var bufferedData = buffImageData.data;
039     // normalize matrix
040     matrix = normalizeMatrix(matrix);
041     var mSize = Math.sqrt(matrix.length);
042     for (var i = 1; i < img.width - 1; i++) {
043         for (var j = 1; j < img.height - 1; j++) {
044             var sumR = sumG = sumB = 0;
045             // loop through the matrix
046             for (var h = 0; h < mSize; h++) {
047                 for (var w = 0; w < mSize; w++) {
048                     var r = convertCoordinates(i + h - 1, j + w - 1, img.width) << 2;
049                     // RGB for current pixel
050                     var currentPixel = {
051                         r: bufferedData[r],
052                         g: bufferedData[r + 1],
053                         b: bufferedData[r + 2]
054                     };
055                     sumR += currentPixel.r * matrix[w + h * mSize];
056                     sumG += currentPixel.g * matrix[w + h * mSize];
057                     sumB += currentPixel.b * matrix[w + h * mSize];
058                 }
059             }
060             var rf = convertCoordinates(i, j, img.width) << 2;
061             data[rf] = findColorDiff(strength, sumR, data[rf]);
062             data[rf + 1] = findColorDiff(strength, sumG, data[rf + 1]);
063             data[rf + 2] = findColorDiff(strength, sumB, data[rf + 2]);
064         }
065     }
066     return pixels;
067 }
068 // process emboss function
069 function processEmboss() {
070     // clear context
071     ctx.clearRect(0, 0, canvas.width, canvas.height);
072     // draw image
073     ctx.drawImage(imgObj, 0, 0, imgObj.width , imgObj.height);
074     // get image data
075     var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
076     // transform image data
077     imageData = transformMatrix(imgObj, imageData);
078     // draw data back
079     ctx.putImageData(imageData, 0, 0);
080 };
081 $(function () {
082     // create canvas and context objects
083     canvas = document.getElementById('source');
084     ctx = canvas.getContext('2d');
085     // load source image
086     imgObj = new Image();
087     imgObj.onload = function () {
088         // draw image
089         ctx.drawImage(this, 0, 0, this.width, this.height, 0, 0, canvas.width, canvas.height);
090     }
091     imgObj.src = 'images/pic1.jpg';
092     // different onclick handlers
093     var iCur = 1;
094     $('#next').click(function () {
095         iCur++;
096         if (iCur > 6) iCur = 1;
097         imgObj.src = 'images/pic' + iCur + '.jpg';
098     });
099     $('#emboss').click(function () {
100         processEmboss();
101     });
102     $('#toImage').click(function () {
103         $('#img').attr('src', canvas.toDataURL('image/jpeg'));
104     });
105 });

This effect is required some pretty difficult matrix transformations, so, you can try to understand it, or use it as is. Of course, our script will pass through all pixels of original image, and after – will apply some transformation.


Live Demo
download in package

Conclusion

I hope that our demo looks fine. Today we have added new interesting effect to our html5 application – Emboss. I will be glad to see your thanks and comments. Good luck!