canvas hole in html5 with image not working - javascript

I am trying to create a hole in canvas; and loading image in canvas after.
I want this canvas to contain a hole at a top layer.
I just read about counter-clockwise canvas rules and then created hole with counter clockwise position as below-
var c = document.getElementById("canvas-front");
var ctx = c.getContext("2d");
var centerX = c.width / 2;
var centerY = c.offsetTop;
var radius = 30;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI,true);//boolean true to counter-clockwise
ctx.fillStyle = 'black';
ctx.fill();
I have canvas before image in it-
After applying image in this canvas-
Canvas-
<canvas id="canvas-front" width="261" height="506"></canvas>
As you can see from both the images, I can't get this hole in canvas work.
How do I create this arc so that image doesn't overlap this.

I get this issue solved-
What I had to do-
On change of file I did empty to canvas and redrew image and then while image is being loaded
the arc is being created to counter-clockwise position-
Drawing the canvas again on image change-
var canvas = document.getElementById("canvas-front");
canvas.width = canvas.width;//blanks the canvas
var c = canvas.getContext("2d");
Creating image and giving it required src from changed input-
var img = new Image();
img.src = e.target.result;
Loading image and creating arc parallely-
img.onload = function () {
c.drawImage(img, 0, 0);
var centerX = canvas.width / 2;
var centerY = canvas.offsetTop;
var radius = 30;
c.beginPath();
c.arc(centerX, centerY, radius, 0, 2 * Math.PI, true);
c.fillStyle = 'black';
c.fill();
}

Related

Filling in two colors while using the rect() method

I was trying to make two different shapes that are different colors but it isn't working. Both of the shapes are the same colors. Please help!(Please note that I am not the best coder in the world)
I've looked for other examples on this website, but all of them use the lineTo() method and I would like to use the rect() method just to make things easier.
//make canvas and set it up
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
canvas.style.position = 'absolute';
canvas.style.left = '0px';
canvas.style.top = '0px';
canvas.style.backgroundColor = '#D0C6C6';
var cH = canvas.height;
var cW = canvas.width;
//draw paddles
//variables
var paddleLength = 120;
var redPaddleY = window.innerHeight / 2;
var bluePaddleY = window.innerHeight / 2;
var paddleWidth = 20;
//drawing starts
function drawPaddles() {
//RED PADDLE
var redPaddle = function(color) {
ctx.fillStyle = color;
ctx.clearRect(0, 0, cW, cH);
ctx.rect(cH / 12, redPaddleY - paddleLength / 2, paddleWidth, paddleLength);
ctx.fill();
};
//BLUE PADDLE
var bluePaddle = function(color) {
ctx.fillStyle = color;
ctx.clearRect(0, 0, cW, cH);
ctx.rect(cH / 12 * 14, bluePaddleY - paddleLength / 2, paddleWidth, paddleLength);
ctx.fill();
};
redPaddle('red');
bluePaddle('blue');
};
var interval = setInterval(drawPaddles, 25);
Whenever you add a shape to the canvas it becomes part of the current path. The current path remains open until you tell the canvas to start a new one with beginPath(). This means that when you add your second rect() it is combined with the first and filled with the same colour.
The simplest fix would be to use the fillRect() function instead of rect which begins, closes and fills a path in one call.
var redPaddle = function(color) {
ctx.fillStyle = color;
ctx.fillRect(cH / 12, redPaddleY - paddleLength / 2, paddleWidth, paddleLength);
};
If you still want to use rect() you should tell the canvas to begin a new path for each paddle.
var redPaddle = function(color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.rect(cH / 12, redPaddleY - paddleLength / 2, paddleWidth, paddleLength);
ctx.fill();
};
I would also suggest moving the clearRect() outside of the drawing functions too. Clear once per frame and draw both paddles.
...
ctx.clearRect(0, 0, cW, cH);
redPaddle();
bluePaddle();
...
You should also investigate requestAnimationFrame() to do your animation loop as it provides many performance improvements over intervals.

How can I find the center of a canvas in js

I have canvas. I have a ctx. I want the ctx to draw in the center of the canvas, however
canvas.height/2
canvas.width/2
does not work
http://jsfiddle.net/3hnbarcg/3/
Also, I need proof It works before I can accept
Have a look at following code.
var centerPointWidth = 10;
var centerPointHeight = 10;
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#000000";
ctx.fillRect((canvas.width / 2) - (centerPointWidth / 2), (canvas.height / 2) - (centerPointHeight / 2), centerPointWidth, centerPointHeight);
<canvas id="c"></canvas>
It works perfectly to draw a rectangle at the center of the canvas.

Inset-shadow on HTML5 canvas image

