An open API service indexing awesome lists of open source software.

https://github.com/ytiurin/downscale

Better image downscale with canvas.
https://github.com/ytiurin/downscale

canvas crop downsample downscale fast image javascript jpeg linear performance photo pica png processing resample resize scale sharp thumbnails upload

Last synced: 9 months ago
JSON representation

Better image downscale with canvas.

Awesome Lists containing this project

README

          

Better image downscale with canvas ([demo](https://ytiurin.github.io/downscale/demo/multiselect.html "Quick demo"))
===================================
This function downscales images in the browser, producing a better quality result, than the traditional [`CanvasRenderingContext2D.scale()`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/scale "The CanvasRenderingContext2D.scale() method of the Canvas 2D API adds a scaling transformation to the canvas units by x horizontally and by y vertically.") method. It neutralises the "fuzzy" look caused by the native canvas downsampling, when processing relatively large images like photos taken with a smartphone. Check the [demo page](https://ytiurin.github.io/downscale/demo/multiselect.html "Quick demo").

![Better image downscale demo](https://github.com/ytiurin/downscale/raw/master/public/demo.jpg)

Motivation
----------
While other image resizing libraries are based on complex interpolation algorithms such as [Lanczos resampling](https://en.wikipedia.org/wiki/Lanczos_resampling "Lanczos resampling and Lanczos filtering are two applications of a mathematical formula. It can be used as a low-pass filter or used to smoothly interpolate the value of a digital signal between its samples."), image downscaling usually doesn't require that complexity, because there is no interpolation happening (in other words we don't create new pixels).

On the other hand, browsers implement very fast [`HTMLCanvasElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement "The HTMLCanvasElement interface provides properties and methods for manipulating the layout and presentation of canvas elements.") downsampling, when the pixel from source position is directly transfered to the destination position, loosing all the neighbouring pixels information. The resulting image may often look very noisy.

To resolve this problem, the proposed function does a simple area-average downsampling, producing preferable results with relatively small processing time.

Performance
-----------
This function uses the technique, proposed by [Paul Rouget](http://paulrouget.com/) in his [article](https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/ "Faster Canvas Pixel Manipulation with Typed Arrays") about pixel manipulation with [Typed Arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays "JavaScript typed arrays are array-like objects and provide a mechanism for accessing raw binary data."). His method reduces the number of read/write operations to the [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer "The ArrayBuffer object is used to represent a generic, fixed-length raw binary data buffer. You cannot directly manipulate the contents of an ArrayBuffer; instead, you create one of the typed array objects or a DataView object which represents the buffer in a specific format, and use that to read and write the contents of the buffer.") of the [`ImageData`](https://developer.mozilla.org/en-US/docs/Web/API/ImageData "The ImageData interface represents the underlying pixel data of an area of a element. It is created using the ImageData() constructor or creator methods on the CanvasRenderingContext2D object associated with a canvas: createImageData() and getImageData(). It can also be used to set a part of the canvas by using putImageData().") returned by the [`CanvasRenderingContext2D.getImageData()`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData "The CanvasRenderingContext2D.getImageData() method of the Canvas 2D API returns an ImageData object representing the underlying pixel data for the area of the canvas denoted by the rectangle which starts at (sx, sy) and has an sw width and sh height. This method is not affected by the canvas transformation matrix.") method. This saves overall processing time when you want to iterate through every pixel of the source image.

Also, the usage of [`Math.round()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round "The Math.round() function returns the value of a number rounded to the nearest integer.") method is avoided in favour of [Bitwise operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators "Bitwise operators treat their operands as a sequence of 32 bits (zeroes and ones), rather than as decimal, hexadecimal, or octal numbers. For example, the decimal number nine has a binary representation of 1001. Bitwise operators perform their operations on such binary representations, but they return standard JavaScript numerical values."), giving a significant boost in performance in some browsers.

Image cropping
--------------
Image cropping is very often used in pair with resizing, but both can be very naturally combined. As we don't need to iterate through pixels in cropped areas, the function does both downscaling and cropping in range of the same processing loop. This saves some memory and processing time.

By default, the source image is cropped in the way, that the center of the source image is transfered to the resulting image.

Rollback to `canvas` resizing
--------
The function also uses basic `canvas` resizing method when the scale factor of the resulting image is greater than 0.5x. So the better downscaling happen only when the resulting image is at least 2 times smaller than the initial image. In other cases basic `canvas` resizing gives better image quality result.

Install
-------
```
npm install downscale
```

Syntax
------
```javascript
Promise downscale(source, width, height[, options]);
```

### Parameters


source


Defines the source of the image data to downscale. This can either be:

width

A Number indicating width of the resulting image. If the value is 0, the width is adapted to keep the same aspect ratio as in the source image.

height

A Number indicating height of the resulting image. If the value is 0, the height is adapted to keep the same aspect ratio as in the source image.

options (optional)


An object with properties representing optional function parameters:



  • imageType

    A DOMString indicating image format. Possible values are jpeg, png, webp. The default format type is jpeg.





  • quality

    A Number between 0 and 1 indicating image quality if the requested imageType is jpeg or webp. The default value is 0.85.





  • returnBlob

    A Boolean indicating if the returned Promise should resolve with Blob object representing the resulting image. The default value is false.





  • returnCanvas

    A Boolean indicating if the returned Promise should resolve with HTMLCanvasElement containing the resulting image. The default value is false.





  • sourceX

    A Number indicating distance from the left side of the source image to draw into the destination context. This allows to crop the source image from the left side. The default value is calculated to centralize the destination rectangle relatively to the source canvas.





  • sourceY

    A Number indicating distance from the top side of the source image to draw into the destination context. This allows to crop the source image from the top side. The default value is calculated to centralize the destination rectangle relatively to the source canvas.





### Return value
A [`Promise`](https://developer.mozilla.org/en-US/docs/Web/API/Promise "The Promise interface represents a proxy for a value not necessarily known at its creation time. It allows you to associate handlers to an asynchronous action's eventual success or failure. This lets asynchronous methods return values like synchronous methods: instead of the final value, the asynchronous method returns a promise of having a value at some point in the future.") that resolves to a [`DOMString`](https://developer.mozilla.org/en-US/docs/Web/API/DOMString "DOMString is a UTF-16 String. As JavaScript already uses such strings, DOMString is mapped directly to a String.") containing the resulting image in [data URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs "URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents.") format.

Examples
--------
### Send image data with ``
This is just a simple code snippet which uses the form file input as a source of the image data.
#### HTML
```html

```
#### Javascript
```javascript
function filesChanged(files)
{
for (var i = 0; i < files.length; i++) {
downscale(files[i], 400, 400).
then(function(dataURL) {
var destInput = document.createElement("input");
destInput.type = "hidden";
destInput.name = "image[]";
destInput.value = dataURL;
// Append image to form as hidden input
document.forms[0].appendChild(destInput);
// Preview image
var destImg = document.createElement("img");
destImg.src = dataURL;
document.body.appendChild(destImg);
})
}
}
```

### Send image data with `FormData`
You can use even cleaner [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData "The FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to \"multipart/form-data\".") interface to send pure `blob` data to the server.
#### HTML
```html

Submit form data


```
#### Javascript
```javascript
var formData = new FormData();
var URL = window.URL || window.webkitURL;

function filesChanged(files)
{
for (let i = 0; i < files.length; i++) {
downscale(files[i], 400, 400, {returnBlob: 1}).
then(function(blob) {
// Append image to form as a blob data
formData.append("userpic[]", blob, files[i].name);
// Preview image
var destImg = document.createElement("img");
destImg.src = URL.createObjectURL(blob);
document.body.appendChild(destImg);
})
}
}

function submitForm()
{
var request = new XMLHttpRequest();
request.open("POST", "http://foo.com/submitform.php");
request.send(formData);
}
```

### Resize `` element
Processing an `` element is quite simple. The function will wait for image load, so you don't have to worry about it.
#### HTML
```html

```
#### Javascript
```javascript
var sourceImg = document.getElementById('source');

downscale(sourceImg, 400, 400).
then(function(dataURL) {
var destImg = document.createElement('img');
destImg.src = dataURL;
document.body.appendChild(destImg);
})
```

### Load image from URL
The function can upload the source image from the given URL with no extra code needed. Keep in mind that the image should share [origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin "The Origin request header indicates where a fetch originates from. It doesn't include any path information, but only the server name. It is sent with CORS requests, as well as with POST requests. It is similar to the Referer header, but, unlike this header, it doesn't disclose the whole path.") with the code file.
```javascript
var imageURL = "/public/1.jpg";

downscale(imageURL, 400, 400).
then(function(dataURL) {
var destImg = document.createElement('img');
destImg.src = dataURL;
document.body.appendChild(destImg);
})
```

Other libraries
---------------
Check out other great in-browser image resizing libraries:
- [pica](https://github.com/nodeca/pica "Resize image in browser with high quality and high speed.") is great image resizing tool with support of [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API "Web Workers makes it possible to run a script operation in background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.") and [WebAssembly](https://developer.mozilla.org/en-US/docs/WebAssembly "WebAssembly is a new type of code that can be run in modern web browsers — it is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages such as C/C++ with a compilation target so that they can run on the web. It is also designed to run alongside JavaScript, allowing both to work together.") from the box
- [Hermite-resize](https://github.com/viliusle/Hermite-resize "Canvas image resize/resample using Hermite filter with JavaScript.") does image resize/resample using Hermite filter and [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API "Web Workers makes it possible to run a script operation in background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.")

License
-------
MIT