Created | ![]() |
Favourites | Opened | Upvotes | Comments |
9. Jan 2020 | 3 | 0 | 344 | 0 | 0 |
This tutorial will show how to rotate images in both Javascript and in ASP.NET Core C#. The focus is on images that originally are wrongly rotated and you need to rotate the image either left or right or upside down.
Index :
There are several ways to rotate an image in Javascript, here I will show 2 methods.
Let's first setup a base html page with 2 buttons (1 for transform rotate and 1 for canvas rotate) and 1 image (the image to rotate) :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Rotating images in Javascript</title>
</head>
<body>
<div style="width:600px;height:600px;margin:50px auto;">
<button onclick="rotateUsingTransform()">Rotate using transform</button>
<button onclick="rotateUsingCanvas('right')">Rotate using canvas</button>
<div style="width:250px;height:300px;border:solid 1px blue;margin-top:20px;">
<img id="myImage" src="skateboard_esben.jpg" style="max-width:100%;max-height:100%;"/>
</div>
</div>
</body>
</html>
"The transform CSS property lets you rotate, scale, skew or translate an element" and works on most DOM elements "whose layout is governed by the CSS box model" not only images, however here we use it to rotate an image.
First let's hook up the first button "Rotate using transform" with the Javascript function that rotates the image :
function rotateUsingTransform() {
var img = document.querySelector('#myImage');
img.style.transform = "rotate(90deg)";
}
Then the "Rotate using transform" button is clicked, the above code uses Javascript to set the transform CSS property on the image to "rotate(90deg)", which will rotate the image around it's center like this :
It looks like the normal flow is unaffected by the transformation still flowing as if the image is filling up it's original space - it seems similar to a relative positioned element : we see the transformed image different from the space it occupies in the normal flow. At the same token, the image we see does NOT respect it's boundaries and the get the above abomination.
Rotating an image using transform have the following problems :
While the transform CSS property have many uses and is quite fun to play with, it does not work very well for rotating images that accidentally have a wrong rotation. Below I explore a better version : rotating images using canvas.
A more involved but better method is to paint the image on a canvas, rotate the image on the canvas, get the base64 encoding of the rotated image bytes and then set the src attribute of the original image to that base64 encoding - this will reset the original image and therefore reset the normal flow either changing the position of normal flow elements OR allow the rotated image to fit into it's container (whatever the goal)
First let's hook up the second button "Rotate using canvas" with the Javascript function that rotates the image :
function rotateUsingCanvas(direction) {
var canvas = document.createElement('canvas');
var img = document.querySelector('#myImage');
var imgWidth = img.naturalWidth;
var imgHeight = img.naturalHeight;
canvas.height = imgWidth;
canvas.width = imgHeight;
var ctx = canvas.getContext("2d");
if (direction == 'left') {
ctx.translate(0, imgWidth); // as canvas context rotation rotates along the (0, 0) coordinate, the image will disappear if we do not move the image imgwidth along the y-axis
ctx.rotate(-90 * Math.PI / 180); // canvas context rotation is measured in radians
}
else { // direction == 'right'
ctx.translate(imgHeight, 0); // as canvas context rotation rotates along the (0, 0) coordinate, the image will disappear if we do not move the image imgHeight along the x-axis
ctx.rotate(90 * Math.PI / 180);
}
ctx.drawImage(img, 0, 0);
var base64Uri = canvas.toDataURL('image/png'); // this is the base64 encoding including 'data:image; base64, '
var base64 = base64Uri.replace(/^data:image.+;base64,/, ''); // this is the real base64 encoding, however we don't use this
img.src = base64Uri;
};
Then the "Rotate using canvas" button is clicked, the above code uses Javascript to create a canvas object, draw the image to rotate on that canvas, rotate the image, get the base64 encoding of the image bytes and then setting the img.src attribute to that base64 encoding :
In the canvas based solution we have managed to rotate the image in a way so it respects it's boundaries and in addition the above solution will redraw the normal flow - the image will display the way it participates in normal flow.
Ok, so ad-hoc rotating an image in Javascript is not difficult, however often there will be a need to save the image in it's new rotation - this could be done just sending the base64 to the server and save, however below I will show how to do the actual rotation directly on the server.
In addition to the actual code doing the image rotation, I will setup an AJAX pipeline from the browser on which an image is requested to be rotated to the server to do the actual rotation and sending the rotated image back to the browser.
First let's set up the Javascript that will send the request for image rotation to the server and receive the image back as a base64 encoding :
function saveRotation(imageName, rotation) {
$.ajax({
url: '/Image/Rotate?imageName=' + encodeURIComponent(imageName)+ "&rotation=" + rotation,
method: 'GET',
dataType: 'json',
success: function (apiResult) { // let's call the returned object for apiResult
if (apiResult.error) {
alert("server error : " + apiResult.error);
}
else {
// the base64 encoding coming back from the server
var rotatedImageAsBase64 = apiResult.data.rotatedImageAsBase64;
// to use a base64 byte encoding as a source for an html image tag, the base64 encoding must be prefixed with a string specifying that it should be understood as a data URI of type base64
var imageSrcBase64 = "data:image/png;base64, " + rotatedImageAsBase64;
// if you want, you can now set the source of an image element
(document.querySelector('#imageElementId')).src = imageSrcBase64;
}
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
alert("client error : " + errorThrown);
}
});
}
Next we need to write the Controller that contains the HTTP endpont on the server that will handle the request for image rotation - I will build the Controller in 5 parts :
First the ImageSharp (and other) libraries. The 3. party ImageSharp library is generally recommended for image manipulations in ASP.NET Core (and you need to add it to your project dependencies eg. via the NuGet Manager) :
using System; // Exception
using Microsoft.AspNetCore.Hosting; // IHostingEnvironment
using Microsoft.AspNetCore.Mvc; // Controller
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
namespace YourProjectName
{
public class ImageController : Controller
{
... HostingEnvironment injection ...
... Rotate function : HTTP endpoint ...
... GetImageFolder function ...
... RotateImage function ...
}
}
Second is the HostingEnvironment injection. The ASP.NET Core framework automatically adds HostingEnvironment to the DiC (Dependency Injection Container), so we don't need to do it ourselves and can just inject it anywhere we want, here into the ImageController :
... using statements ...
namespace YourProjectName
{
public class ImageController : Controller
{
private IHostingEnvironment _hostingEnvironment; // we need the HostingEnvironment to map paths
public ImageController(IHostingEnvironment hostingEnvironment) // HostingEnvironment is automatically available in DiC so can be injected anywhere
{
_hostingEnvironment = hostingEnvironment;
}
... Rotate function : HTTP endpoint ...
... GetImageFolder function ...
... RotateImage function ...
}
}
Third is Rotate(...) the HTTP endpoint for the above AJAX request (/Image/Rotate?...) :
... using statements ...
namespace YourProjectName
{
public class ImageController : Controller
{
... HostingEnvironment injection ...
[HttpGet]
public JsonResult Rotate(string imageName, int rotation)
{
string error = "";
string validation = "";
string rotatedImageAsBase64 = "";
try
{
string imagePath = GetImagePath(imageName);
if (System.IO.File.Exists(imagePath))
{
rotatedImageAsBase64 = RotateImage(imagePath, rotation);
}
}
catch(Exception ex)
{
error = ex.Message;
}
var apiResult = new
{
error,
data = new { rotatedImageAsBase64 };
}
return Json(apiResult);
}
... GetImageFolder function ...
... RotateImage function ...
}
}
Fourth is GetImagePath(...) a helper function to map the folder where we keep the image to rotate :
... using statements ...
namespace YourProjectName
{
public class ImageController : Controller
{
... HostingEnvironment injection ...
... Rotate function : HTTP endpoint ...
public string GetImagePath(string imageName)
{
// 1 get path path to the root of your ASP.NET Core project, eg. on my dev machine it is D:\Websites\_webmodelling\topiqs\topiqs
var contentRootPath = _hostingEnvironment.ContentRootPath; // here we use the injected HostingEnvironment
// 2 navigate from the project content root to the location of the image to rotate, eg. in my case it is on /FileServer/ContentImages/ in the parent of the project content root
var contentRootParentPath = contentRootPath.substring(0, contentRootPath.LastIndexOf("YourProjectName", StringComparision.OrdinalIgnoreCase);
var imageFolderPath = Path.Combine(contentRootParentPath, "/FileServer/ContentImages/");
// 3 return the image path
return imageFolderPath + imageName;
}
... RotateImage function ...
}
}
Fifth and last is RotateImage(...) the actual rotation code, this is there we use the ImageSharp library referenced above :
... using statements ...
namespace YourProjectName
{
public class ImageController : Controller
{
... HostingEnvironment injection ...
... Rotate function : HTTP endpoint ...
... GetImageFolder function ...
public static string RotateImage(string imagePath, int rotation)
{
string imageAsBase64 = "";
var rotateMode = RotateMode.None; // set the RotationMode enum (there are modes apart from None : Rotate90, Rotate180 & Rotate270)
if (rotation == 90 || rotation == -270) // rotating 90 degrees right or 270 degrees left is the same thing
{
rotateMode = RotateMode.Rotate90;
}
else if (rotation == 180 || rotation == -180)
{
rotateMode = RotateMode.Rotate180;
}
else if (rotation == 270 || rotation == -90)
{
rotateMode = RotateMode.Rotate270;
}
using (Image<Rgba32> image = Image.Load(imagePath))
{
image.Mutate(ctx => ctx.Rotate(rotateMode)); // rotate the image
image.Save(imagePath, new PngEncoder()); // save the rotated image (we can only save as Png)
// apart from saving the rotated image, I also want this function to return a base64 encoding of the rotated image
using (var ms = new MemoryStream())
{
image.Save(ms, new PngEncoder());
byte[] imageBytes = ms.ToArray();
imageAsBase64 = Convert.ToBase64String(imageBytes);
}
}
return imageAsBase64;
}
}
}
Ok, that's all - you should now be able to rotate images in both the browser and on the server.
Please leave a comment.