Scale image width and height according to a number - node.js

I have a certain amount of squares with the width and height of 40, Is there any way to fit them "perfectly" into an image generated with node-canvas without the image being too big or too small? Also making the image size balanced so that there is not too much squares on the width or too much on the height.
Here is what I have tried:
let b = ((accent.length + other.length + 1) * 40); // the amount of squares * 40 (width/height)
let canvas = new Canvas(b, b);
and:
let b = ((accent.length + other.length + 1) * 40) / 2; // the amount of squares * 40 (width/height)
let canvas = new Canvas(b, b);
But that either just makes the image too big or too small.

Related

Resize image keeping aspect ratio and fit in bounds nodejs

I need to resize images to fit in a specific dimensions. I want to keep aspect ratio.
For example
original image:
w:634
h:975
resize to max:
w:50
h:100
result:
w:50
h:85
I have not found anything that could do that(calculations for w and h)
and I am too dumb to figure it out by myself
copilot suggested me something that just keeps aspect ratio
If you want to use packages. I prefer jimp for image editing.
Jimp.read('image.jpg')
.then((lenna) => {
const isHorizontal = lenna.getWidth() > lenna.getHeight();
const ratio = isHorizontal
? lenna.getWidth() / lenna.getHeight()
: lenna.getHeight() / lenna.getWidth();
const width = 375; // set the width you want
const height = isHorizontal ? width / ratio : width * ratio;
return lenna.resize(width, height).quality(60).write("image.jpg");
})
.catch((err) => {
console.error(err);
});
Calculate the aspect ratio of your original image and of your maximum dimensions. According to that ratios, either take the maximum width or height as fixed and calculate the other side accordingly.
let
oheight = 634, owidth = 975,
mheight = 50, mwidth = 100,
theight, twidth;
let
oratio = owidth/oheight, // (~1.54)
mratio = mwidth/mheight, // (2)
if (mratio > oratio) {
//original image is "higher" so take the maximum height
//and calculate the width accordingly
theight = mheight; //50
twidth = theight * oratio; //77
} else {
//original image is "wider" so take the maximum width
//and calculate the height accordingly
twidth = mwidth;
theight = twidth / oratio;
}
But any decent image processing library will have the functionality, that you can pass in the maximum dimensions and define to keep the aspect ratio and will do these calculations internally ...

Scalr resize and crop to size

I would like to show an image in all sort of placements with different width and height.
I am using a method for crop and resize with Sclar,
But I have 2 problems:
The result doesn't look so good in some cases. I think it is because the image in the code is first scaled.
I get an exception in other cases. For example:
Invalid crop bounds: x [32], y [-1], width [64] and height [64] must
all be >= 0
What is the best way of resizing a cropping and image to some target width and height?
Here is my current method:
public static BufferedImage resizeAndCropToCenter(BufferedImage image, int width, int height) {
image = Scalr.resize(image, Scalr.Method.QUALITY, Scalr.Mode.FIT_TO_WIDTH,
width * 2, height * 2, Scalr.OP_ANTIALIAS);
int x, y;
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
if (imageWidth > imageHeight) {
x = width / 2;
y = (imageHeight - height) / 2;
} else {
x = (imageWidth - width) / 2;
y = height / 2;
}
return Scalr.crop(image, x, y, width, height);
}
In the resize method, you are always doing FIT_TO_WIDTH no matter what the dimensions are. Perhaps you should do something different depending on whether the image and the desired size are both portrait or landscape format. What do you aim to achieve here?
Instead of
y = (imageHeight - height) / 2;
use
y = Math.abs((imageHeight - height) / 2);
to make sure y is never negative. Do the same for x in the else block.

How to animate rectangle to fixed width in steps with createjs?