I've seen this question before but the answers given are for canvas images that have been drawn on via path however, i'm drawing an image.
Is it possible to create an inset-shadow?
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 10;
context.shadowColor = 'rgba(30,30,30, 0.4)';
var imgOne = new Image();
imgOne.onload = function() {
context.drawImage(imgOne, 0, 0);
};
imgOne.src = "./public/circle.png";
So I draw the circle picture on. I've now at the moment got a slight shadow on the outside of the circle, how can I get this inset instead of offset?
Composition chain
Use a series of composite + draw operation to obtain inset shadow.
Note: the solution require exclusive access to the canvas element when created so either do this on an off-screen canvas and draw back to main, or if possible, plan secondary graphics to be drawn after this has been generated.
The needed steps:
Draw in original image
Invert alpha channel filling the canvas with a solid using xor composition
Define shadow and draw itself back in
Deactivate shadow and draw in original image (destination-atop)
var ctx = c.getContext("2d"), img = new Image;
img.onload = function() {
// draw in image to main canvas
ctx.drawImage(this, 0, 0);
// invert alpha channel
ctx.globalCompositeOperation = "xor";
ctx.fillRect(0, 0, c.width, c.height);
// draw itself again using drop-shadow filter
ctx.shadowBlur = 7*2; // use double of what is in CSS filter (Chrome x4)
ctx.shadowOffsetX = ctx.shadowOffsetY = 5;
ctx.shadowColor = "#000";
ctx.drawImage(c, 0, 0);
// draw original image with background mixed on top
ctx.globalCompositeOperation = "destination-atop";
ctx.shadowColor = "transparent"; // remove shadow !
ctx.drawImage(this, 0, 0);
}
img.src = "http://i.imgur.com/Qrfga2b.png";
<canvas id=c height=300></canvas>
Canvas will shadow where an image changes from opaque to transparent so, as K3N shows in his correct answer, you can turn the image inside out (opaque becomes transparent & visa-versa) so the shadows are drawn inside the circle.
If you know your circle's centerpoint and radius, you can use a stroked-path to create an inset circle shadow. Here's an example:
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
context.beginPath();
context.arc(cw/2,ch/2,75,0,Math.PI*2);
context.fillStyle='lightcyan';
context.fill();
context.globalCompositeOperation='source-atop';
context.shadowOffsetX = 500;
context.shadowOffsetY = 0;
context.shadowBlur = 15;
context.shadowColor = 'rgba(30,30,30,1)';
context.beginPath();
context.arc(cw/2-500,ch/2,75,0,Math.PI*2);
context.stroke();
context.stroke();
context.stroke();
context.globalCompositeOperation='source-over';
<canvas id="canvas" width=300 height=300></canvas>
If your path is irregular or hard to define mathematically, you can also use edge-path detection algorithms. One common edge-path algorithm is Marching Squares. Stackoverflow's K3N has coded a nice Marching Squares algorithm.
Inspired by markE's answer , I made my own version based on a png instead of vector-graphics.
Additionnaly, I made possible to choose the true alpha of the shadow (because the default shadow strength is a way too soft in my opinion)
var img = document.getElementById("myImage");
img.onload = function(){
createInnerShadow(this,5,1);
}
function createInnerShadow(img,distance,alpha){
//the size of the shadow depends on the size of the target,
//then I will create extra "walls" around the picture to be sure
//tbat the shadow will be correctly filled (with the same intensity everywhere)
//(it's not obvious with this image, but it is when there is no space at all between the image and its border)
var offset = 50 + distance;
var hole = document.createElement("canvas");
var holeContext = hole.getContext("2d");
hole.width = img.width + offset*2;
hole.height = img.height + offset*2;
//first, I draw a big black rect
holeContext.fillStyle = "#000000";
holeContext.fillRect(0,0,hole.width,hole.height);
//then I use the image to make an hole in it
holeContext.globalCompositeOperation = "destination-out";
holeContext.drawImage(img,offset,offset);
//I create a new canvas that will contains the shadow of the hole only
var shadow = document.createElement("canvas");
var shadowContext = shadow.getContext("2d");
shadow.width = img.width;
shadow.height = img.height;
shadowContext.filter = "drop-shadow(0px 0px "+distance+"px #000000 ) ";
shadowContext.drawImage(hole,-offset,-offset);
shadowContext.globalCompositeOperation = "destination-out";
shadowContext.drawImage(hole,-offset,-offset);
//now, because the default-shadow filter is really to soft, I normalize the shadow
//then I will be sure that the alpha-gradient of the shadow will start at "alpha" and end at 0
normalizeAlphaShadow(shadow,alpha);
//Finally, I create another canvas that will contain the image and the shadow over it
var result = document.createElement("canvas");
result.width = img.width;
result.height = img.height;
var context = result.getContext("2d");
context.drawImage(img,0,0)
context.drawImage(shadow,0,0);
//and that's it !
document.body.appendChild(result);
}
function normalizeAlphaShadow(canvas,alpha){
var imageData = canvas.getContext("2d").getImageData(0,0,canvas.width,canvas.height);
var pixelData = imageData.data;
var i,len = pixelData.length;
var max = 0;
for(i=3;i<len;i+=4) if(pixelData[i]>max) max = pixelData[i];
max = (255/max) * alpha;
for(i=3;i<len;i+=4) pixelData[i] *= max;
canvas.getContext("2d").putImageData(imageData,0,0)
}
<html>
<body>
<img id="myImage" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAACWCAYAAAB92c4YAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MkFENzg2NTc4MDg5MTFFOEI1OTdBNEZCMEY2OTg3OTAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MkFENzg2NTg4MDg5MTFFOEI1OTdBNEZCMEY2OTg3OTAiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoyQUQ3ODY1NTgwODkxMUU4QjU5N0E0RkIwRjY5ODc5MCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoyQUQ3ODY1NjgwODkxMUU4QjU5N0E0RkIwRjY5ODc5MCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pk/K8voAADFpSURBVHja5H0HmFXVufa76+l1ep+hOkMvQxURLIDYQlBjFBWNeqMxidGYqOk35pqbxJgbo8aS/NEYjYldI8WrEjoICAy9TIdpZ+b0vvf+v7XPGRiUMsycAfz/xbMfzpyyy7u+8n7f+tZaHAaocRwHSZJ4OioTiUSpqqpOi8UyMZlMTojH4102m62V3l9Hr9+mo1PTNJyLTRioE5tMppF2u/0uWZZLCayviKLoorfbBUFgoNUYjca/8zxvMRgM19JnaiwWq/3/BiCr1TqNJGIxAfM/JDWuSCS6LRaLPxqJRv+dVLQ3BUHiBVGYFYvGXtQ09UNFSd5EgNpJuvaca5KUcYDy8/NdBQUFXw8Ggz8gCXJ4/dG5isK9IcmGqTzUSyxm/iKTgXPG4vHJKqTJAX9wW15e4bLs7OxrI5HwblK34LkEkJjxE4rixZJsWi6IViESjf1s3PD4zBkThLtGDuUcpUUWOO0aZFFDLKGhpSuJnQdcX19fw3+6YWs0Fo6gQRDE50iizhmAuEyezGg0wGJ1/dRqUgvnzlAW7D4Ycz/1IxMqhyqEnJK6mtbjynxahhUJDYfceGWZM/78y56/7N1V/yOOU1rOBXXLqIrxvHlQtguP/f4hbfb8GYJpT5OGmxeo9L5KIBA2TDCUHgf7O05YKQocVh+mT4sJV1123oQWX86Cmu2HNwJq0/8TAJE3gtHkLJ46nn9vWFli2A3zDSgsSGDWFA4WgwZN7YUgqyROkSicliZ86cujXG2+3Ct37vR9SEgeJorwxQZINti5KePdL/3y24nzuwI8LphEEuFUYeoVOD1xIpCSGvh4Ay66cppl+y6x2uNRXw4GfbEvLEAcPZTD6fzq924NPzihUsD0CRqyXAo0pYe9Ob0TEkgJSMYgKqqqC95446Df7w+u0rSzI0V8f08gyVZMGSveecF4Hgnqfacj2TdgjrkrCeioQ/WYMObMq/g6z8tZZ0uC+g1QIqmOmDoqNom4DbFnhewR4ZMB56N7sOBO3HD9sFK73XJFhh3umQMoO8s6qmowjOSICKAMumWeKJq3HhPHyBg9pvBLPJOqLyJA8ai3LMdNToh6XCICCC1zt6YlQjBZujB1avFkcmR5X0iArBbJYZS4I04oo42pWfQwxk/IzbPbbSO+mDYooSQVFQNkIuik4XZUnueEIKgjv5AARWPojMS0VBSR6ciAIxYSakVJXgylZe7Zomj84gFkMNqbPd5UgkxVTleFVMJAASefmDhqsSDs3Hr8+tezrnK7Hbd84QDq9AbrG1s13b0nktxxVU23TZ99nwIzLmsc3lgzFOs3pkDijvcdezl2HcxBR1sIxcXmS75wACkJtaGxhW9n3iuR4I5LjP1+jsgx93kAoq2YcOFF+NuKEVi1mtO/fMx3OHL1wXoMzg+gvSOGhoagIAhn1t33O9QQJEMwN0u+7uIpWiGTIqtFPUZyAkEeXV0SHI4UiTzmw1gHnNkSKqtn4fm/HMTIsijsNu1zQIqJZky+eDg6A3l5Wz/teD0WC3d9gSQojION6tZQhEIoUjGNP6oqLHzq8oqwWhUIjCNRd3AGOkx0WOiwkl4Ft6O8RMWoKeOxerOGeJy+RELCsYOpHaMQPB2dW3HbLYNySkuzb5Ekwxcno5idnS1Gk5b6plY/shw86g+JKMhRYCBA4jECTOFgpche4+Noak6goYVDq0dEOCrq5NIoBVEyugZ2pxUHdwEtrWEk2+IIRzgwdi4SSHYCtLikE0OHmVAxyDq9pib2xQEomYzfWtcQu7apVYpNHMEbvvZDBffcwmHOBRoEkqAObxj/u5lDfecIyJbJKK2YgvaoD/fdew+cziw8+fQzaGxRsOXT9diyJR+KdTwMlhLIRhd0cUSCDPRBLF/yIsZXL8XhQwF7+r6TXwiA4vG4JIqWg3vr4iOaW3kU5AOf7iGAZgPL1mhY+sk8zLr0Dlx5/vnIdhvToCZJNaNM+nD9dQv09wYPHoyKiirccfutxw1c6xp51DV0kPo2tfB8bVJVT0y6eIHoAX1O/yyCJEBNqBEtnS9hyb3TScD1CyB2MVEUzcRmHt3foI3ZU4/SS6cADR6g/RCpS4eKabPuxFVXXnTsRUUR3/3u/ce8t3LlGpSUlCMSUeD1+RDwB2A2m1FUlKNzrBf+8kds2rQJl19++bITEVJycTBZrRab1XLzqOKR15Y6S4s5nlM9EY+3zdvmFyUx0dbVtmtf476XVEXdRDHAwAJksViQm5trjEYT22oO8Ovf+N9o6X2L6SbJk63cxOOauVH8+LknMPfSC8k7ndhh1te3oGbHMkSiFuz67WsEYBJGQxI+vwqnYwq+ec+91OsKHn300R0tLS0vHD8vJcHssk7nA77f3zf24XH3jf3uUT/NNJWEJsSFsVndPHdD+/q7nlz65IvN7c1PCJqwNRwKD4ybZz1LUjS7vb31I19AqbJZ+Jm3XCUil4z0EuI1l14kgIvtxl/f9KN68kUwyJ93mo3NPjz2qztxz3Vr8KXLIpgxqh0zxrdhypgWXDjbij37m4kjBfHcs88GXnrpxWvoJ/s/e45BQwajrLj8rlsL/+PvpZHDRYZwDBfmXkscgz4Mpg96/bf9L+DPh5/EfUUPivdcfM/4wrLCO2uaa7LCsfBSkigt4wCRSAsOh+NCMin/Npossy6drl0wvFzA8CEKdhwQUZEDjB7NgwuvxbMvrIUvREGnaEUonERjUxvefe8d/Ou1u3Hr5StRNZTuz9dJgJJ+Rj3QIvQ6WIu8QjP++vJydHbWtu/Z0/lzRi97dhCpDULx4CPXWG949JHxP+J9RA/eb3gLcwYtglkyoi3WisZwI8Jk8/7mfgpD+UrMDV8Jm8+KSeWTuDFjxkxe3bw6TCq9+ngqx/VTgriCgpKhFlP8zu/e5P/OkHIeK7YYcf+typGEvcjTg5Nt9nqi+GgDUNeSh6Rqgd3sw6jB7ZhcLUAwGlKPrfXIYzNhM2p4//0E4mIedtQVeh7+4doR9IXWI/ZBkpHldjy8YKry8zyLEXk19+PO0feiWWxBsZSvn+Onjb/C2tBm/Nh9N37m+yH+M+tXGG4djsV7bkOekI/F02/C6w2vtz/+9uOTI4FIbcaZdCCUsC2+Wn2GvIpRkSTcs0gjw6aSHemBPjlkk1FE5QgBU6eFMKXSA6clioZWIzZ+KqHlsIqOTgWeLgU+n4p2j4q6ZvKAHxnxlzdyMG2yCes2ywc2b2n/PXR2lGpGi1O8fJLx94KWzJs+JoatHU2YEL4e+XlZOtBqMoHfRl5CobkYd2XfghnybFQ5KnWvWB9rwMbkZuT78rDoghst7+57z9TR2fHuZwcr+z/0rMbyy/M1R4tPxIpNIorzFVxyPt1f9Gg4xZgxu5K3A3hruYSafSWwGEpQmJMLkyxi154QwrEIkkqMpFLV0yY8Ue7S/Bz84pulePq19Vi6Yu/rBgMfi0Z7XFpVnSIfz7JYeFS6DRCnH8AHbyzBVwZfm/q8U4XUkMQsx0ggFyhzlVJMmESCV/HgkO/hwfj3UgOYZKOurv7SFfsa9j8UDUY8mQUIfKDTr0WzXDBec5WGnXtSaqJH8KQ5DQ0Kdh/kiEFrWLbShYuqZ+K6S/LgsIpg1ISxFY7LJWnjdI3XOzCdFGCeS0MEX7t6EtZuqh/dITKkIz0QinYs3Wr7862zIg+/soGIqchhnMmMdYfWwkvGP5rwo1RsQ2lnEWq316GiuAT/jq7ATzfehv8Y+SNcP/jWlCq3kSZ0hvJVNfIzk9nyMQXdH3OItycS8f6rmGSw+bLcpoV3LwzlDRvOY+QwTh9OTpIkPPWSiFffK0coMArJ2HDyeA5SNaBykAsJsuwsFakomv5/6lCOOZhjYe877RJysnIq13zatCUY8O85yuKJDhhNqw62CdZ5Y5UpoS4Hl905H4/u/y/4Kp/GZnkJJk/fi23GZXgn+SxRiQgWFi5Au9CGVa0rcVXF9egM+/Dfu3+C0hlPc0/+2FR95SXCNZLBuKi22doWjUa39X9kVUsqLZ1Wr6Ikrw75Ff69VTJGnKfhDy9JONw4ATdfORETRhaicrAb46tIcmwGSCLfa//APBVTi5JCN1Z+Umdubul8pedwbTgcVMJJ2/ocp/KVHNnqnBC+B+v4d9CarN2zeR8eeW2l9pxsSriGlocH796TA82m4e5x9+PLFTfg5YaXcHfDNyEULsXDt0rIzlNRWgLMv1SxCpp6+a664tX9BogZtUgkUrN6i/DmroPavO8uijvrWgWsWlOFGy6rgtlqRDyhsPEzkhYVZuPp53OYGtotBhzqiOV8sr2eWHAicISsWm2oGlay+MYpvhu2N3AoCszENPdMPN30wS+6ukKPhwLBXc0+0xsNXeoVbdK2vIuyv4IRtkr8tellMo08JpinwsftQ9XYQ7CTVxSYjhP+I4ZDeG1pgs9QdQczHMm2McNx4x1fVgr/9r4Dw4tGobjIRmolfG6goi/Je1HgEI5y5jVbG98NBQN1R9ItxF14JCZdPlGaH+cTJD2vIrdjIsZaJ1QfQvMYTuMmGXnpgsvyFk75fuV/2uYXXY5gPIA7Gx7BllA9FpmuQ158Gl6r2YSuWBfFlqTODupICqDe+UgLZrSAih6e6/Jr8PtsyC6XcbKQUKYgMkEP11vAFLJJWS4T/H5/wTFOlAy5N4QdXvJEI/MF8IYg9gf+gQecS923j7r9qxElAj8Z67XtqzHWOUWXjlebX8Gkgt1wVRzCh/K7ENumo2ZVHgLiQcgU6FYO4hELq+j0mmszVh8kSnZuXJXx6xdPjuSvq3GjPK+YxN9A4YV4DAh6cp+i6U93tcDlMBFf4nutZkaDGRt3tMbaPL7X1R4jBDabrX7nYYNV1RLnTSyVTLKzHQ1bh2B04ShSaTN8yQh+sWwuVE5AoxJA/bjFuPfmKK6aDUyfrmLS1H0YOroR9c1GjCRGP2xCEi+/KeKDNYUPZgwgge5rzDDtW1fNimdtOWCAkS+GzcyRqJpIzbge4QmPeCyBF9/eiqnjy2AxSeStTp1+YOmNbJeRWLg8dPnqXa9DUzq6P4tFwvD448s/2a/9af1BkzR/mnfq/loJVeIsSCYRbqMDtf592Nz4IYJZScy76lPkuegeTSTBUR68KqKklMe4Kg1/X8qhlmjJr56Rnmxq6vpd5irMeNlSPRLfmjc9YZctHKFvwvASJ2KJhJ7e4IkYcQwokqYlq/YSiw6ivTOip2fdTrNuvEUCj+NSUtb9v55SIfsjkySajEas3NwcWbNp39OapnT2tICqkiTmHAvLpqwNtZ7YjeMwx/5818vo4Now0T4RbttgDMqdDLtSjOUdb8NqFOBwAGaHlqp+S9Br8nDlRRru+rF5ZYdHvN7rbVUzBpCmclnTx2n3zBivmEeMkHGguQtrt0jIssskMTEEAmEYDCKC4TgOtUdw64JxRBYN2EKqtn5bEw40dqHLH0UklkQsnmQjtqmBD3rd0RVmn2uvLt0VemfFvh8HA4H3GAc6rqQR2bYGhs37zXlPDWozt+KfLa/i+uyvotBWCFWT8G7i57AXNCIUA5at5dHl5ShQTpMOAsmVDWzeqfnWrPc9S45HzaCRVlwOm2bl9QIoDt+6OYnrvrkDL78fxuASE2ZVl+nS4LQbcOWsoQSASu87MazcjS5fBPWHfGB57fpDXrDEA6t0/XB9XUtbR+ANReUawjFlCNnpj0JB30vscehcLD0ZV3ukB00mE8goF4+SRpc5idrfLNyCSYYpZAcNZHSBp7vuxrV3LsPkKhsFcgqiAQ1/elPAb/6PgO8sSqYGG0jK7eaomxTXSn918ZmzQXJ2jpMzat0yT2f+2vVEvAotmDmhFHl5LpIgSedNsbiiJ+wZN4qSxJhNMkYPz8MVFw7D4qvHEoDDsK/eW193KHRLXWPbXa6s/F9mZ9nHmYxCAbNFLpfjS5PGl+8yGIwXpzOUgslsKrHbs+8cbZq+0pSVGPpCwwt4/eBbGOccj0/bd+Cumq/BMmg9qortdHtJPWIxSqRO1JF2q4a3Voh6aISEhk6/pYPjzYEMxWKp5rCZCvKzfHrv6/JKQeUlU1SsWLePHtaNivLck5JNRibZz2xECF95f5fvg3VNl/u62mrY5wcPHuDJa30jFot/SqGFy2bRfvvDb44t+8njW6/auLluWXFJmcxpob/NGxk7v9LtwawLDmF3vAOtvige+nAFCs7rxN2LRbz+vo2NBOv2zGBUU6QsBNx8tQqfj9dtUSuFqtv3COtFQU2SkGdg4FAQRhcWld5fVGC/ZsHMtpLsbIHcu6xfmxnZMVVx/PMDImARF4ZVuHRvdDzuI5Bom4wy3v1oD/74jy0/jEZCb8bjse6BAY0IZ1MikUjyglQ8erB8b2G2WbZYTCN2HAj909vZ3irJ1g9DCSV3xkjfiClVBdzY6ZNRPSIX46dGMKPaCYdsxYebonh9eQvCERFt7SJy3awAVdOlxMKKv0zEkd7j8d5Hrgd83rY6pgr9SphJkoRBgwY9O3nK+V/ze3bg7vnrMHGCCGeBRZcgFjKxwb92r4bfPGeGgRuPS6cPR45bZnG77t6ZzeLINnV0xfHRJwfx0juftOzc3TwmFWMfbRUVg2Z6vV6BCOOHudnWRaoSfjAQ5pdGo/HvBQKBeOp+jOBE0/mTRhhemjczu1TgZboG2Zp4lK4WRkluHIU5Kl1LwyYKeWdVc1gwL657MJaW8Uc4zL1V/temrdH58Zi//wkzso/yRRdd9NPvf/+BPDZUunmvjP0NfqjxLjhtCRhtnJ73sJg4zJocRyTeiOXrm7FjfwhtnQl0+pI40OTDmk9rsXn3GsyZ44MtdzC/dq0nIMuCiSRGJfsSNBhMapZbesZqkWceOtz5gtcX3BYIKX8MBoNLYrGYcvR+mGfjGhxOc+V/LPRMqKzwYnCRD+OHhzF5pIKqwUBZgUb8S8HY0SlTUJSrgmMSbQb++xlDeMWGnMWdnqbmjOSDDAZD6XnnnTfcarVizty5cLlvgaczgPXrluPjt5bAKmwkLnQYIwclUFggYf4VIubOasGevYexu04kt85TMKtibmUSo4cpMJVWoK7Lbf7DH/7wX8XFudi2bWvggw8+qKup2fGIyeR4wePpuJWIZ14oFGxV1eNP6JAlngy/oZnjoqRCFHrwGiwWluFUSMI0/WDeqpDUq5BcusYq/e0c3lvG4emX5J/4fY0bMpkwGzt48GAD4yTRaBQWMxvRKEbl8MXkphfjYG0Ltm5dh7+t/hjxwDoUumoxY0IIVaMEVFWybkt3PmMbcVYGQmpQxKG13YBLL72EHTbqhFG1tQe/3dDYMrWry/sSY+WMeDLDriifT7LzIhHUCuWSolwCR9CQn5eAHi+n892a1iNgZtNHrBxWr9PwnUfMz8Zj2q+CQX/mihcqKipGFRcXE6dJ6PaIHqaH8QaGDsnHwi9fjQe+/zi+/fBaIPspLP5BETZtTk1s0Ujn9SPGbppNRfBjaDmw/+AW/RwrVqwg7+LDFVdcOcjj8eTrGUZ6OrvdPqsw3/kWXa/kOJS+asJ5kWqm1s50RQkratfUz2cSWCHFx6uAr35b/OPe/e13ezyHMlvdQcRsAgOISZAsy3rPnqhZWVxmjOP+B57A66uvxbqNQXDmY1MaWiKKoqwuArsZmzfvwJtvvol58+ahpKQkl65VoZ/HZne5bPxTgwqFObwgmo8dsZWRn2u+bNoYzcDCdiNz5ScL8yRgUw0iDQ3R37C03PFmF/ULIL/fv23JkiW65FBEfcrv19fXY1L1CHz3+3/Eq/++Fhs++QxIrCYxvBezLxBwzzfvwNix49jILQoLC9ko7lA9MI1Gujq88as37QxVRyPhPcdQBcLrvIr4giGlnK5euvScIo1ltUi8KEvCiaZe9RkgNm5OPGfjmjVr8Nxzz6G1tfWk32c2iqliVlaWnmN+6AfP4+8rPiNJDCClHd5DG2G22OkaFv03rMiBJGgoG+pm0urz+XdHY7Htn60TMFus1TPHxyd3V/33Ig+HQDCmJuN+NeNj86WlpTkXX3zxr++9917s3LkTr732Gj766CPMJW82bNgwnST2bJ2dncfYqewsMx760XN45GdsPOPvmDzRqvfolm1RfLhmDF5+6Q8E/JMYPnwo3G43U7OvVFVVFRHnMZJ7F8lzJpqbm3fSeZ+g93yywYzCXP66GePBKyQNZlPvitoTipDgRVtCTfr6DxB7QOY5mK2hG7x74cKFg1iPzp49m1VdYN26dbrdIG5CDzYc48aNY0RS/35DQ4P+oD1blotA+sGzePQRDUbTP1BAbvfF92fivgefIKmx44ILLsBjjz2GnJwcXHbZZUPuv//+Id3XZ5L1pz+/gieeeLIwsLfmbldWkaN6hHdBST5PjDype67eTBDyBRAReCGi9keC2A3RMcjpdC6knqwmcTaQuE/evn07BY4uXd1YmzJlin4wddu4cSPefvttUG/rD1hbW4srr7zy8xVqJEnfe+hZ/PcjAQQDtbjnvudRVOjQP5s6dSqYjbviiiswfvx4vbylu33wwYdY+cHPMHVscrHVaLWForHE/Au0ClFkXk7p3ZQI+k5nF0cxvRY8iRaevP6HgODoeOCaa655aMGCBfYJEyboBpncLlauXInVq1fjwgsvxPz58497jkgkgrq6Ov37kydP1qXweK2xsZXADKGyctAx7zMbt2/fPtx8881HEyvU3Q89sAD3XfcW8S4L/Y5HOwWZNoNEks1KZ7RT5rp1C0Di8aVv8Bve/Fdwsp4MOh0JYuCQtEhkIJ8jO3MTsy3MwB5RD3p99dVX627417/+tW5jFi1adDwqQA9decrOLCk5/lyVadOmYenSpSB7g6KiIv29bdv3w21ci9wCktwIDysFmdZShlziCBnsjYGORDR4uohTcCd2dyf0Yix8IJvx+MMPP3wTGWNdlU4QboC+gwMHDujSNBCturoa77777pG/t27dQOFLJz2UkGLG6ZTp8cjgySqj2AyBVo+wl42wnFBQjhvPEOkj/vG1O+644y6mUmR7dIk6Wbvtttv0hxiIqdxMhXfv3q3bM9YONW1FeWGif2WcpDu1hwQKa2I74vHw6QFExGwQSc0jFKnr9qZnCHFiFSnRCd369eszDhCT5rKyMqxatSo13ByoR16WcCSU61sii1R1txaXZcfWk9rhz77BACkoKHj4+uuvz2XAnEi1jteYEd6yZcuAqNn555+PDRs2pEYwEh7YzEKfJ+9x6Zql9dvlfV2dnXtPCyDyMtVz5sy5gRnE3oQPPRvjPMxYD0QbPXo0iBCisclD7j4CUeyfejUfZhJk/JDnEtFeA8TsDAFz16WXXqqnMJhon05jIQGzQRSjZRygtF3ELmLtRoP8OaZ+elE28P4qXqtrVP6RSETQa4CIiFVccsklCxixY2CdiLOcim2HQqEBkaKhQ4fq3pIXpD47A5ZaDXg1vPKudWksGl55qvPwPcBhhnDhrFmz7PF4/KSpi1Mk8fUwYCAaU+G2tlaSHisFracPkF71Zubw+J/l8Kc13E9jUe+pB4x7VE9w5eXlC0aOHKWPPJzKrZ+NxlQ4EY8hqZkRjqq9rtHVi/vYMCNF+Y89yam//4vxmwF/+7reSGFPFDSKypf9/omncLjVS2Is9+khupNnA9GY04hGw+AEJ3zBkwdK+ti+kJp6BRuH2joOt35PPPyDx4QFfl/n8/F4pLds4GgLhZO7so0bbkr6/21Zt7mZiGoWXO5cio57N/jBeoSlSWfMmNEr7tSHQQI96bZ8+Ue48Pw25Jakixx6zi9jB0kKBI6CX2DjNg6/fQ7B7/6S+/Mn28RFybhvYzwePR2Hd7QZTeYhl8/ichfMasWu2uexf/NfsXrZcIiWahSVVKO8ohL5+UUUIGaRhzNA/owNZy6eSdDp0oPTabfffiv+TUHyMy9+lJw5ycrxbJIm9V+czF44qsDn53CoDdGDDVz7zgParvomYXkkor6RTAQPKMrp20au50uXy37H334l/3FEBQebXYGzIA41nEBzSwL7G3nUt5jh8bsQiudA5XIhSG7qVSdCUTMk2YYwea+JEyeCov4BtUUdHR2YeeHF7Tt37LgXvOjhedGiaWQ5lVicrAZxDKHdZLK0xON+b8q+9t1piEctvISifOPofLeCpMrp40ksUuZhQEkhHWXsSywJ3g4t1oJITEU4Qt7Oko//ea0SY0Y/iNmzp+kzgM6EsX78t7/KueGGGy4OhcKLw+Ge6RwWfyQoUo9m5FpHjDQzygXZ6nkOO6cXeEuSenQcSUlVzmsRNo9dJL03wGw0IdtlhjM/BxZrIcaMGX1GwOluxNdw44033kJ3dVvPRFqm2xGAHHanraJELGdVuvoqmqJ2EmOcTisQhvGImYy7BVlZLpzp9tBDDzFu9EOj0Vgw4AB1dnXlF+cmClMpVi2V0z0VTeA0BGImAtRJ0iOfcYCYqn3nO98pI1pxT19Y/2kAxMHtdpQVkUfXtJT09IonigJaOtlIhfuskcebbrqJOYbbiPkXDihAFhM3KC9L0/O9vesMEi/ZhDYCyGxynjWAmP25++67c81m86KBsEVpgFTwWrAsx8Wn5qqLvYhzmP6JRrR0SHA5s89qCMLy4lVVVTcQUTUODEBEP91OscRuSa0k1SuAmIXmSYI6+GOS+WejsdDmiiuuGEVedMaAAMSWesjPlYoMUqrkTBR6MSrJMuS8Ff6gAVabFWe7sWEnt9u9QBTFzANEEbJoMyez2bQAlhLojSpz5MGSCQMRMiPs5wBApGIYO3bsRQSQdSBskNlhVZ3klMBzWopFn0qCyM35QxJU1QSz2YhzYWHa2bNnDyUON3ogvJjFZubMLDLm06u3nPJx6YuhmMQWuD1mLsbZbGyQkQLl6ZnMZelnstpdBosZ8pGsW69+ySMYlYgzWc8ZgNgIbn5+fnUmF8fV4TAaDfkmA2dPPffnFzg6EUC+AE+/teBcaSwWHDNmzCiSoozRap7iGFZgMIvTErpa9W6wQEtJUJglsYz63+fKWvQlJSUFgUAgY7EZ73K52TSji9hq4tqR5St68bAcry/qJklyanr3OQJQYaFeO5OxsIMn6akcNnz4CJPZhtNbkphD93ogqej+3ACIjQTn5eVlLDjk8/Jyp5aXV2QrmtQjydgLPdMUMGLZXZp7NlcN726sDnLz5s1oa2szZeqcotfrXbV06bJlxebIZfrKTb2SIk5fQsNlUxAI+nVwjlfUfaba/n378Mrf/45XX3310z179rxAxnp5dyVIvwFqbW3dS9HMDe2d5m2aZihh0tArbUkmUeCOIBhsR4Kth5FI6MVSZ6Il4zHUHtiHlavX4IP1m/Hxkn+1edpabyT+8xGbEcQGPjMmQd2BJ5vjkJqBw0HrjZIpKgrdQYrb2uHzhZCbGx8YMGJRtLe1Yd/ePdi9dx927N2Pgx1edMAAedBIKLmVcNhXvpqIhJd3eDwZv343QDabRbPqLFrr3RwpDQI5Mg+K8r3Yvq0GhbkOPcN3uk1TkggGAvB1dcHr7UIrgdHQ0Ii6hgY0tnagqdMLTzQJ1Z4FMacI7sHT4L5gMF23FBZ3Fj5++KZw1Nf1zECA0xMgTkgtv3KEKGqnKFFnc7wQ7cL542NY9L2/4o31G2CXWCGlrC95YyF+JNNr+Uh0zWYVJhCPJxAmYxqOxhCi18FoHKwGN0qSG9HIM0pGGFzZkF2FMI8cC0deEQqzcmGyOiERKWUrk/OcAIlC0rqPlyC0be0zvlBk+0Cpc/rueU3ReluOxEiijCRbXSoWwqjSVoybPAodo25AgrxaeyyCBKmFQgAkE1FobHaylj65kYNglSAQeKJsgMhAlAywEFmVzVZIBhMk2QhelPUKDkGUqCNEfeeFI+UuWmpIOdrlwydP/nR/e1PTL4LRgVt4shugSCzORbul5mRIMcYdMYzEn//px+1zaiEJrbh0ZAGeb65F1oRJUBOx1MPoc995/WAzidn/bB0PTmCqKaRec6IOAgODE8T0e/xRA9idVOjBs9i5WdnAql8+jI6azQ+H4sn2geRg3fIf7PLDT566gN0dl953R1OORmys17T0AKUskKR4JZB2QIoFMXooGfl6P5xFQ6FEQuheIakboBRgfHrBAP5YD9CzU7qB0E6YhIJkBra9+AR2/vO5NgX8BwPNv/RgVTZaor6QqSWeUPVovrmNzSHl9NcMmMPtAjZsE/UCAY1+IkgBClJtaGon0c7Kwf56DZIjn9SDLbhkoYMNRZshSil14ZiE8KkFDXUaofY4tHQcp2knl10CR6a4ePsrz8Di3Y8Jc+YjEo0OeBpBB4jZirrm+O5AhA35cNhxEHh3JaevXtdNA95fnepZXQJUP2wOO779P8Pw898Nwl+2T0bJ9LlQYjjywMc8eG8AOJlas6UpiGJtfeEJdOzajKv+83EYHQ7gDGy2IabSywl4Q6Y1B5vCd+a5OFRVAGu2sSXCONbnyHWxO1ERCnPQxweVOFhhfHD8Q9g17kKUluRDIAlRlYwvak+GnC5HRn/d735O/0cw/YHf6VtuqKyA4Aw0PhVsqgiHsXz5OvEQW4Aky84h28FhXQ3dAwEimtjSNBxa2TLXQspYiIIKd44DJVVF9BiZB4fdh0yuvHPfDnz8s9thzsrHlPt+Q3dsYFv/6KvInDGA9EAv3HX4g/WWx95dkYCSEPClOcD7/+ZTy7CTqhXmcNjPJkvLZLkNbMBQgGzLIqaLjG6HwDN1snBIhH3Y/NxvsOXP/4UR19yF0Yu+wQQ31RHplWHORDsyRhIKBXDgQPSxH/3BQfZGuSPLTQCtRJvHo03/xTei3PmjOTzzD2DWGBlefxGWb7Mj+7pR5Nb7r0b6SIqUnpHZ7sHut17H4c0rkDd6Gmb+8BmIZjPiIe0Yx2cwnpktJD7XDYyXaJpIJlGLFeRkVVsmzV07bKiBcyd2UgyUh9IiulnOBdO0xcgdTrFQ/PSlR5A43Y7oM59JIGO+ADz7PkXzJx/C27APOZXVGDbvq7Dk5yAZQY9EHvS1QcaUc/j4lz/oWv7sH0e2dHQcOiMSdCQGZQqOVHW1pkTDuRUl8azL7zAEOzwY6rbD5Coh903ejk2V6AM4LNLrPLgHvrqdCHUcRrClAVGfh0yLCbkjJ6Fq4V2w5eXoqpsInXiOUiQSFQgc6Yyp2PHa4XZPojgUVAQlAqOUBKckKDTg9FFnRe2D3SG7kW8IY/VT3wdfOAT2ogpkj5oMa0EJnMXDYLTZ9c1L4mHtZIFOmlCqZ9YGneBmVCbfKT6jkgEV0xtd9n0WiVNOwEpReNGVN5MkSqklcbKLyZ1b9PlwvTH47CtsJqRZloRwPDGgAJ3UV5plMSmRPz8SBwlivw0eK47QN8mMRsjAx2FxF+ng6Hamt95Qr4EWxGg8MeD7R5wUoHA8KcaiMSHlUTldgvqNEFuMTVFSnsjiIIZsPcYI97aRaLOSUv6sAqTnQTjuSPDJ8/2vnNDS8yYZEZT6OOiorwDGCNMZCDVOCpDVIENM3Uia3Qr92oGu+4mYqupRfl8kMq2JgshKLSCfVYBsNquROL3Yg//3X4JYBJ9edYrj+g64pv/4LAN0uKNTVDSN1/M4OuPtn0TrqVw1ZYNSAPUd8H7bw0wANKSshJyYwB/J2Gl9fyDtiBvTSVQ6ccb1+VyCqG+sKJxVgBobG40c353O1/Ot/XZiWsqA9Bmcbh4kpGqAzq6KxfSO5tO9pgH9Za9c+kzdG631A+90qlU8qwClvsH3MLBqv/FhWznow9T9QOhMlkmcllHRVKVfzINhopNEVT1mpKKvCbWzzoNSD8UdUfxUpN9fDdP0gb804n22+IIkn30J0ms5e6xJryr9G6Dj0l5MS6dLdVbdRxlIr5WvnVWARH17Qe6Ij9bXduzrLWndKhY/6vL7KEHqUQk6u7FYgo0yd4+tczgyjtUf1WcFV3qBhNZXo5/qIYEijdR2kWcRILfDxvOspzKUlNcHWBno6b1SVVXpAzyp7W10bT3bRjoeT5i4nkXZ/byd7v16UkQxFXb0iWymJUg72xKkqIpBXycDPUZV+8PwWOVePJ7OSKZtWp/UlACSzwEvxqYocMwGpRPBPM/3OULQczj6KEZ6sxG2v0ayb0Zf7U69nG2AREmy8WzLqu6UayYSZj0i8e4K2dPVXebFRIMBFpPx7HoxRYON3Uj3Q/S313Q3z7wYqxFixpZsUF9GJ9hYGruX/Px8w0CPsIqn4PNW0WhOr73OXH7/bSLjVYLJDJEOlrTnhLTqadwxBVNHde/oFu3ds5EEI2B1mHGwtk4aaKZ4QoBsbJnAvDyrZHWCZ8Mz9DD9S1KlhpiNVgs6aj7BAYsTZlc2tMowTO4CSNQRvGwk78SnVm3pUXLHjLKaUJGMhBHuasfuHXXwrH2bFY0KrHjzTFSYfa6xqnWvx2Nu+Pgd5I2dCmteMT2EQa/w0lipcM9FrD9TFNad52ca2Z00ZGFcqMOHji2b9DcZSew8sAOd+7dBjUVSpY/UEUxK9Xw1n6re6lZDJm26xNHn9oJS5Lpy2Rai5oGeAnFCgNjUosKyMlm02nFo4wrE/V3U41mw5pfAklMIU1YBTM5sAsxGLteo2xV90Wy29V48ikTEj2hXB8Kewwi1NSHccVhXL4cWgqN8KIZcdh09oAG2rDwkYwpJR4iOAP0uTH+H9bRIevRCHzeT6H5kq0tXTbMTcLbUYMnvf332hp4Dfj9a2zsMYy6YR0DkIOrtAJupGe1sR7C1Ef5DBxEP+JGMho8xFRq6Z4ybYLQ5YaSedg8ejfJZC+EoGYyi+D7847HHEA8FwZuF1GgqK+u12CFRZ5i7R5l6FnJ2H0zVlFTlAKtJEgTedNYAYmsyJ+JxXonFCIQIib8BrpIhMI61HL3x9A1rinokrtKT8UJqcjAvdMdwLFGWep1U+JRKsYJMiwmSSR9LTKlsj6Sl1oNccml15Q1pshlRUPvJJwgHQ9azBpDVYkZObk5M6Wa7dOdKQkmVoxyj96kKVhxJzbJ9IVOvPhtpsVpDppKt29ah9ce3o3D8DBSMnUZqWwYjU1eThdTVkOJbbAYRiZcSjyERDiLm98DfuB/tOzeic8d62LkYho8acevObTv20qX+Eo1F1YGwRycEKB6Lwx8IJmS6aRaw6mEB10OPelK/Uy6Cksr7s/pmtmOdzKsIH65B+8f18K9/CwpzW2xXFZJSuhgBKacNc0yvn+RY7TX9LxLyRpMB2XZSRVsWmxeWO/i8oX9av2Ltl5saGr5BUl+XaYBOyPzYVE2n3VYRCoXnmt25unG05eRBtoipAig62MavbNCDp/+ZdPDpQ3+ffS5zEGVOVw8lnoC3oRb7l7yK3SuXw5HtQsmQoXBmZ8Nmt8JqlmE18DCLKsxCAhZRgc3Ew2GR4bBb4HA5YHO7iSZY9RQM2/zR2+WF2WrG2Aljh3V6Ohfm5+avTyaTjdFoNGMAnZSGVlRUTO7wtKw1O52c7MyHq+I83Ysxw2ty58BAvSiTYRWMjL9Qr3Op6nOV1IIZ4ai3DaGWJgQO1SLQuBfxjmYU5WTh4IGD+tZ6g6sqP59JOSbVfGrpZEA5XU4MrhiElcs/bm1pOjynsalp64ADxAJTh8PxLavL+LiBHkYmACyswIluiG0Mm0zvm8qGcLS0HUptS0zSpKl6UowtUsC2OzcYZBgtVvJspB45uXBYHXj/jXdQXjkIJnLf/a2WZwvLsWVMy0pK8NYrb25pPdxyYSAQ8A8oQGxJPuJCf7JlGRebbRYUlJSnmHQ6W8V9ppd7FlVxR8IDTn9fS/t+ZkTZw5SVlUKLq1i9cjVJ0dCMTOdk52Xz5pVYEsve+tdPWlpaf5qJiXUnDFbZ2JWqKaXMtlhJjVjpCxuyYQ+jKqkpmKlD1bfBYqt3dh+KmpIu/RxpCtDtYdjiI02NTcguyEVxUTHaD7chE+v+sEL2uto65JcUoLii7E76O3dAo3mZPJfRpG+ABZlIn5bBAQRWZca2kRhTPQ5Bb0AHkql0f5aUYONk5Orh6ehA1ZgRBSazaV5GACosKtRXEi+gw+V2wZTeAoIuSNGDoFd28Hp6gpG1zKRfmMR0dXZBNkgsZYGgL4DWpha0NbfqNqs/KYyODg/c2VnIycudkYk5tLw+X4vEk803zyKXq6S352Tb5kXCkZg+z4sOYq1oOtCg54IzI0Uq2qm3K4ZUwN/l08/f0nQIDfvqdJXU92Y9TYliHcmkyGRiq9GowzKRKxJZAovtg9rU5NcrJszkVZhx00We41tibDYfp+nutPFAPUmYCblF+UgkE/2rNiPQibtg8KDB8LR0BOgeomaLOXRw1/7/7WrzTMwpzBvjyKJYju30K/D4jFvokcDXjgxjM3JL58KSt9/XQoHQ8oww6ZZDh/X4KZlI6DusdC9Sm7ILwi8Dnkhkf83+C+kmi81Wi3Zg5z4fAWgvLCvm2c0xI9wn0SWbEaEYTyRmnZ2TvWfvzt0LQ8GQl67r83Z6LfSAcwxGw1WywTDRYrMUk+pZBbZRfQ+hoGsrmqpFo5GoLxqJtFAoVB8JhVfHYrHlJD3bMuHFuJP3cmo3OGLVDlK3SfS3Rnq9jWzI1+zZzh8UlBWabE57yp338FSphKD2uSt1y0Aq+c/pm80aKKyo+WTbPxsbGq/5bCzFqv8kSSScDHn+QCCPPneyhdzYfdARosNvNpm7SK26EolEgO4rqaqZXUekT0rKpEySpCoC69smm/lqZ7Yrx0rhgmw0HAWARfTp06dmGarpFRpURIJhBHx+BLr89Z3tnueIeP4uqSQDOAcb13cbkt5rUNVyiHFPJZs0kTxfJXnBQvrMStIvMxWUJD1tHCOw4mTHvH6vr0mW5T3kHjdHItENpGZ+nMPt/wowAA/TSNpqb2vYAAAAAElFTkSuQmCC" />
</body>
</html>
the jsfiddle is here : https://jsfiddle.net/jrekw5og/141/
Inspired by K3N's answer, I've created Inset.js for this exact situation!
Inset.js
Only requires setting ctx.shadowInset = true;
For example: http://codepen.io/patlillis/pen/ryoWey
var ctx = canvas.getContext('2d');
var img = new Image;
img.onload = function() {
ctx.shadowInset = true;
ctx.shadowBlur = 25;
ctx.shadowColor = "#000";
ctx.drawImage(this, 0, 0);
}
img.src = "http://i.imgur.com/Qrfga2b.png";
const width = 100 * devicePixelRatio;
const height = 100 * devicePixelRatio;
// original canvas
const c = document.getElementById('canvas');
c.width = 300 * devicePixelRatio;
c.height = 300 * devicePixelRatio;
c.style.width = '300px';
c.style.height = '300px';
const cctx = c.getContext('2d');
cctx.fillStyle = 'rgb(20,205,75)';
cctx.arc(150 * devicePixelRatio, 150 * devicePixelRatio, 50 * devicePixelRatio, 0, Math.PI * 2);
cctx.fill();
// temporary canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
canvas.style.width = `${width / devicePixelRatio}px`;
canvas.style.height = `${height / devicePixelRatio}px`;
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
// original object on temporary canvas
ctx.arc(50 * devicePixelRatio, 50 * devicePixelRatio, 50 * devicePixelRatio, 0, Math.PI * 2);
ctx.fill();
// shadow cutting
ctx.globalCompositeOperation = 'xor';
ctx.arc(50 * devicePixelRatio, 50 * devicePixelRatio, 50 * devicePixelRatio, 0, Math.PI * 2);
ctx.fill();
// shadow props
ctx.shadowBlur = 50;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = -25;
ctx.shadowColor = '#000';
ctx.arc(50 * devicePixelRatio, 50 * devicePixelRatio, 50 * devicePixelRatio, 0, Math.PI * 2);
ctx.fill();
// shadow color
ctx.globalCompositeOperation = 'source-in';
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// object cutting
ctx.globalCompositeOperation = 'destination-in';
ctx.arc(50 * devicePixelRatio, 50 * devicePixelRatio, 50 * devicePixelRatio, 0, Math.PI * 2);
ctx.fill();
// shadow opacity
cctx.globalAlpha = .4
// inserting shadow into original canvas
cctx.drawImage(canvas, 200, 200);
Colored shadow /w opacity

