AS3 Feather Edge Circle with gradient - actionscript-3

I'm trying to draw a very big circle and feather the edge with a gradient
var mat:Matrix= new Matrix();
var colors:Array=[0x000000,0x000000];
var alphas:Array=[1,0];
var ratios:Array=[200,255];
var size:int = (circleMenu.innerRadius + circleMenuOffset) * circleMenuScale;
mat.createGradientBox(size * 2,size * 2, 0 , 0 , 0);
circleMenuBackground.graphics.clear();
circleMenuBackground.graphics.beginGradientFill(GradientType.RADIAL, colors,alphas,ratios,mat);
circleMenuBackground.graphics.drawCircle(size/2,size/2,size);
circleMenuBackground.graphics.endFill();
The circle is very big, and I want the gradient to start at the last 10% or so of the circle. I can either make it fully black, or disappear, I can't work out how to get a very small gradient. Any clues gratefully appriciated!

Related

SkiaSharp - how to create a hatch or dotted pattern on the filled space between curves

In SkiaSharp I can nicely fill the space between two curves by using SKPathFillType.EvenOdd. Below I show a simplified excerpt from the code.
My question is how can I give a certain pattern to this filled area between the curves ? Here I can only fill it with a color and give it a transparency. I'm interested in applying a pattern, such as hatch or dots.
Thank you for any support.
Greetings,
Sorin
SKPath path = new SKPath();
path.FillType = SKPathFillType.EvenOdd;
// start the first curve
path.MoveTo(....);
path.LineTo(....); // draw the curve and close it
....
path.AddCircle(....); // add a second curve as a circle
SKPaint paint = new SKPaint(new SKFont(SKTypeface.Default)) {
IsAntialias = true,
Style = SKPaintStyle.Fill,
Color = SKColors.Blue.WithAlpha((byte)(0xFF * (1 - 0.5))),
StrokeWidth = 1
};
canvas.DrawPath(path, paint);
I've managed to fix this with a trick.
First of all, I do all I wrote above, i.e.
canvas.DrawPath(path, paint)
.... will draw a filled area between the two curves, with a certain transparency.
On top of that (literally), I draw another pattern:
var hatch = new SKPath();
hatch.AddCircle(0, 0, 1);
var hatchPaint = new SKPaint {
PathEffect = SKPathEffect.Create2DPath(SKMatrix.MakeScale(7, 7), hatch),
Color = SKColors.RosyBrown,
Style = SKPaintStyle.Stroke,
StrokeWidth = 3
};
And again:
canvas.DrawPath(path, hatchPaint);
This draws a nice hatch pattern on top of the filled area between the curves.
Note: the size of the pattern is essential - here AddCircle(0, 0, 1), where the circle ray is 1 pixel. If you have a larger one, the hatch pattern will spill out the filled area, which is not what you want. To me this looks like a bug in SKIA.

Why are artifacts visible in a scaled html5 canvas?

