2

Is it possible to skew or distort an Image object in Java? I 'pull' one side of an image out, making it seem closer to me. (LIke 3D).

Any suggestions?

Hidde
  • 11,493
  • 8
  • 43
  • 68
  • possible duplicate of [Java image transformation](http://stackoverflow.com/questions/3202323/java-image-transformation) – trashgod Sep 09 '11 at 19:52

2 Answers2

3

Yes. Lots of ways but I would start with the Advanced Imaging API. It provides a ton of advanced imaging functionality.

But just to do the type of transform that you're talking about you might just need an Affine Transform. Sample results here for the previous link.

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Paul Sasik
  • 79,492
  • 20
  • 149
  • 189
  • Good advice on JAI, but I don't think `AffineTransform` alone is sufficient, as suggested [here](http://stackoverflow.com/questions/3202323/java-image-transformation/3202687#3202687). – trashgod Sep 09 '11 at 19:51
  • I'm not certain that it's the right transform either. That's why I added the image searrch result, for corroboration. – Paul Sasik Sep 09 '11 at 20:27
  • 1
    Maybe [`PerspectiveTransform`](http://download.oracle.com/docs/cd/E17802_01/products/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/PerspectiveTransform.html)? – trashgod Sep 09 '11 at 20:56
  • Trashgod, PerspectiveTransform is what I needed. I need perspective and the straightness of the lines. Thanks. – Hidde Sep 13 '11 at 06:11
1

You can also do this with JavaFX.

The following example uses PerspectiveTransform and a bit of rotation on the BufferedImage.

It turns this image

Stack Overflow logo

into this

Stack Overflow logo distorted

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;

/**
* Distorts images using transformations.
* <p>
* Created by Matthias Braun on 2018-09-05.
*/
public class Distortion {

    public static void main(String... args) throws IOException {
        URL imgUrl = new URL("https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a");
        BufferedImage img = ImageIO.read(imgUrl);
        BufferedImage distorted = distortImg(img);

        File newImgFile = new File(System.getenv("HOME") + "/distorted.png");
        System.out.println("Saving to: " + newImgFile);
        ImageIO.write(distorted, "png", newImgFile);

        // Since we started a JavaFX thread in distortImg we have to shut it down. Otherwise the JVM won't exit
        Platform.exit();
    }

    /**
    * Applies perspective transformations to a copy of this {@code image} and rotates it.
    * <p>
    * Since this method starts a JavaFX thread, it's important to call {@link Platform#exit()} at the end of
    * your application. Otherwise the thread will prevent the JVM from shutting down.
    *
    * @param image the image we want to distort
    * @return the distorted image
    */
    private static BufferedImage distortImg(BufferedImage image) {
        // Necessary to initialize the JavaFX platform and to avoid "IllegalStateException: Toolkit not initialized"
        new JFXPanel();

        // This array allows us to get the distorted image out of the runLater closure below
        final BufferedImage[] imageContainer = new BufferedImage[1];

        // We use this latch to await the end of the JavaFX thread. Otherwise this method would finish before
        // the thread creates the distorted image
        final CountDownLatch latch = new CountDownLatch(1);

        // To avoid "IllegalStateException: Not on FX application thread" we start a JavaFX thread
        Platform.runLater(() -> {
            int width = image.getWidth();
            int height = image.getHeight();
            Canvas canvas = new Canvas(width, height);
            GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
            ImageView imageView = new ImageView(SwingFXUtils.toFXImage(image, null));

            PerspectiveTransform trans = new PerspectiveTransform();
            trans.setUlx(0);
            trans.setUly(height / 4);
            trans.setUrx(width);
            trans.setUry(0);
            trans.setLrx(width);
            trans.setLry(height);
            trans.setLlx(0);
            trans.setLly(height - height / 2);

            imageView.setEffect(trans);

            imageView.setRotate(2);

            SnapshotParameters params = new SnapshotParameters();
            params.setFill(Color.TRANSPARENT);

            Image newImage = imageView.snapshot(params, null);
            graphicsContext.drawImage(newImage, 0, 0);

            imageContainer[0] = SwingFXUtils.fromFXImage(newImage, image);
            // Work is done, we decrement the latch which we used for awaiting the end of this thread
            latch.countDown();
        });
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return imageContainer[0];
    }
}
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171