Pattern gets shifted when painting at the bottom edge of the canvas

I would like to paint a repeating grass pattern at the bottom of a canvas. The image does repeat, but it's vertically shifted.
The pattern is based on a 192x50 image:
I've noticed that if I paint from the y-coordinates 50, 100, 150, and so on, the pattern is displayed correctly. It doesn't work at other coordinates.
The resulting canvas has a vertically shifted grass pattern:
I don't know why it gets shifted like that.
Below is my code.
HTML:
<canvas id="myCanvas" style="display: block;">
</canvas>
JavaScript:
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
// Grass Background Image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "img/grass.png";
I do the following in a loop:
if (bgReady) {
ctx.drawImage(bgImage,0, canvas.height -50,192,50);
var ptrn = ctx.createPattern(bgImage, "repeat"); // Create a pattern with this image, and set it to repeat".
ctx.fillStyle = ptrn;
ctx.fillRect(0,canvas.height - bgImage.height,canvas.width, 50); // context.fillRect(x, y, width, height);
}
I tried to put your code in jsFiddle and it seems to work as expected:
var canvas = document.getElementById('myCanvas');
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
// Grass Background Image
var bgImage = new Image();
bgImage.onload = function () {
var ctx = canvas.getContext("2d");
ctx.drawImage(bgImage,0, canvas.height -50,192,50);
var ptrn = ctx.createPattern(bgImage, "repeat"); // Create a pattern with this image, and set it to repeat".
ctx.fillStyle = ptrn;
ctx.fillRect(0,canvas.height - bgImage.height,canvas.width, 50); // context.fillRect(x, y, width, height);
};
bgImage.src = "data:image/png;base64,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";
https://jsfiddle.net/or68kcpc/
The error must be somewhere else.
The problem is that the pattern is calculated starting from the top left corner of the canvas. If you paint starting at a canvas y-coordinate that isn't an integer multiple of the image height, the visible portion of the pattern won't start at the top of the image.
To fix this, shift the drawing context down before painting with the pattern, then paint the pattern at a location shifted up, then shift the context back up:
var shiftY = canvas.height % image.height;
context.translate(0, shiftY);
context.fillRect(0, canvas.height - image.height - shiftY,
canvas.width, image.height);
context.translate(0, -shiftY);
Run the snippet below to see a demonstration. The canvas height is 120, which means that we start painting from the y-coordinate 120 - 50 = 70, which is not a multiple of 50.
To correct for this, we shift the context down by 120 % 50 = 20 and then shift the painting location up by 20. Thus, the pattern gets painted on the shifted context at the y-coordinate (70 - 20) = 50, which is a multiple of the image height.
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d');
canvas.height = 120;
canvas.width = 500;
var image = new Image();
image.src = 'http://i.stack.imgur.com/2bfPb.png';
image.onload = function () {
var pattern = context.createPattern(image, "repeat");
context.fillStyle = pattern;
var shiftY = canvas.height % image.height;
context.translate(0, shiftY);
context.fillRect(0, canvas.height - image.height - shiftY,
canvas.width, image.height);
context.translate(0, -shiftY);
};
#myCanvas {
border: 1px solid #666;
}
<canvas id="myCanvas"></canvas>
Let me make one more observation. The loop in your code is inefficient and the bgReady flag is unnecessary because you can run the painting code in the image.onload function, as I have done in my snippet.

