Multiply effect kinteicjs - javascript

I want to know have to make multiply effects with kineticjs framework.
My problem is next:
I have background which is image
I need to draw polygon over image and apply multiply effect on that new polygon.
Can someone help?
Thanks

You can apply image filters by:
Reading the rgb pixels of the image you wish to filter-- context.getImageData
Alter the rgb pixel values according to your filter
Replace the pixels in the image with the altered pixels-- context.putImageData
Here is how to apply a multiply filter to an image:
function multiply(R,G,B){
imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
data = imgData.data;
for (var i=0;i<data.length;i+=4){
data[i ] = R*data[i]/255;
data[i+1] = G*data[i+1]/255;
data[i+2] = B*data[i+2]/255;
}
ctx.putImageData(imgData,0,0);
}
// Usage:
multiply(130,130,200)

Related

WebGL readPixels returns flipped y axis

I have no idea why but the image that I read from canvas gets flipped on y axis.
The ultimate goal is the read a portion of WebGL canvas and extract it as JPG/PNG.
Workflow is the following:
gl.readPixels
create 2D canvas
load Uint8Array pixels to 2D canvas as imageData
get 2D canvas as blob
create object URL
use it as image src
Here's my code: https://jsitor.com/acM-2WTzd
I'm really sorry about the length (almost 300) but it's WebGL, there's so much boilerplate and setup.
I've tried to debug it for several hours and I have no idea (granted it could be the shader, I'm quite new at that).
If you have any additional question, please feel free to ask!
Unlike context.getImageData(), gl.readPixels() reads pixel data starting from the bottom-left corner, not from the top-left corner. You can apply a transformation on tempCanvas and draw it onto itself after putting the image data like this:
context.putImageData(imageData, 0, 0);
// add the following
context.translate(0, cropHeight);
context.scale(1, -1);
context.drawImage(tempCanvas, 0, 0);
Alternatively, you can manually rearrange the pixel data before returning it from your getPixels() function:
function getPixels(x, y, width, height) {
const length = width * height * 4;
const row = width * 4;
const end = (height - 1) * row;
const arr = new Uint8Array(length);
const pixels = new Uint8Array(length);
if (draw) draw();
gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, arr);
for (let i = 0; i < length; i += row) {
pixels.set(arr.subarray(i, i + row), end - i);
}
return pixels;
}

In a 2D canvas, is there a way to give a sprite an outline?

I'd like to give a sprite an outline when the character gets healed/damaged/whatever but I can't think of a way to code this using the 2d canvas. If it were possible, I'd think it would be a global composite operation, but I can't think of a way to achieve it with one of them.
I did find this stackoverflow answer that recommends creating a fatter, solid color version of the original and put the original on top of it. That would give it an outline, but it seems like a lot of extra work especially considering I'm using placeholder art. Is there an easier way?
This question is different from the one linked because this is specifically about the HTML5 2D canvas. It may have a solution not available to the other question.
For what it's worth, I don't mind if the outline creates a wider border or keeps the sprite the same size, I just want the outline look.
Just draw your original image in 8 position around the original image
Change composite mode to source-in and fill with the outline color
Change composite mode back to source-over and draw in the original image at correct location
This will create a clean sharp outline with equal border thickness on every side. It is not so suited for thick outlines however. Image drawing is fast, especially when image is not scaled so performance is not an issues unless you need to draw a bunch (which in that case you would cache the drawings or use a sprite-sheet anyways).
Example:
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.src = "http://i.stack.imgur.com/UFBxY.png";
function draw() {
var dArr = [-1,-1, 0,-1, 1,-1, -1,0, 1,0, -1,1, 0,1, 1,1], // offset array
s = 2, // scale
i = 0, // iterator
x = 5, // final position
y = 5;
// draw images at offsets from the array scaled by s
for(; i < dArr.length; i += 2)
ctx.drawImage(img, x + dArr[i]*s, y + dArr[i+1]*s);
// fill with color
ctx.globalCompositeOperation = "source-in";
ctx.fillStyle = "red";
ctx.fillRect(0,0,canvas.width, canvas.height);
// draw original image in normal mode
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(img, x, y);
}
<canvas id=canvas width=500 height=500></canvas>
Maybe it would be worth trying this :
• build a canvas 1.1 time bigger than the original sprite
• fill it with the outline color
• draw the sprite scaled by 1.1 on the canvas using destination-in globalCompositeOperation.
Then you have a bigger 'shadow' of your sprite in the outline color.
When you want to draw the outline :
• draw the 'shadow' (centered)
• draw your sprite within the shadow.
Depending on the convexity of your sprite, this will work more or less nicely, but i think it's worth trying since it avoids you doubling the number of input graphic files.
I just did a short try as proof-of-concept and it quite works :
http://jsbin.com/dogoroxelupo/1/edit?js,output
Before :
After :
html
<html>
<body>
<image src='http://www.gifwave.com/media/463554/cartoons-comics-video-games-sprites-scott-pilgrim-paul-robertson_200s.gif' id='spr'></image>
<canvas id='cv' width = 500 height= 500 ></canvas>
</body>
</html>
code
window.onload=function() {
var spr = document.getElementById('spr');
var margin = 4;
var gh = createGhost(spr, '#F80', margin);
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
var outlined = true;
setInterval(function() {
ctx.clearRect(0,0,cv.width, cv.height);
if (outlined)
ctx.drawImage(gh, 0, 0)
ctx.drawImage(spr, 0, 0)
outlined = !outlined;
}, 400);
}
function createGhost (img, color, margin) {
var cv= document.createElement('canvas');
cv.width = img.width+2*margin;
cv.height = img.height + 2*margin;
var ctx = cv.getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0,0, cv.width, cv.height);
ctx.save();
ctx.globalCompositeOperation = 'destination-in';
var scale = cv.width/spr.width;
ctx.scale(cv.width/spr.width, cv.height/spr.height);
ctx.drawImage(img, -margin, -margin);
ctx.restore();
return cv;
}
You could use strokeRect method to outline the sprite after drawing it. It should be asy if you know your sprite's dimensions...

