Jimp: Rotation adds black border

Created on 12 Apr 2019  路  15Comments  路  Source: oliver-moran/jimp

Expected Behavior

When the image is rotated, the image is identical to the original

Current Behavior

A black border (1px) is added

Failure Information (for bugs)

Steps to Reproduce

(typescript)

Jimp.read(this._encodedImgDataUrl).then(jimpContext => {
            jimpContext.rotate(degrees, false).getBase64(this._mime, (_instance_, result: string) => {
                this._globalRender(result);
            });
        });

IF YOUR ISSUE DEPENDS ON A PARTICULAR IMAGE BE SURE TO INCLUDE THIS AS WELL. WE CAN'T REPORDUCE IF WE DON'T HAVE YOUR IMAGE

  1. step 1
  2. step 2
  3. you get it... -->

Screenshots

Context

  • Jimp Version: 0.6.1
  • Operating System: Win 10
  • Node version: 8.15.0

Failure Logs

bug

Most helpful comment

After some digging, I found this issue is introduced by #708
most of the time we just need simple 90-degree rotation, and advancedRotate() on 90/270 degree will result in total size change.

Workaround: implement your own simpleRotate() method using https://github.com/oliver-moran/jimp/blob/736054ef1bea6b6fb03db3e75f05cd922a9c104f/packages/plugin-rotate/src/index.js#L3-L20

All 15 comments

I faced the same problem.

Here is the jsfiddle to reproduce the issue.

I faced the same problem.

Here is the jsfiddle to reproduce the issue.

LOL the for loop!!! 馃槀

@tonysamperi
Well, it's pretty much how I found the issue. :)
I have a simple photo editor and after multiple 90 degrees rotations, I get a similar result.

@tooleks
For me it was trickier, because I used it as backend (NodeJS) rotation and I started rotating always from the original image.
I found the issue, because I had to validate the upper part of the picture: the background had to be a light color (above rgb(150, 150, 150) )...
The control failed after rotating, so I zoomed the image and saw the black line! 馃槈

After some digging, I found this issue is introduced by #708
most of the time we just need simple 90-degree rotation, and advancedRotate() on 90/270 degree will result in total size change.

Workaround: implement your own simpleRotate() method using https://github.com/oliver-moran/jimp/blob/736054ef1bea6b6fb03db3e75f05cd922a9c104f/packages/plugin-rotate/src/index.js#L3-L20

Is the above merged PR supposed to resolve the black border?

I see the PR says "in scope of EXIF rotation", but I'm testing on 0.12.4 and it still shows up. If I only want to do EXIF rotation (90, 180 or 270deg) should I be calling a different rotation function than rotate ?

Not quite sure how I can resolve this without forking the repo and using above suggested simpleRotate() 馃檲

That could just be another function that the rotate plugin adds. I'd be open to a PR if someone wants to make it

I had included it (rotateSimple) in my plugin collection for Jimp, already some months ago

https://www.npmjs.com/package/@ozelot379/jimp-plugins

I had included it (rotateSimple) in my plugin collection for Jimp, already some months ago

https://www.npmjs.com/package/@ozelot379/jimp-plugins

It's a great project but I cant to introduces in my project because I don't have support for ES Modules:c

Is the above merged PR supposed to resolve the black border?

No. Thanks to #875, EXIF autorotation stops using rotate(), hence is no longer affected by this bug.

I see the PR says "in scope of EXIF rotation", but I'm testing on 0.12.4 and it still shows up. If I only want to do EXIF rotation (90, 180 or 270deg) should I be calling a different rotation function than rotate ?

Jimp reads EXIF metadata on file open and autorotates pictures accordingly.

This problem still occurs with the last version and using node.js:

My picture after an image.rotate(90);

image

For the one that doesn't want to add plugins or whatever ES module management Thanks me later

"patch-jimp-rotate.js"

module.exports = (deg, image) => {
    // Restore simple rotate of jimp v0.5.6
    // https://github.com/oliver-moran/jimp/issues/821

    if (deg % 90 !== 0) {
        return image;
    }

    let steps = Math.round(deg / 90) % 4;
    steps += steps < 0 ? 4 : 0;

    if (steps === 0) {
        return image;
    }

    const srcBuffer = image.bitmap.data;
    const len = srcBuffer.length;
    const dstBuffer = Buffer.allocUnsafe(len);

    let tmp;

    if (steps === 2) {
        // Upside-down
        for (let srcOffset = 0; srcOffset < len; srcOffset += 4) {
            tmp = srcBuffer.readUInt32BE(srcOffset, true);
            dstBuffer.writeUInt32BE(tmp, len - srcOffset - 4, true);
        }
    } else {
        // Clockwise or counter-clockwise rotation by 90 degree
        rotate90degrees(image.bitmap, dstBuffer, steps === 1);

        tmp = image.bitmap.width;
        image.bitmap.width = image.bitmap.height;
        image.bitmap.height = tmp;
    }

    image.bitmap.data = dstBuffer;

    return image;

    function rotate90degrees(bitmap, dstBuffer, clockwise) {
        const dstOffsetStep = clockwise ? -4 : 4;
        let dstOffset = clockwise ? dstBuffer.length - 4 : 0;

        let tmp;
        let x;
        let y;
        let srcOffset;

        for (x = 0; x < bitmap.width; x++) {
            for (y = bitmap.height - 1; y >= 0; y--) {
                srcOffset = (bitmap.width * y + x) << 2;
                tmp = bitmap.data.readUInt32BE(srcOffset, true);
                dstBuffer.writeUInt32BE(tmp, dstOffset, true);
                dstOffset += dstOffsetStep;
            }
        }
    }
};

"in your code"

//image.rotate(90);
image = require('./patch-jimp-rotate')(90, image); //this can be more sexy, you can declare it above

@cladveloper (FYI)

@mathias22osterhagen22 I am going through the same situation, but in my case it is when I crop the image with .crop (), did you find a solution for this problem? please let me know, thanks

Image Original
image

Image cropped
image

@Eriickson yes sure. I passed on this library instead: https://www.npmjs.com/package/sharp

@jimp/plugin-rotate/dist/index.js:29-30 have "+1" on their resizing height/width that are causing this issue for the rotate

w = Math.ceil(Math.abs(this.bitmap.width * cosine) + Math.abs(this.bitmap.height * sine)) + 1;
h = Math.ceil(Math.abs(this.bitmap.width * sine) + Math.abs(this.bitmap.height * cosine)) + 1;
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dtrofimov picture dtrofimov  路  5Comments

molipha picture molipha  路  6Comments

fatihturgut picture fatihturgut  路  4Comments

sesirimarco picture sesirimarco  路  3Comments

chancein007 picture chancein007  路  5Comments