Can you draw images in an arc on a canvas?

You can draw arcs and you can draw images on a canvas using javascript, but is there anyway to do both? that is, is there anyway to draw an image as the arc instead of a solid line?
If not, is there a seperate way to arc an image?
I tried doing a pixel-by-pixel transformation of the image along the points of an arc, but it ended up being very slow and looking pretty poor since I can't directly get pixel data from javascript (or can you? I didnt see a way) so for each pixel, I needed to calculate the point along the arc, draw the current image pixel, refetch it, draw the image data to the calculated point, then clear that point on the canvas.
Have you tried setting the strokeStyle to a CanvasPattern based on an image? It looks like you could do something like this (assuming img is an HTMLImageElement that you want to draw from and ctx is a CanvasRenderingContext2D):
var pattern = ctx.createPattern(img, "repeat");
ctx.strokeStyle = pattern;
ctx.beginPath();
ctx.arc(123, 408, 80, 0, 1.5*Math.PI, false);
ctx.stroke();
Use this html to create arc : -
<canvas id="myCanvas" width="578" height="250"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = .8 * Math.PI;
var endAngle = 2.2 * Math.PI;
var counterClockwise = false;
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
context.lineWidth = 30;
// line color
context.strokeStyle = 'blue';
context.stroke();
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = 8 * Math.PI;
var endAngle =2.3 * Math.PI;
var counterClockwise = false;
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
context.lineWidth = 30;
// line color
context.strokeStyle = 'red';
context.stroke();
</script>

Categories

Resources