I've seen this and this discussion about removing antialiasing in canvases, but I don't think this is the same thing.
After scaling an html5 canvas by an arbitrary value (i.e., making it responsive), I've noticed that if I draw two rectangles of the same size and in the same location, the edges of the scaled side of the first rectangle remain visible.
I've included an example snippet where I draw a grey rectangle, then draw an red rectangle on top of it. There's a one-pixel red vertical line on the left and right edges of the grey rectangle. I know it may seem trivial, but it's very noticeable in my situation.
How do I fix this? Thanks!
var example = document.getElementById("example");
var ctx = example.getContext('2d');
ctx.scale(1.13,1);
ctx.fillStyle = "LightGrey";
ctx.fillRect(10,10,50,30);
ctx.fillStyle = "Black";
ctx.font = "20px Arial";
ctx.fillText("< Looks good.",70,30);
ctx.fillStyle = "Red";
ctx.fillRect(10,50,50,30);
// This light grey rectangle should completely cover the previous red one, but it doesn't!
ctx.fillStyle = "LightGrey";
ctx.fillRect(10,50,50,30);
ctx.fillStyle = "Black";
ctx.font = "20px Arial";
ctx.fillText("< Do you see red?",70,70);
<canvas id="example"></canvas>
You are scaling the transform matrix by a factor of 1.13 on the X axis.
So your coordinate 10, will actually end up on at coordinate 11.3 on the real pixels matrix.
You can't draw on fraction of pixels, so indeed antialiasing will kick in here.
So why does the first one looks better?
Because the mix between grey and white* is more neutral than the one between red grey and white. But even your first rect is antialiased.
Just zoom in your canvas and you'll see it, there is a one pixel band on both sides that is actually semi-transparent.
* "White" here is the one of the page's background
var example = document.createElement("canvas");
var ctx = example.getContext('2d');
ctx.scale(1.13,1);
ctx.fillStyle = "LightGrey";
ctx.fillRect(10,10,50,30);
ctx.fillStyle = "Red";
ctx.fillRect(10,50,50,30);
ctx.fillStyle = "LightGrey";
ctx.fillRect(10,50,50,30);
// draw bigger with no antialiasing
var z_ctx = zoomed.getContext('2d');
zoomed.width = example.width * 10;
zoomed.height = example.height * 10;
z_ctx.imageSmoothingEnabled = false;
z_ctx.drawImage(example, 0,0, zoomed.width, zoomed.height);
<canvas id="zoomed"></canvas>
So how to avoid this?
Well simply avoid filling at non integer pixel coordinates. This means you have to be constantly aware of your context transformation matrix too, not only of the values you pass to the drawing functions.
(Ps: also remember that stroke is an even eviler beast since it start drawing from the middle of the line, so in this case, you even have to take into considerations the lineWidth, see this Q/A on the matter).

HTML5 Canvas determine shapes from a sillouette

I'm trying to identity the center x and y of a circle drawn from an png image source in a canvas, is there a context 2d function that can do this?
Or is there a function that can trace a circle in a png file so that I can identify its coordinates for center x and y?
I just need the logic thanks
There is no native method to identify shapes on an html5 canvas.
Once the pixels are drawn any information about how they were drawn (circle, rectangle, etc) is forgotton.
A method to find your circle
Your circle must be differentiated from the rest of the image.
Is it a unique color? Is the rest of the image transparent?
At the point where you have a differentiation, you can use getImageData to fetch the red, blue, green & alpha information about every pixel on the canvas.
var pixelData = context.getImageData(0,0,canvas.width,canvas.height).data;
This pixelData is one long array with each pixel's color data being in an element:
firstPixelRed=pixelData[0];
firstPixelGreen=pixelData[1];
firstPixelBlue=pixelData[2];
firstPixelAlpah=pixelData[3];
//
secondPixelRed=pixelData[4];
secondPixelGreen=pixelData[5];
secondPixelBlue=pixelData[6];
secondPixelAlpah=pixelData[7];
You can use this pixelData to identify all pixels which are inside your circle.
From these "inside circle" pixels, find their minimumX, minimumY, maximumX & maximumY coordinates.
These minimums & maximums will give you the bounding box of the circle.
topleft = [minumumX,minumimY]
topright = [maximumX,minimumY]
bottomright= [maximumX,maximumY]
bottomleft = [minumumX,maximumY]
The radius of the circle is:
var radius = (maximumX - minimumX) /2;
So the center point of the circle is:
var centerX = minimumX + radius;
var centerY = minimumY + radius;
And you've got your circle with center point [centerX,centerY] with radius!

AS3 image rotate within / scale to fit sprite