JavaScript canvas, manually cloning a canvas onto another generates a weird pattern

I'm trying to make a text effect similar to the effect found at the bottom of this article
My proposed approach is:
Make two canvasses, one is visible, the other is invisible I use this as a buffer.
Draw some text on the buffer canvas
Loop over getImageData pixels
if pixel alpha is not equal to zero (when there is a pixel drawn on the canvas buffer) with a small chance, ie 2%, draw a randomly generated circle with cool effecs at that pixel on the visible canvas.
I'm having trouble at step 4. With the code below, I'm trying to replicate the text on the second canvas, in full red. Instead I get this weird picture.
code
// create the canvas to replicate the buffer text on.
var draw = new Drawing(true);
var bufferText = function (size, textFont) {
// set the font to Georgia if it isn't defined
textFont = textFont || "Georgia";
// create a new canvas buffer, true means that it's visible on the screen
// Note, Drawing is a small library I wrote, it's just a wrapper over the canvas API
// it creates a new canvas and adds some functions to the context
// it doesn't change any of the original functions
var buffer = new Drawing(true);
// context is just a small wrapper library I wrote to make the canvas API a little more bearable.
with (buffer.context) {
font = util.format("{size}px {font}", {size: size, font: textFont});
fillText("Hi there", 0, size);
}
// get the imagedata and store the actual pixels array in data
var imageData = buffer.context.getImageData(0, 0, buffer.canvas.width, buffer.canvas.height);
var data = imageData.data;
var index, alpha, x, y;
// loop over the pixels
for (x = 0; x < imageData.width; x++) {
for (y = 0; y < imageData.height; y++) {
index = x * y * 4;
alpha = data[index + 3];
// if the alpha is not equal to 0, draw a red pixel at (x, y)
if (alpha !== 0) {
with (draw.context) {
dot(x/4, y/4, {fillColor: "red"})
}
}
}
}
};
bufferText(20);
Note that here, my buffer is actually visible to show where the red pixels are supposed to go compared to where they actually go.
I'm really confused by this problem.
If anybody knows an alternative approach, that's very welcome too.
replace this...
index = x * y * 4;
with...
index = (imageData.width * y) + x;
the rest is good :)

html5 canvas use image as mask