I'm trying to create loading bar for my game. I create basic rectangle and added to the stage and caluclated size acording to the number of files so I get fixed width. Everything works, but for every step (frame) it creates another rectangle, how do I get only one object?
this is my code:
function test(file) {
r_width = 500;
r_height = 20;
ratio = r_width / manifest.length;
if (file == 1) {
new_r_width = 0
// Draw
r = new createjs.Shape();
r_x = (width / 2) - (r_width / 2);
r_y = (height / 2) - (r_height / 2);
new_r_width += ratio;
r.graphics.beginFill("#222").drawRect(r_x, r_y, new_r_width, r_height);
stage.addChild(r);
} else {
stage.clear();
new_r_width += ratio;
r.graphics.beginFill("#" + file * 100).drawRect(r_x, r_y + file * 20, new_r_width, r_height);
stage.addChild(r);
}
stage.update();
}
https://space-clicker-c9-zoranf.c9.io/loading/
If you want to redraw the rectangle, you will have to clear the graphics first, and then ensure the stage is updated. In your code it looks like you are clearing the stage, which is automatically handled by the stage.update() unless you manually turn off updateOnTick.
There are some other approaches too. If you just use a rectangle, you can set the scaleX of the shape. Draw your rectangle at 100% of the size you want it at, and then scale it based on the progress (0-1).
r.scaleX = 0.5; // 50%
A new way that is supported (only in the NEXT version of EaselJS, newer than 0.7.1 in GitHub), you can save off the drawRect command, and modify it.
var r = new createjs.Shape();
r.graphics.beginFill("red");
var rectCommand = r.graphics.drawRect(0,0,100,10).command; // returns the command
// Later
rectCommand.w = 50; // Modify the width of the rectangle
Hope that helps!

Need Algorithm for Tie Dye Pattern

I am looking for an algorithm or help developing one for creating a tie-dye pattern in a 2-dimensional canvas. I will be using HTML Canvas (via fabric.js) or SVG and JavaScript, but I'm open to examples in any 2D graphics package, like Processing.
I would draw concentric rings of different colors, and then go around radially and offset them. Here's some pseudo-code for drawing concentric rings:
const kRingWidth = 10;
const centerX = maxX / 2;
const centerY = maxY / 2;
for (y = 0; y < maxY; y++)
{
for (x = 0; x < maxX; x++)
{
// Get the color of a concentric ring - assume rings are 10 pixels wide
deltaX = x - centerX;
deltaY = y - centerY;
distance = sqrt (deltaX * deltaX + deltaY * deltaY);
whichRing = int(distance / kRingWidth);
setPixel(x, y, myColorTable [ whichRing ]); // set the pixel based on a color look-up table
}
}
Now, to get the offsets, you can perturb the distance based on the angle of (x, y) to the x axis. I'd generate a random noise table with, say 360 entries (one per degree - you could try more or fewer to see how it looks). So after calculating the distance, try something like this:
angle = atan2(y, x); // This is arctangent of y/x - be careful when x == 0
if (angle < 0) angle += 2.0 * PI; // Make it always positive
angle = int(angle * 180 / PI); // This converts from radians to degrees and to an integer
distance += noiseTable [ angle ]; // Every pixel at this angle will get offset by the same amount.

Rotating an Image in Silverlight without cropping