We're making bitmap data from a sprite where we want to take an image and rotate within / scale to fit. This is our code, which includes a rotation.
_rotation defines how much the user has input.
The problem is, we're getting an output file that is 100% white.
We think that the image is rotating about 0x0y therefore rotating the image outside the bounds of the sprite.
Furthermore, the image is not scaling to the child, instead is sort of "cropping" as it inherits.
What is the best way of doing this? Basically we want to take an image and rotate within / scale to fit
var sprite1:Sprite = new Sprite();
addChild(sprite1);
var photoBitmap:Bitmap = new Bitmap(_bitmapData);
sprite1.addChild(photoBitmap);
sprite1.rotation = _rotation;
var sprite2:Sprite = new Sprite();
addChild(sprite2);
sprite2.addChild(sprite1);
var bitmapData:BitmapData = new BitmapData(sprite2.width,sprite2.height,false,0xFFFFFF);
bitmapData.draw(sprite2);
The simple way to draw sprite with scaling/rotating is using Matrix in method bitmapData.draw().
Example:
var sourceImage:SomeSprite = new SomeSprite();
var matrix:Matrix = new Matrix();
matrix.scale(0.5, 0.5);
matrix.rotate(0.5 * Math.PI);
var newImage:BitmapData = new BitmapData(sourceImage.width/2, sourceImage.height/2);
newImage.draw(sourceImage, matrix);
Your issue is likely a cause of rotating around the top left corner (which can make the entire object left of or above the registration point (0 x and 0 y) and not get drawn.
An easy way you can account for this, is to move sprite1 after the rotation to account for the new size and position caused by rotating:
...
sprite2.addChild(sprite1);
var actualPosition:Rectangle = sprite2.getBounds(sprite2); //this gets the new position/dimensions of the object
sprite1.x = -actualPosition.x;
sprite1.y = -actualPosition.y;
var bitmapData:BitmapData = new BitmapData(sprite2.width,sprite2.height,false,0xFFFFFF);
...

Make white background of image transparent in css

I have two images, one of which is a small icon that is superimposed over the first image. My icon has a white background, so when the icon is placed over the other image, we get this effect where a white square appears over the image. Ideally, I do not want to display this white background on top of my other image. Is there is a CSS property I can apply to my icon to make its white background transparent?
Actually there is a way although only currently supported on Chrome, Firefox, and Safari. If the background color is white, you can add the CSS property:
mix-blend-mode: multiply;
You can read more about it here: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
Opacitator
mix-blend-mode does work for some browsers, but we've found that it causes performance issues in chrome, I have no idea why.
A designer on my team came up with this genius hack, where you create a layer that is mostly transparent, but when it is laid over a white background, it's color will match the color of the surrounding background.
The way this "magical" color is found; is by calculating how much darker each color axis should be for the amount of opacity removed. The formula for this is 255 - ( 255 - x ) / opacity. The issue is: If the opacity is set too low the formula gives you negative numbers (which can't be used). If the opacity is too high, you'll get some coloring on the non-white portions of your image.
Initially we used a spreadsheet that would do the calculations and through manual trial and error we would find that Goldilox color.
Once we started using sass I realized that this can be accomplished with a binary search. So I created a sass function that does the work for us.
Check out this gist on sassmeister. Pass your background color in-to the opacitator function on line 56 of the sass-code. and use the generated rgba color in a div (or a pseudo element) to overlay the image.
I also created a working example on codepen.
As there is no reliable way to remove background with CSS, sharing a code snippet of how I did it with JS:
public async removeImageBackground(image) {
const backgroundColor = { red: 255, green: 255, blue: 255 };
const threshold = 10;
const imageElement = new Image();
imageElement.src = image;
await new Promise(function(resolve) { imageElement.addEventListener('load', resolve); });
var canvas = document.createElement('canvas');
canvas.width = imageElement.naturalWidth;
canvas.height = imageElement.naturalHeight;
var ctx = canvas.getContext('2d');
ctx.drawImage(imageElement, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (var i = 0; i < imageData.data.length; i += 4) {
const red = imageData.data[i];
const green = imageData.data[i + 1];
const blue = imageData.data[i + 2];
if (Math.abs(red - backgroundColor.red) < threshold &&
Math.abs(green - backgroundColor.green) < threshold &&
Math.abs(blue - backgroundColor.blue) < threshold) {
imageData.data[i + 3] = 0;
}
}
ctx.putImageData(imageData, 0, 0);
return canvas.toDataURL(`image/png`);
}
You can make a container for your image.
Then for the css of the container:
overflow:hidden; height: (depends on your image, then make it a smaller px); width:100%;
Hope it helps. :)
No. Not yet...
It is getting very close to possible, though. Check out this article about CSS Filters, an experiemental css feature, that is doing some things client-side that are neat.
CSS Filters