am looking for a solution to realize a spotlight on a html canvas which draws an image. I darkened the complete image with:
context.globalAlpha = 0.3;
context.fillStyle = 'black';
context.fillRect(0, 0, image.width, image.height);
This works fine. Now I want to remove the darkening effect on a specific rectangle area. How can I do this without calculation of the "surrounding" rectangles?
Redraw a region of original image
You can simply draw back the image with the specified rectangular region you want to "reveal":
/// reset global alpha and then
context.globalAlpha = 1;
context.drawImage(image, rx, ry, rw, rh,
rx, ry, rw, rh)
Here you pick first the source rectangle (first line), then the destination rectangle. If width and height (rw and rh) are different the source region is scaled to fit the destination region. If the image size is 1:1 both source and destination would be the same (as in this example).
Clipping
As an alternative you can use clipping instead:
/// to remove clipping properly save current state
context.save();
/// define region you want to clip
context.beginPath();
context.rect(rx, ry, rw, rh);
/// set clip
context.clip();
/// draw the image as the original
context.drawImage(image, x, y);
/// remove clipping
context.restore();
ONLINE DEMO SHOWING THESE TWO TECHNIQUES
Alternatives
Composite modes (globalCompositeOperation) allow clipping as well but would not be useful here as you would still need to draw with a region of the original image and therefor you can just draw it directly (as in first example).
Another way could be to use two canvases where you draw the image on the bottom canvas and the mask on the top canvas. Then punch a hole in the top canvas. If you want to animate/move around the mask this can be a good solution.
Related
I am making a rectangle :
rect = new Rectangle();
rect.x = 40;
rect.y = 100;
rect.setSize(100,100);
and then also drawing some texture on the position of rectangle like:
batch.draw(myTexture, rect.x , rect.y, rect.width, rect.height);
Now what i want to do is also see the shape of rectangle like i must see some boundary so that i can see my rectangle boundary as well as my texture. And i need this for debugging purposes as the texture is complex like somewhere the background is transparent so i want to remove the collision from those places and that is why i am thinking if i could see both the rectangles and the sprite it would be great.
Although i tried doing this:
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
shapeRenderer.setColor(Color.RED);
shapeRenderer.rect(0,0,100,100);
shapeRenderer.end();
I thought the above code would help me draw the texture as well as see the rectangle but turns out i could see only the rectangle's boundary and not the texture.
Here is the output:
In the image , you can see a red rectangle and that is where i was trying to debug my bar (a bar of yellow color was also supposed to be shown here) but my yellow doesn't appear as this red-boundary rectangle overlapped my bar.
To be clear, i want to see the shapeRenderer right above the texture and be able to see the both of them
So i found the answer,
As you know i wanted to appear a rectangle over my texture so that i could see both - my texture and my shapeRenderer shape but instead i could see only my shape and not the texture.
Problem was that What is was doing is i began the batch and inside that i wrote the code for drawing to the batch and also the code for drawing the shapeRenderer to the batch like this:
batch.begin();
batch.draw(texture,0,0,0,0);
shapeRenderer.begin();
shapeRenderer.rect(0,0,0,0);
shapeRenderer.end();
batch.end();
The above code replaces my texture as it can only draw one thing at the coordinates 0,0 so first it draws the texture at 0,0 and then the shaperenderer at 0,0.
The solution was:
To draw the shapeRenderer separately and not within the batch.begin() and batch.end() like:
batch.begin();
batch.draw(texture,0,0,0,0);
batch.end();
shapeRenderer.begin();
shapeRenderer.rect(0,0,0,0);
shapeRenderer.end();
I am trying to fill color for an arc. But, I don't want to use ctx.lineWidth to do that.
I try to draw it here. Please teach me how to fill color for it.
You need to define a continuous path to be able to fill it with a single color if you don't want to use line-width.
Using two arcs means you need to "reverse" one of them so their end-points connect correctly without crossing. You can do this by using the counter-close wise flag at the end set to true and swap start and end angles:
ctx.arc(W/2, H, H-40, 2*Math.PI, Math.PI, true); // CCW, swapped angles
ctx.arc(W/2, H, H, Math.PI, 2*Math.PI);
ctx.fill(); // now it can be filled
Updated fiddle
How to draw shapes like rectangle, circle with only outline (fill with transparent color inside) in AS3 like this
rectangle and circle
Use graphics.lineStyle with graphics.drawRect or graphics.drawCircle.
// this means you want to draw a line on the outer edge
// 1 is thickness, and you got more options like color, etc.
graphics.lineStyle(1);
// draw rect
graphics.drawRect(x, y, width, height);
// draw circle
graphics.drawCircle(x, y, radius);
Thanks to #BotMaster for pointing out you don't need begin/end fill.
The problem
I'm trying to create a brush tool with opacity jitter (like in Photoshop). The specific problem is:
Draw a stroke on an HTML canvas with different levels of opacity. Pixels with higher opacity should replace pixels with lower opacity; otherwise, pixels are left unchanged.
Transparency should not be lost in the process. The stroke is drawn on a separate canvas and merged with a background canvas afterwards.
The result should look like this. All code and the corresponding output can be found here (JSFiddle).
Because you can't stroke a single path with different levels of opacity (please correct me if I'm wrong) my code creates a path for each segment with different opacity.
Non-solution 1, Using the 'darken' blend mode
The darken blend mode yields the desired result when using opaque pixels but doesn't seem to work with transparency. Loosing transparency is a dealbreaker.
With opaque pixels:
With transparent pixels:
Non-solution 2, Using the 'destination-out' compositing operator
Before drawing a new stroke segment, subtract its opacity from subjacent pixels by using the 'destination-out' compositing operator. Then add the new stroke segment with 'source-over'. This works almost but it's a little bit off.
Looking for a solution
I want to avoid manipulating each pixel by hand (which I have done in the past). Am I missing something obvious? Is there a simple solution to this problem?
"Links to jsfiddle.net must be accompanied by code."
Because you can't stroke a single path with different levels of opacity (please correct me if I'm wrong)
You're wrong =)
When you use globalCompositeOperation = 'destination-out' (which you are in lineDestinationOut) you need to set the strokeStyle opacity to 1 to remove everything.
However, simply changing that in your fiddle doesn't have the required effect due to the order of your path build. Build the 10% transparent one first, the whole length, then delete and draw the two 40% transparent bits.
Here's a jsfiddle of the code below
var canvas = document.getElementById('canvas');
var cx = canvas.getContext('2d');
cx.lineCap = 'round';
cx.lineJoin = 'round';
cx.lineWidth = 40;
// Create the first line, 10% transparency, the whole length of the shape.
cx.strokeStyle = 'rgba(0,0,255,0.1)';
cx.beginPath();
cx.moveTo(20,20);
cx.lineTo(260,20);
cx.lineTo(220,60);
cx.stroke();
cx.closePath();
// Create the first part of the second line, first by clearing the first
// line, then 40% transparency.
cx.strokeStyle = 'black';
cx.globalCompositeOperation = 'destination-out';
cx.beginPath();
cx.moveTo(20,20);
cx.lineTo(100,20);
cx.stroke();
cx.strokeStyle = 'rgba(0,0,255,0.4)';
cx.globalCompositeOperation = 'source-over';
cx.stroke();
cx.closePath();
// Create the second part of the second line, same as above.
cx.strokeStyle = 'black';
cx.globalCompositeOperation = 'destination-out';
cx.beginPath();
cx.moveTo(180,20);
cx.lineTo(260,20);
cx.stroke();
cx.strokeStyle = 'rgba(0,0,255,0.4)';
cx.globalCompositeOperation = 'source-over';
cx.stroke();
cx.closePath();
Use two layers to draw to:
First calculate the top layer opacity 40% - 10% and set this as alpha on top layer
Set bottom layer to 10%
Set top layer with dashed lines (lineDash) (calculate the dash-pattern size based on size requirements)
Draw lines to both layers and the bottom layer will be a single long line, the top layer will draw a dashed line on top when stroked.
Copy both layers to main canvas when done.
#HenryBlyth's answer is probably the best you're going to get; there's no native API to do what you're being asked to do (which, in my opinion, is kinda weird anyways... opacity isn't really supposed to replace pixels).
To spell out the solution in one paragraph: Split up your "stroke" into individual paths with different opacities. Draw the lowest opacity paths as normal. Then, draw the higher opacities with "desitination-out" to remove the low-opacity paths that overlap. Then, draw the high opacity paths as usual, with "source-over", to create the effect desired.
As suggested in the comments to that answer, #markE's comment about making each path an object that is pre-sorted before drawing is a great suggestion. Since you want to perform manual drawing logic that the native API can't do, turning each path into an object and dealing with them that way will be far easier than manually manipulating each pixel (though that solution would work, it could also drive you mad.)
You mention that each stroke is being done on another canvas, which is great, because you can record the mouseevents that fire as that line is being drawn, create an object to represent that path, and then use that object and others in your "merged" canvas without having to worry about pixel manipulation or anything else. I highly recommend switching to an object-oriented approach like #markE suggested, if possible.
I am trying to understand the method transition that falls in the Matrix Class. I am using it to copy pieces of a bitMapData. But I need to better understand what transitions do.
I have a tilesheet that has 3 images on it. all 30x30 pixels. the width of the total bitmap is 90pxs.
The first tile is green, the second is brown, and the third is yellow. If I move over 30pxs using the matrix that transitions, instead of getting brown, I get yellow, if I move over 60px, I get brown.
If I move -30 pixels, then the order is correct. I am confused on what is going on.
tileNum -= (tileNumber * tWidth);
theMatrix = new Matrix();
theMatrix.translate(tileNum,0);
this.graphics.beginBitmapFill(tileImage,theMatrix);
this.graphics.drawRect(0, 0,tWidth ,tHeight );
this.graphics.endFill();
Can someone tell me how transitions work, or some resources that show how they work. I ultimately want to know a good way to switch back and forth between each tile.
First of all, don't confuse translation with transition. The latter is a general English word for "change", whereas to translate in geometry and general math is to "move" or "offset" something.
A transformation matrix defines how to transform, i.e. scale, rotate and translate, an object, usually in a visual manner. By applying a transformation matrix to an object, all pixels of that object are rotated, moved and scaled/interpolated according to the values stored inside the matrix. If you'd rather not think about matrix math, just think of the matrix as a black box which contains a sequence of rotation, scaling, and translation commands.
The translate() method simply offsets the bitmap that you are about to draw a number of pixels in the X and Y dimensions. If you use the default ("identity") matrix, which contains no translation, the top left corner of your object/bitmap will be in the (0,0) position, known as the origin or registration point.
Consider the following matrix:
var mtx : Matrix = new Matrix; // No translation, no scale, no rotation
mtx.translate(100, 0); // translated 100px on X axis
If you use the above matrix with a BitmapData.draw() or Graphics.beginBitmapFill(), that means that the top left corner of the original bitmap should be at (x=100; y=0) in the target coordinate system. Sticking to your Graphics example, lets first consider drawing a rectangle without a matrix transformation.
var shape : Shape = new Shape;
shape.graphics.beginBitmapFill(myBitmap);
shape.graphics.drawRect(0, 0, 200, 200);
This will draw a 200x200 pixels rectangle. Since there is no transformation involved in the drawing method (we're not supplying a transformation matrix), the top left corner of the bitmap is in (x=0; y=0) of the shape coordinate system, i.e. aligned with the top left corner of the rectangle.
Lets look at a similar example using the matrix.
var shape : Shape = new Shape;
shape.graphics.beginBitmapFill(myBitmap, mtx);
shape.graphics.drawRect(0, 0, 200, 200);
This again draws a rectangle that is 200px wide and 200px high. But where inside this rectangle will the top left corner of myBitmap be? The answer is at (x=100, y=0) of the shape coordinate system. This is because the matrix defines such a translation.
But what then will be to the left of (x=100; y=0)? With the above code, the answer is that the bitmap repeats to fill the entire rectangle, and hence you will see the rightmost side of the bitmap, to the left of the leftmost side, as if there was another instance of the bitmap right next to it. If you want to disable the repeating image, set the third attribute of beginBitmapFill() to false:
shape.graphics.beginBitmpFill(myBitmap, mtx, false);
Lets take a look at one last example that might help your understanding. Remember that the translation matrix defines the position of the top left corner of an image, in the coordinate system of the shape. With this in mind, consider the following code, using the same matrix as before.
var shape : Shape = new Shape;
shape.graphics.beginBitmapFill(myBitmap, mtx);
shape.graphics.drawRect(100, 0, 100, 100);
Notice that this will draw the rectangle 100px in on the X axis. Not coincidentally, this is the same translation that we defined in our matrix, and hence the position of the top left corner of the bitmap. So even though repeating is enabled, we will not see a repeating image to the left of our rectangle, because we only start drawing at the point where the bitmap starts.
So the bottom line is, I guess, that you could think of the transform matrix as a series of transformation commands that you apply to your image as you draw it. This will offset, scale and rotate the image as it's drawn.
If you are curious about the inner workings of the matrix, Google transformation matrices, or read up on Linear Algebra!