Is it possible to use a image with a shape as a mask for a whole canvas or images within the canvas?
I want to place images in a canvas with a mask over the images, and then save it as a new image.
You can use a black and white image as a mask using 'source-in' globalCompositeOperation. First you draw your mask image to the canvas, then you change the globalCompositeOperation to 'source-in', finally you draw your final image.
Your final image will only be draw where it overlay the mask.
var ctx = document.getElementById('c').getContext('2d');
ctx.drawImage(YOUR_MASK, 0, 0);
ctx.globalCompositeOperation = 'source-in';
ctx.drawImage(YOUR_IMAGE, 0 , 0);
More info on global composite operations
In addition to Pierre's answer you can also use a black and white image as a mask source for your image by copying its data into a CanvasPixelArray like:
var
dimensions = {width: XXX, height: XXX}, //your dimensions
imageObj = document.getElementById('#image'), //select image for RGB
maskObj = document.getElementById('#mask'), //select B/W-mask
image = imageObj.getImageData(0, 0, dimensions.width, dimensions.height),
alphaData = maskObj.getImageData(0, 0, dimensions.width, dimensions.height).data; //this is a canvas pixel array
for (var i = 3, len = image.data.length; i < len; i = i + 4) {
image.data[i] = alphaData[i-1]; //copies blue channel of BW mask into A channel of the image
}
//displayCtx is the 2d drawing context of your canvas
displayCtx.putImageData(image, 0, 0, 0, 0, dimensions.width, dimensions.height);

Canvas: mask an image and preserve its alpha channel?

Here's what I'm trying to do:
Get image A, and image B. Image B is a black and white mask image.
Replace image A's alpha channel with image B's red channel.
Draw image C on the canvas.
Draw image A on top of image C.
Everything seems ok until step 4. Image C isn't visible at all and where image A should be transparent there's white color.
cx.putImageData(imageA, 0, 0);
var resultData = cx.getImageData(0, 0, view.width, view.height);
for (var h=0; h<resultData.data.length; h+=4) {
resultData.data[h+3] = imageB.data[h];
}
cx.putImageData(imageC, 0, 0);
cx.putImageData(resultData, 0, 0);
Simon is right: the putImageData method does not pay any attention to compositing; it merely copies pixel values. In order to get compositing, we need to use drawing operations.
We need to mess with the channels (turn red into alpha) with the pixel data, put that changed pixel data into an image, and then use a composite operation to get the desired masking.
//copy from one channel to another
var assignChannel = function(imageData, channelTo, channelFrom) {
if(channelTo < 0 || channelTo > 3 || channelFrom < 0 || channelFrom > 3) {
throw new Error("bad channel number");
}
if(channelTo == channelFrom)
return;
var px = imageData.data;
for(var i = 0; i < px.length; i += 4) {
px[i + channelTo] = px[i + channelFrom];
}
};
/**============================================================================
* this function uses 3 or 4 canvases for clarity / pedagogical reasons:
* redCanvas has our mask image;
* maskCanvas will be used to store the alpha channel conversion of redCanvas' image;
* imageCanvas contains the image to be masked;
* ctx is the context of the canvas to which the masked image will be drawn.
============================================================================**/
var drawOnTopOfRed = function(redCanvas, maskCanvas, imageCanvas, ctx) {
var redImageData = redCanvas.getContext("2d").getImageData(0, 0, w, h);
//assign the alpha channel
assignChannel(redImageData, 3, 0);
//write the mask image
maskCanvas.getContext("2d").putImageData(redImageData, 0, 0);
ctx.save();
//draw the mask
ctx.globalCompositeOperation = "copy";
ctx.drawImage(maskCanvas, 0, 0);
//draw the image to be masked, but only where both it
//and the mask are opaque; see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing for details.
ctx.globalCompositeOperation = "source-in";
ctx.drawImage(imageCanvas, 0, 0);
ctx.restore();
};
jsfiddle example
A doodle with the example:
Because in step 4 you are using putImageData which perfectly replaces pixels. You want to draw image A on top of image C, so you can't do this. Instead you will want to use drawImage()
So do:
cx.putImageData(imageC, 0, 0); // step 3
// create a new canvas and new context,
// call that new context ctx2 and canvas can2:
var can2 = document.createElement('canvas');
// set can2's width and height, get the context etc...
ctx2.putImageData(resultData, 0, 0);
cx.drawImage(can2, 0, 0); // step 4 using drawImage instead of putting image data

Categories

Resources