Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rotation adds black border #721

Closed
tonysamperi opened this issue Apr 12, 2019 · 23 comments
Closed

Rotation adds black border #721

tonysamperi opened this issue Apr 12, 2019 · 23 comments
Labels
bug there is a bug in the way jimp behaves

Comments

@tonysamperi
Copy link

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

@hipstersmoothie hipstersmoothie added the bug there is a bug in the way jimp behaves label Apr 27, 2019
@tooleks
Copy link

tooleks commented Jun 21, 2019

I faced the same problem.

Here is the jsfiddle to reproduce the issue.

@tonysamperi
Copy link
Author

I faced the same problem.

Here is the jsfiddle to reproduce the issue.

LOL the for loop!!! 😂

@tooleks
Copy link

tooleks commented Jun 26, 2019

@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.

@tonysamperi
Copy link
Author

@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! 😉

@soimy
Copy link
Contributor

soimy commented Jul 9, 2019

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

@ianloubser
Copy link

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() 🙈

@hipstersmoothie
Copy link
Collaborator

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

@ghost
Copy link

ghost commented May 24, 2020

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

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

@cladveloper
Copy link

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

@skalee
Copy link
Contributor

skalee commented Jul 14, 2020

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.

@mathias22osterhagen22
Copy link

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

My picture after an image.rotate(90);

image

@mathias22osterhagen22
Copy link

mathias22osterhagen22 commented Nov 25, 2020

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)

@Eriickson
Copy link

@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

@mathias22osterhagen22
Copy link

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

@stevezac-osu
Copy link
Contributor

@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;

@Valink
Copy link

Valink commented Apr 22, 2022

Still encounter this issue, any update on it ?
image

@Mizumaki
Copy link

Thanks to @mathias22osterhagen22, works well on the degree which is multiple of 90.

Here is the TypeScript code
(Sadly, Jimp doesn't export Bitmap type so I had to use any for it...

import type Jimp from 'jimp';

/**
 * Util to handle Jimp rotate bug
 * https://github.com/oliver-moran/jimp/issues/721#issuecomment-733804760
 */
export const simpleRotate = (deg: number /* TODO: Restrict only 0,90,180,270 */, image: Jimp) => {
  // Restore simple rotate of jimp v0.5.6
  // https://github.com/oliver-moran/jimp/issues/821

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

  let steps = (deg / 90) % 4;
  if (steps < 0) steps += 4;

  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);
      dstBuffer.writeUInt32BE(tmp, len - srcOffset - 4);
    }
  } 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;

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  function rotate90degrees(bitmap: any, dstBuffer: Buffer, clockwise: boolean) {
    const dstOffsetStep = clockwise ? 4 : -4;
    let dstOffset = clockwise ? 0 : dstBuffer.length - 4;

    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) as number;
        dstBuffer.writeUInt32BE(tmp, dstOffset);
        dstOffset += dstOffsetStep;
      }
    }
  }
};

@ghost
Copy link

ghost commented Sep 28, 2022

Is there any fix for this. I'm getting this when I rotate -90

@aquilatrindade
Copy link

Hey everyone, i had the same problem, i'dnt know if this are the correct solution but i solved it replacing the "modular if" with the original image sizes:

REPLACE THIS
if (w % 2 !== 0) { w++; }
if (h % 2 !== 0) { h++; }

FOR THIS
if (w % 2 !== 0) { w = this.bitmap.height; }
if (h % 2 !== 0) { h = this.bitmap.width; }

in the file: node_modules@jimp\plugin-rotate\dist\index.js

I hope this can help you

@tonysamperi
Copy link
Author

Someone should really fork this lib and start merging all the PRs...
I have a few libs of my own already...

@hipstersmoothie
Copy link
Collaborator

hey @tonysamperi unfortunately it's just me manning the ship of here! other than a brief stint around 4 years ago I haven't been a big user of jimp myself so it fell by the wayside as I went through my career.

I'm happy to facilitate contribution. If you have changes you want to make I'm all for getting them in (this is to anyone). Jimp is a community led effect and the community is me 😓

@aquilatrindade would you like to submit a pr to fix those lines?

@tonysamperi
Copy link
Author

hi @hipstersmoothie actually as I said I have a few libraries of my own to maintain already!
Plus I only used this library for 1 mocked service 4 years ago. Never had to work with images in Node luckily for me...
I didn't really need the bug resolved, just thought that reporting might help others.
I'm closing this since I've been receiving notifications for the past 4 years...
Cheers everybody.

@hipstersmoothie
Copy link
Collaborator

Good call 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug there is a bug in the way jimp behaves
Projects
None yet
Development

No branches or pull requests