Local image Transform
This program has been used to locally warp an image, by applying a sinusoidal warp to one segment of a
standard JPEG (using 8-bit color channels). This is useful for warping textures by curving them to better fit
curved objects when mapping the texture to a 3D object. This is written in Java using the NetBeans IDE.
package localimagetransform;

import java.util.*;
import java.io.*;
import javax.imageio.ImageIO;
import java.awt.image.ColorModel;
import java.awt.image.BufferedImage;

public class LocalTransform
{
private BufferedImage img = null;
private BufferedImage processedImg;
private BufferedImage canvas;
private int imageHeight;
private int imageWidth;
private int[] myPixels;
private int[] myProcessedPixels = null;
private int numberPixelsInOriginalImage;
private int newNumberPixels;
private boolean imageHasAlpha;
private int channels;
private int canvasColor = 0x66a30f;

/** Creates a new instance of LocalTransform */
public LocalTransform()
{
    imageHeight = 0;
    imageWidth = 0;
    numberPixelsInOriginalImage = 0;
}

public LocalTransform(String imageFileName)
{
    imageHeight = 0;
    imageWidth = 0;
    LoadImage(imageFileName);
    myPixels = getPixels();
}

public void LoadImage(String filename)
{
    try
    {
        img = ImageIO.read(new File(filename));
        imageHeight = img.getHeight();
        imageWidth = img.getWidth();
    }
    catch(IOException e)
    {
        System.err.println("Could not load graphic file!" + e.getMessage());
    }
    numberPixelsInOriginalImage = imageHeight*imageWidth;
    ColorModel myColorModel = img.getColorModel();
    imageHasAlpha = myColorModel.hasAlpha();
    System.out.println("Image " + filename + " loaded");
    System.out.println("Image has Alpha? " + imageHasAlpha);
    //setCanvas();
    
}

public void setCanvas()
{
    if(imageHasAlpha)
    {
        channels = 4;
    }
    else
    {
        channels = 3;
    }
    //use 8-bit color
    myProcessedPixels = new int[numberPixelsInOriginalImage];
    if(channels == 3)
    {
        for(int i = 0; i < numberPixelsInOriginalImage; i++)
        {
            myProcessedPixels[i] = canvasColor;
        }
    }
    else if(channels == 4)  //N.B. This has not been tested on an image with translucency yet
    {
        for(int i = 0; i < numberPixelsInOriginalImage; i++)
        {
            //set alpha
            myProcessedPixels[i*4] = 255;
            //set R
            myProcessedPixels[(i*4)+1] = 255;
            //set G
            myProcessedPixels[(i*4)+2] = 0;
            //set B
            myProcessedPixels[(i*4)+3] = 0;
        }
    }
    processedImg = new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB);
    processedImg.setRGB(0,0,imageWidth, imageHeight, myProcessedPixels, 0,imageWidth);
    //Save Canvas
    try
    {
        File outputFile = new File("canvas.jpg");
        ImageIO.write(processedImg,"jpg",outputFile);
    }
    catch(IOException e)
    {
        System.err.println("Could not save image!" + e.getMessage());
    }
}

public int[] setCanvas(int w, int h)
{
    //use 8-bit color
    newNumberPixels = w*h;
    int[] myNewCanvasPixels = new int[newNumberPixels];
    
    for(int i = 0; i < newNumberPixels; i++)
    {
        myNewCanvasPixels[i] = canvasColor;
    }
    canvas = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
    canvas.setRGB(0,0,w, h, myNewCanvasPixels, 0,w);
    System.out.println("Target canvas created");
    //Save Canvas
    try
    {
        File outputFile = new File("canvas.jpg");
        ImageIO.write(canvas,"jpg",outputFile);
        System.out.println("Target canvas saved");
    }
    catch(IOException e)
    {
        System.err.println("Could not save image!" + e.getMessage());
    }
    return myNewCanvasPixels;
}

