Sharp: Enhancement: create blank/empty image of given dimensions/channels

Created on 20 Jun 2016  路  16Comments  路  Source: lovell/sharp

Is it possible to create an empty canvas image with Sharp and process it through a stream or do I need to load a canvas image file from disk?

enhancement

Most helpful comment

You could use a raw pixel input buffer for this, something like this (ES2015) example:

const width = 200;
const height = 100;
const channels = 4;
const rgbaPixel = 0x00000000;

const canvas = Buffer.alloc(width * height * channels, rgbaPixel);

sharp(canvas, { raw : { width, height, channels } })...

All 16 comments

Are you able to provide more information about the problem you're trying to solve? What do you mean by "canvas" in this context?

The lwip node module has a method called "create" (https://github.com/EyalAr/lwip#create-a-new-image) that creates an empty image. I was wondering if it is possible to accomplish the same thing in sharp, or if I always have to start off from an existing image file.

You could use a raw pixel input buffer for this, something like this (ES2015) example:

const width = 200;
const height = 100;
const channels = 4;
const rgbaPixel = 0x00000000;

const canvas = Buffer.alloc(width * height * channels, rgbaPixel);

sharp(canvas, { raw : { width, height, channels } })...

@pjarts Were you able to make any progress with this?

Would a little helper function ("create(...)"?) in sharp make sense,
like one that uses the canvas code above?

@lovell, sorry for the late reply. Yes your code seems to do what I was looking for, although I agree with @strarsis that a helper function would be nicer.

@pjarts Thanks for confirming.

Let's leave this issue open to track adding such a helper - I'll need to have a think about what the API might look like.

  1. A kind of factory/'static' method:

    sharp.empty()
    
  2. With configuration object passed:

    sharp.create/empty/new({ width: 1024, height: 768 });
    
  3. Like 2. but directly with 'constructor' method:

    sharp({ width: 1024, height: 768 });
    
  4. With no arguments passed at all - using resize to achieve the desired dimensions:

    sharp().resize(1024, 768);
    
  5. Like 3. but using an empty dummy image from constant (not very elegant imho
    because of dummy image and even artifacts that may be caused):

    sharp(sharp.EMPTY_IMAGE).resize(1024, 768);
    

The forthcoming v0.16.0 release moves all image input logic to a common location, namely the _createInputDescriptor function in JavaScript then CreateInputDescriptor method in C++.

This is the best place to add such a "create image" helper, so any part of the API that can handle input images will be able to use it.

Here's how the API could work:

sharp(null, {
  create: {
    width: 1024,
    height: 768,
    channels: 3,
    background: rgb
  }
})...
sharp('input.jpg')
  .overlayWith(null, create: {
    width: 16,
    height: 32,
    channels: 4,
    background: rgba
  })...

where rgb and rgba are parsed by the color module, the same approach used by the existing background function.

[email protected] doesn't seem to support it yet.

@strarsis That's correct. My comment about v0.16.0 was that it provided the ground work for this feature request.

Is this feature gonna be available in a next release ?

@5im0n I'm happy to help with a PR if you're able.

Commit 1aa053c adds the ability to create a blank image using the API described by comment https://github.com/lovell/sharp/issues/470#issuecomment-239620622 above. This will be available in v0.17.3.

v0.17.3 now available, thanks for all the suggestions.

To combine multiple images, I did this instead of the empty canvas:

    for (let i = 1, left = singleWidth; i < imgCount; i++, left += singleWidth) {
        // only one overlay per pipeline
        if (i > 1) {combined = sharp(await combined.toBuffer())}
        combined.overlayWith(imgBufs[i], {left, top: 0})
    }

so basically forcing sharp to process the pipeline once there is more than one overlay. Async/await makes this nice, with promises this code would be a bit harder.

(incidentally, I wonder if there would be a use for a .perform() call that runs the pipeline and starts a new one?)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sansroman picture sansroman  路  3Comments

jaydenseric picture jaydenseric  路  3Comments

paulieo10 picture paulieo10  路  3Comments

henbenla picture henbenla  路  3Comments

Andresmag picture Andresmag  路  3Comments