// Catalano Imaging Library
// The Catalano Framework
//
// Copyright © Diego Catalano, 2012-2016
// diego.catalano at live.com
//
// Copyright © Andrew Kirillov, 2005-2008
// andrew.kirillov@gmail.com
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
package Catalano.Imaging.Filters;
import Catalano.Imaging.FastBitmap;
import Catalano.Imaging.IApply;
import Catalano.Imaging.IApplyInPlace;
/**
* Rotate image using bilinear algorithm.
*
* The class implements image rotation filter using bilinear
* algorithm, which does not assume any interpolation.
*
* Rotation is performed in counterclockwise direction.
*
* @author Diego Catalano
*/
public class RotateBilinear implements IApply, IApplyInPlace {
private double angle;
private boolean keepSize;
private int newWidth;
private int newHeight;
private int fillRed = 0;
private int fillGreen = 0;
private int fillBlue = 0;
private int fillGray = 0;
/**
* Get Angle.
* @return Angle.
*/
public double getAngle() {
return -angle;
}
/**
* Set Angle.
* @param angle Angle.
*/
public void setAngle(double angle) {
this.angle = -angle;
}
/**
* Keep original size.
* @return True if keep the original image size, otherwise false.
*/
public boolean isKeepSize() {
return keepSize;
}
/**
* Set keep original size.
* @param keepSize True if keep the original image size, otherwise false.
*/
public void setKeepSize(boolean keepSize) {
this.keepSize = keepSize;
}
/**
* Set Fill color.
* @param red Red channel's value.
* @param green Green channel's value.
* @param blue Blue channel's value.
*/
public void setFillColor(int red, int green, int blue){
this.fillRed = red;
this.fillGreen = green;
this.fillBlue = blue;
}
/**
* Set Fill color.
* @param gray Gray channel's value.
*/
public void setFillColor(int gray){
this.fillGray = gray;
}
/**
* Initialize a new instance of the RotateBilinear class.
* @param angle Angle [0..360].
*/
public RotateBilinear(double angle) {
this.angle = -angle;
this.keepSize = false;
}
/**
* Initialize a new instance of the RotateBilinear class.
* @param angle Angle [0..360].
* @param keepSize Keep original size.
*/
public RotateBilinear(double angle, boolean keepSize) {
this.angle = -angle;
this.keepSize = keepSize;
}
@Override
public FastBitmap apply(FastBitmap fastBitmap) {
if (fastBitmap.isGrayscale()){
int width = fastBitmap.getWidth();
int height = fastBitmap.getHeight();
double oldIradius = (double) ( height - 1 ) / 2;
double oldJradius = (double) ( width - 1 ) / 2;
CalculateNewSize(fastBitmap);
FastBitmap destinationData = new FastBitmap(newWidth, newHeight, FastBitmap.ColorSpace.Grayscale);
// get destination image size
double newIradius = (double) ( newHeight - 1 ) / 2;
double newJradius = (double) ( newWidth - 1 ) / 2;
// angle's sine and cosine
double angleRad = -angle * Math.PI / 180;
double angleCos = Math.cos( angleRad );
double angleSin = Math.sin( angleRad );
// destination pixel's coordinate relative to image center
double ci, cj;
// coordinates of source points
double oi, oj, ti, tj, di1, dj1, di2, dj2;
int oi1, oj1, oi2, oj2;
// width and height decreased by 1
int imax = height - 1;
int jmax = width - 1;
ci = -newIradius;
for (int i = 0; i < newHeight; i++) {
// do some pre-calculations of source points' coordinates
// (calculate the part which depends on y-loop, but does not
// depend on x-loop)
ti = angleCos * ci + oldIradius;
tj = -angleSin * ci + oldJradius;
cj = -newJradius;
for (int j = 0; j < newWidth; j++) {
// coordinates of source point
oi = ti + angleSin * cj;
oj = tj + angleCos * cj;
// top-left coordinate
oi1 = (int) oi;
oj1 = (int) oj;
// validate source pixel's coordinates
if ( ( oi1 < 0 ) || ( oj1 < 0 ) || ( oi1 >= height ) || ( oj1 >= width ) ){
// fill destination image with filler
destinationData.setGray(i, j, fillGray);
}
else{
// bottom-right coordinate
oi2 = ( oi1 == imax ) ? oi1 : oi1 + 1;
oj2 = ( oj1 == jmax ) ? oj1 : oj1 + 1;
if ( ( di1 = oi - (double) oi1 ) < 0 )
di1 = 0;
di2 = 1.0 - di1;
if ( ( dj1 = oj - (double) oj1 ) < 0 )
dj1 = 0;
dj2 = 1.0 - dj1;
// get four points
int p1 = fastBitmap.getGray(oi1, oj1);
int p2 = fastBitmap.getGray(oi1, oj2);
int p3 = fastBitmap.getGray(oi2, oj1);
int p4 = fastBitmap.getGray(oi2, oj2);
int g = (int)(
di2 * ( dj2 * ( p1 ) + dj1 * ( p2 ) ) +
di1 * ( dj2 * ( p3 ) + dj1 * ( p4 ) ) );
destinationData.setGray(i, j, g);
}
cj++;
}
ci++;
}
return destinationData;
}
else if (fastBitmap.isRGB()){
int width = fastBitmap.getWidth();
int height = fastBitmap.getHeight();
double oldIradius = (double) ( height - 1 ) / 2;
double oldJradius = (double) ( width - 1 ) / 2;
CalculateNewSize(fastBitmap);
FastBitmap destinationData = new FastBitmap(newWidth, newHeight, FastBitmap.ColorSpace.RGB);
// get destination image size
double newIradius = (double) ( newHeight - 1 ) / 2;
double newJradius = (double) ( newWidth - 1 ) / 2;
// angle's sine and cosine
double angleRad = -angle * Math.PI / 180;
double angleCos = Math.cos( angleRad );
double angleSin = Math.sin( angleRad );
// destination pixel's coordinate relative to image center
double ci, cj;
// coordinates of source points
double oi, oj, ti, tj, di1, dj1, di2, dj2;
int oi1, oj1, oi2, oj2;
// width and height decreased by 1
int imax = height - 1;
int jmax = width - 1;
ci = -newIradius;
for (int i = 0; i < newHeight; i++) {
// do some pre-calculations of source points' coordinates
// (calculate the part which depends on y-loop, but does not
// depend on x-loop)
ti = angleSin * ci + oldIradius;
tj = angleCos * ci + oldJradius;
cj = -newJradius;
for (int j = 0; j < newWidth; j++) {
// coordinates of source point
oi = ti + angleCos * cj;
oj = tj - angleSin * cj;
// top-left coordinate
oi1 = (int) oi;
oj1 = (int) oj;
// validate source pixel's coordinates
if ( ( oi1 < 0 ) || ( oj1 < 0 ) || ( oi1 >= height ) || ( oj1 >= width ) ){
// fill destination image with filler
destinationData.setRGB(i, j, fillRed, fillGreen, fillBlue);
}
else{
// bottom-right coordinate
oi2 = ( oi1 == imax ) ? oi1 : oi1 + 1;
oj2 = ( oj1 == jmax ) ? oj1 : oj1 + 1;
if ( ( di1 = oi - (double) oi1 ) < 0 )
di1 = 0;
di2 = 1.0 - di1;
if ( ( dj1 = oj - (double) oj1 ) < 0 )
dj1 = 0;
dj2 = 1.0 - dj1;
// get four points (red)
int p1 = fastBitmap.getRed(oi1, oj1);
int p2 = fastBitmap.getRed(oi1, oj2);
int p3 = fastBitmap.getRed(oi2, oj1);
int p4 = fastBitmap.getRed(oi2, oj2);
int r = (int)(
di2 * ( dj2 * ( p1 ) + dj1 * ( p2 ) ) +
di1 * ( dj2 * ( p3 ) + dj1 * ( p4 ) ) );
// get four points (green)
p1 = fastBitmap.getGreen(oi1, oj1);
p2 = fastBitmap.getGreen(oi1, oj2);
p3 = fastBitmap.getGreen(oi2, oj1);
p4 = fastBitmap.getGreen(oi2, oj2);
int g = (int)(
di2 * ( dj2 * ( p1 ) + dj1 * ( p2 ) ) +
di1 * ( dj2 * ( p3 ) + dj1 * ( p4 ) ) );
// get four points (blue)
p1 = fastBitmap.getBlue(oi1, oj1);
p2 = fastBitmap.getBlue(oi1, oj2);
p3 = fastBitmap.getBlue(oi2, oj1);
p4 = fastBitmap.getBlue(oi2, oj2);
int b = (int)(
di2 * ( dj2 * ( p1 ) + dj1 * ( p2 ) ) +
di1 * ( dj2 * ( p3 ) + dj1 * ( p4 ) ) );
destinationData.setRGB(i, j, r, g, b);
}
cj++;
}
ci++;
}
return destinationData;
}
return null;
}
@Override
public void applyInPlace(FastBitmap fastBitmap) {
FastBitmap temp = apply(fastBitmap);
fastBitmap.setImage(temp);
}
private void CalculateNewSize(FastBitmap fastBitmap){
// return same size if original image size should be kept
if ( keepSize ){
this.newWidth = fastBitmap.getWidth();
this.newHeight = fastBitmap.getHeight();
return;
}
// angle's sine and cosine
double angleRad = -angle * Math.PI / 180;
double angleCos = Math.cos( angleRad );
double angleSin = Math.sin( angleRad );
// calculate half size
double halfWidth = (double) fastBitmap.getWidth() / 2;
double halfHeight = (double) fastBitmap.getHeight() / 2;
// rotate corners
double cx1 = halfWidth * angleCos;
double cy1 = halfWidth * angleSin;
double cx2 = halfWidth * angleCos - halfHeight * angleSin;
double cy2 = halfWidth * angleSin + halfHeight * angleCos;
double cx3 = -halfHeight * angleSin;
double cy3 = halfHeight * angleCos;
double cx4 = 0;
double cy4 = 0;
// recalculate image size
halfWidth = Math.max( Math.max( cx1, cx2 ), Math.max( cx3, cx4 ) ) - Math.min( Math.min( cx1, cx2 ), Math.min( cx3, cx4 ) );
halfHeight = Math.max( Math.max( cy1, cy2 ), Math.max( cy3, cy4 ) ) - Math.min( Math.min( cy1, cy2 ), Math.min( cy3, cy4 ) );
this.newWidth = (int)(halfWidth * 2 + 0.5);
this.newHeight = (int)(halfHeight * 2 + 0.5);
}
}
Ads help maintain this website.