I am currently working on a simple Silverlight app that will allow people to upload an image, crop, resize and rotate it and then load it via a webservice to a CMS.
Cropping and resizing is done, however rotation is causing some problems. The image gets cropped and is off centre after the rotation.
WriteableBitmap wb = new WriteableBitmap(destWidth, destHeight);
RotateTransform rt = new RotateTransform();
rt.Angle = 90;
rt.CenterX = width/2;
rt.CenterY = height/2;
//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = width;
tempImage2.Height = height;
tempImage2.Source = rawImage;
wb.Render(tempImage2,rt);
wb.Invalidate();
rawImage = wb;
message.Text = "h:" + rawImage.PixelHeight.ToString();
message.Text += ":w:" + rawImage.PixelWidth.ToString();
//Finally set the Image back
MyImage.Source = wb;
MyImage.Width = destWidth;
MyImage.Height = destHeight;
The code above only needs to rotate by 90° at this time so I'm just setting destWidth and destHeight to the height and width of the original image.
It looks like your target image is the same size as your source image. If you want to rotate over 90 degrees, your width and height should be exchanged:
WriteableBitmap wb = new WriteableBitmap(destHeight, destWidth);
Also, if you rotate about the centre of the original image, part of it will end up outside the boundaries. You could either include some translation transforms, or simply rotate the image about a different point:
rt.CenterX = rt.CenterY = Math.Min(width / 2, height / 2);
Try it with a piece of rectangular paper to see why that makes sense.
Many thanks to those above.. they helped a lot. I include here a simple example which includes the additional transform necessary to move the rotated image back to the top left corner of the result.
int width = currentImage.PixelWidth;
int height = currentImage.PixelHeight;
int full = Math.Max(width, height);
Image tempImage2 = new Image();
tempImage2.Width = full;
tempImage2.Height = full;
tempImage2.Source = currentImage;
// New bitmap has swapped width/height
WriteableBitmap wb1 = new WriteableBitmap(height,width);
TransformGroup transformGroup = new TransformGroup();
// Rotate around centre
RotateTransform rotate = new RotateTransform();
rotate.Angle = 90;
rotate.CenterX = full/2;
rotate.CenterY = full/2;
transformGroup.Children.Add(rotate);
// and transform back to top left corner of new image
TranslateTransform translate = new TranslateTransform();
translate.X = -(full - height) / 2;
translate.Y = -(full - width) / 2;
transformGroup.Children.Add(translate);
wb1.Render(tempImage2, transformGroup);
wb1.Invalidate();
If the image isn't square you will get cropping.
I know this won't give you exactly the right result, you'll need to crop it afterwards, but it will create a bitmap big enough in each direction to take the rotated image.
//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = Math.Max(width, height);
tempImage2.Height = Math.Max(width, height);
tempImage2.Source = rawImage;
You need to calculate the scaling based on the rotation of the corners relative to the centre.
If the image is a square only one corner is needed, but for a rectangle you need to check 2 corners in order to see if a vertical or horizontal edge is overlapped. This check is a linear comparison of how much the rectangle's height and width are exceeded.
Click here for the working testbed app created for this answer (image below):
double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
The pseudo-code is as follows (actual C# code at the end):
Convert rotation angle into Radians
Calculate the "radius" from the rectangle centre to a corner
Convert BR corner position to polar coordinates
Convert BL corner position to polar coordinates
Apply the rotation to both polar coordinates
Convert the new positions back to Cartesian coordinates (ABS value)
Find the largest of the 2 horizontal positions
Find the largest of the 2 vertical positions
Calculate the delta change for horizontal size
Calculate the delta change for vertical size
Return width/2 / x if horizontal change is greater
Return height/2 / y if vertical change is greater
The result is a multiplier that will scale the image down to fit the original rectangle regardless of rotation.
**Note: While it is possible to do much of the maths using matrix operations, there are not enough calculations to warrant that. I also thought it would make a better example from first-principles.*
C# Code:
/// <summary>
/// Calculate the scaling required to fit a rectangle into a rotation of that same rectangle
/// </summary>
/// <param name="rotation">Rotation in degrees</param>
/// <param name="pixelWidth">Width in pixels</param>
/// <param name="pixelHeight">Height in pixels</param>
/// <returns>A scaling value between 1 and 0</returns>
/// <remarks>Released to the public domain 2011 - David Johnston (HiTech Magic Ltd)</remarks>
private double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
{
// Convert angle to radians for the math lib
double rotationRadians = rotation * PiDiv180;
// Centre is half the width and height
double width = pixelWidth / 2.0;
double height = pixelHeight / 2.0;
double radius = Math.Sqrt(width * width + height * height);
// Convert BR corner into polar coordinates
double angle = Math.Atan(height / width);
// Now create the matching BL corner in polar coordinates
double angle2 = Math.Atan(height / -width);
// Apply the rotation to the points
angle += rotationRadians;
angle2 += rotationRadians;
// Convert back to rectangular coordinate
double x = Math.Abs(radius * Math.Cos(angle));
double y = Math.Abs(radius * Math.Sin(angle));
double x2 = Math.Abs(radius * Math.Cos(angle2));
double y2 = Math.Abs(radius * Math.Sin(angle2));
// Find the largest extents in X & Y
x = Math.Max(x, x2);
y = Math.Max(y, y2);
// Find the largest change (pixel, not ratio)
double deltaX = x - width;
double deltaY = y - height;
// Return the ratio that will bring the largest change into the region
return (deltaX > deltaY) ? width / x : height / y;
}
Example of use:
private WriteableBitmap GenerateConstrainedBitmap(BitmapImage sourceImage, int pixelWidth, int pixelHeight, double rotation)
{
double scale = CalculateConstraintScale(rotation, pixelWidth, pixelHeight);
// Create a transform to render the image rotated and scaled
var transform = new TransformGroup();
var rt = new RotateTransform()
{
Angle = rotation,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(rt);
var st = new ScaleTransform()
{
ScaleX = scale,
ScaleY = scale,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(st);
// Resize to specified target size
var tempImage = new Image()
{
Stretch = Stretch.Fill,
Width = pixelWidth,
Height = pixelHeight,
Source = sourceImage,
};
tempImage.UpdateLayout();
// Render to a writeable bitmap
var writeableBitmap = new WriteableBitmap(pixelWidth, pixelHeight);
writeableBitmap.Render(tempImage, transform);
writeableBitmap.Invalidate();
return writeableBitmap;
}
I released a Test-bed of the code on my website so you can try it for real - click to try it
P.S. Yes this is my answer from another question, duplicated exactly, but the question does require the same answer as that one to be complete.

Resources