public void processImage()
{
    try
    {
        //Apply local transform
        //Displace left half of image vertically down by a sine function through pi/2
        //Transform parameters
        int curvature = +1; //+1 or 0, -1 not fully implemented
        int imageDownShift = 0;
        double maxAngle = Math.PI/2; //90 degrees
        
        //Calculate final image size required with height increase
        //Set amplitude of sine displacement
        int amplitude = (int)(0.4*imageHeight);
        //Calculate size of final image needed for transform
        int newImageWidth = imageWidth;
        int newImageHeight = imageHeight + amplitude;
        
        //Place pixels in 1D array
        int[] pixel1DArray = setCanvas(newImageWidth, newImageHeight);
        for(int i = 0; i < myPixels.length; i++)
        {
            pixel1DArray[i] = myPixels[i];
        }
        
        //Place pixels in a 2D array
        int[][] tempArray = new int[imageWidth][imageHeight];
        for(int y = 0; y < imageHeight; y++) //rows
        {
            for(int x = 0; x < imageWidth; x++) //columns = data in 1 row
            {
                tempArray[x][y] = myPixels[(y*imageWidth) + x];
            }
        }
        
        //Flip image if needed
        tempArray = horizontalFlip(imageWidth, imageHeight, tempArray);
        
        //Expand image as needed
        int[][] pixel2DArray = new int[newImageWidth][newImageHeight];          
        for(int y = 0; y < imageHeight; y++) //rows
        {
            for(int x = 0; x < imageWidth; x++) //columns = data in 1 row
            {
                pixel2DArray[x][y] = tempArray[x][y];  //myPixels[(y*imageWidth) + x];
            }
        }
        for(int y = imageHeight; y < newImageHeight; y++) //rows
        {
            for(int x = 0; x < imageWidth; x++) //columns = data in 1 row
            {
                pixel2DArray[x][y] = canvasColor;
            }
        }
                  
        //Create 2D pixel array target buffer to hold processed pixels
        int[][] temp2DArray = new int[newImageWidth][newImageHeight];
        
        //Transform
        myProcessedPixels = new int[pixel1DArray.length];
        
//******************* Main transform function *********************************************************************
        //Apply transformation to temp2DArray row-by-row
        //System.out.println(amplitude);            
        for(int y = 0; y < newImageHeight; y++)
        {
            for(int x = 0; x < newImageWidth; x++)
            {
                //transform left half of image only
                if(y >=  amplitude && x < newImageWidth/2)
                {
                    double D;
                    switch(curvature)
                    {
                        //Curve up
                        case +1:
                            D = y - (  (amplitude) * Math.sin(Math.PI/2 * x/(newImageWidth-x))  );
                            break;
                        case -1:
                            D = y - (  (amplitude) * ( 1- Math.sin(Math.PI/2 * x/(newImageWidth-x) ) )  );
                            break;
                        case 0:
                            D = y;
                            break;
                        default:
                            D = y;
                    }
                    int Y = (int)D;
                    temp2DArray[x][y] = pixel2DArray[x][Y];
                }
                if(y >= amplitude-imageDownShift && x >= newImageWidth/2)
                {
                    //Shift remainder of image
                    int Y;
                    switch(curvature)
                    {
                        case +1: //Move image down
                            Y = y-amplitude;
                            break;
                        case -1: //Move image up
                            Y = y + amplitude;
                        case 0:
                            Y = y;
                            break;
                        default:
                            Y = y;
                    }
                    temp2DArray[x][y] = pixel2DArray[x][Y];
                }
                if (y < amplitude)
                {
                    if(curvature == + 1)
                    {
                        //Pack with canvas color
                        temp2DArray[x][y] = canvasColor;
                    }
                }
            }
        }
        
//**********************************************************************************************************           
        //Load myProcessedPixels 1D array
        for(int y = 0; y < newImageHeight; y++) //rows
        {
            for(int x = 0; x < newImageWidth; x++) //columns = data in 1 row
            {
                myProcessedPixels[(y*newImageWidth) + x] = temp2DArray[x][y];
            }
        }
        //Save processed image from myProcessedPixels 1D array
        saveImage(newImageWidth,  newImageHeight, myProcessedPixels);
        
    }
    catch(NullPointerException e)
    {
        System.err.println("Could not process image - no image file loaded! " + e.getMessage());
    }
}

private int[][] shiftImageUp(int w, int h, int shiftAmplitude, int[][] pixelArray)
{
    int myTempArray[][] = new int[w][h];
    for(int y = 0; y < h-shiftAmplitude; y++)
    {
        for(int x = 0; x < w; x++)
        {
            myTempArray[x][y] = pixelArray[x][y+shiftAmplitude];
        }
    }
    return myTempArray;
}

private int[][] shiftImageDown(int w, int h, int shiftAmplitude, int[][] pixelArray)
{
    int myTempArray[][] = new int[w][h];
    for(int y = shiftAmplitude; y < h; y++)
    {
        for(int x = 0; x < w; x++)
        {
            myTempArray[x][y] = pixelArray[x][y-shiftAmplitude];
        }
    }
    return myTempArray;
}

public int[][] horizontalFlip(int w, int h, int[][] pixelArray)
{
    int[][] tempArray = new int[w][h];
    for(int y = 0; y < h; y++)
    {
        for(int x = 0; x < w; x++)
        {
            tempArray[x][y] = pixelArray[x][h-y-1];
        }
    }
    return tempArray;
}

public int[] getPixels()
{
    //Initialise empty pixel array
    //int numPixels = imageWidth*imageHeight;
    int[] tempPixels = new int[numberPixelsInOriginalImage];
    //Load pixels into array
    try
    {
        tempPixels =  img.getRGB(0,0, imageWidth, imageHeight, myPixels, 0, imageWidth);
        System.out.println("Pixels loaded");
    }
    catch(NullPointerException e)
    {
        System.err.println("Could not save image!" + e.getMessage());
    }
    return tempPixels;
}

public void saveImage()
{
    try
    {
        BufferedImage myImage = canvas; // set equal to canvas
        myImage.setRGB(0,0,imageWidth,imageHeight,myPixels,0,imageWidth); // set equal to original image
        File outputFile = new File("savedImg.jpg");
        try
        {
            ImageIO.write(myImage,"jpg",outputFile);
        }
        catch(IOException e)
        {
            System.err.println("Could not save image!" + e.getMessage());
        }
    }
    catch(NullPointerException e)
    {
        System.err.println("Could not save image - no image file loaded! " + e.getMessage());
    }
}

public void saveImage(int w, int h, int[] pixelArray)
{
    try
    {
        BufferedImage myImage = canvas; // set equal to canvas
        myImage.setRGB(0,0,w,h,pixelArray,0,w);
        File outputFile = new File("savedImg.jpg");
        try
        {
            ImageIO.write(myImage,"jpg",outputFile);
        }
        catch(IOException e)
        {
            System.err.println("Could not save image!" + e.getMessage());
        }
    }
    catch(NullPointerException e)
    {
        System.err.println("Could not save image - no image file loaded! " + e.getMessage());
    }
}

}
package localimagetransform;

public class Main
{

/** Creates a new instance of Main */
public Main()
{
}

/**
 * @param args the command line arguments
 */
public static void main(String[] args)
{
    //In this case modify an image called "myImage.jpg"        
    LocalTransform myTransform = new LocalTransform("myImage.jpg");       
    myTransform.processImage();
    //myTransform.saveImage();
}

}