blob: f33c37e5449e5cff210ae7f32b185fe079480e8d [file] [log] [blame]
/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package org.apache.batik.ext.awt.image.renderable;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
/**
* Default BumpMap implementation.
*
* @author <a href="mailto:vincent.hardy@eng.sun.com>Vincent Hardy</a>
* @version $Id$
*/
public final class BumpMap {
/**
* Image whose alpha channel is used for the
* normal calculation
*/
private RenderedImage texture;
/**
* Surface scale used in the normal computation
*/
private double surfaceScale, surfaceScaleX, surfaceScaleY;
/**
* User space to device space scale factors
*/
private double scaleX, scaleY;
/**
* Stores the normals for this bumpMap.
* scaleX and scaleY are the user space to device
* space scales.
*/
public BumpMap(RenderedImage texture,
double surfaceScale,
double scaleX, double scaleY){
this.texture = texture;
this.surfaceScaleX = surfaceScale*scaleX;
this.surfaceScaleY = surfaceScale*scaleY;
this.surfaceScale = surfaceScale;
this.scaleX = scaleX;
this.scaleY = scaleY;
}
/**
* @return surface scale used by this bump map.
*/
public final double getSurfaceScale(){
return surfaceScale;
}
/**
* @param x x-axis coordinate for which the normal is computed
* @param y y-axis coordinate for which the normal is computed
*/
public final double[][][] getNormalArray
(final int x, final int y,
final int w, final int h)
{
final double[][][] N = new double[h][w][4];
Rectangle srcRect = new Rectangle(x-1, y-1, w+2, h+2);
Rectangle srcBound = new Rectangle
(texture.getMinX(), texture.getMinY(),
texture.getWidth(), texture.getHeight());
if (srcRect.intersects(srcBound) == false)
return N;
srcRect = srcRect.intersection(srcBound);
final Raster r = texture.getData(srcRect);
srcRect = r.getBounds();
// System.out.println("SrcRect: " + srcRect);
// System.out.println("rect: [" +
// x + ", " + y + ", " +
// w + ", " + h + "]");
final DataBufferInt db = (DataBufferInt)r.getDataBuffer();
final int[] pixels = db.getBankData()[0];
final SinglePixelPackedSampleModel sppsm;
sppsm = (SinglePixelPackedSampleModel)r.getSampleModel();
final int scanStride = sppsm.getScanlineStride();
final int scanStridePP = scanStride + 1;
final int scanStrideMM = scanStride - 1;
int a = 0;
int i=0, j=0;
double prpc=0, prcc=0, prnc=0;
double crpc=0, crcc=0, crnc=0;
double nrpc=0, nrcc=0, nrnc=0;
double invNorm;
final double quarterSurfaceScaleX = surfaceScaleX / 4f;
final double quarterSurfaceScaleY = surfaceScaleY / 4f;
final double halfSurfaceScaleX = surfaceScaleX / 2f;
final double halfSurfaceScaleY = surfaceScaleY /2;
final double thirdSurfaceScaleX = surfaceScaleX / 3f;
final double thirdSurfaceScaleY = surfaceScaleY / 3f;
final double twoThirdSurfaceScaleX = surfaceScaleX * 2 / 3f;
final double twoThirdSurfaceScaleY = surfaceScaleY * 2 / 3f;
final double pixelScale = 1.0/255;
if(w <= 0)
return N;
// Process pixels on the border
if(h <= 0)
return N;
int xEnd = srcRect.x+srcRect.width-1;
if (xEnd > x+w) xEnd = x+w;
int yEnd = srcRect.y+srcRect.height-1;
if (yEnd > y+h) yEnd = y+h;
final int offset =
(db.getOffset() +
sppsm.getOffset(srcRect.x -r.getSampleModelTranslateX(),
srcRect.y -r.getSampleModelTranslateY()));
int yloc=y;
if (yloc < srcRect.y) {
yloc = srcRect.y;
}
// Top edge extend filters...
if (yloc == srcRect.y) {
double [][] NRow = N[yloc-y];
int p = offset + scanStride*(yloc-srcRect.y);
int xloc=x;
if (xloc < srcRect.x)
xloc = srcRect.x;
p += xloc-srcRect.x;
crcc = (pixels[p] >>> 24)*pixelScale;
nrcc = (pixels[p + scanStride] >>> 24)*pixelScale;
if (xloc != srcRect.x) {
crpc = (pixels[p - 1] >>> 24)*pixelScale;
nrpc = (pixels[p + scanStrideMM] >>> 24)*pixelScale;
}
else {
// Top left pixel, in src (0, 0);
crnc = (pixels[p+1] >>> 24)*pixelScale;
nrnc = (pixels[p + scanStridePP] >>> 24)*pixelScale;
final double [] n = NRow[xloc-x];
n[0] = - twoThirdSurfaceScaleX *
((2*crnc + nrnc - 2*crcc - nrcc));
n[1] = - twoThirdSurfaceScaleY *
((2*nrcc + nrnc - 2*crcc - crnc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
p++;
xloc++;
crpc = crcc;
nrpc = nrcc;
crcc = crnc;
nrcc = nrnc;
}
for (; xloc<xEnd; xloc++) {
// Middle Top row...
crnc = (pixels[p+1] >>> 24)*pixelScale;
nrnc = (pixels[p + scanStridePP] >>> 24)*pixelScale;
final double [] n = NRow[xloc-x];
n[0] = - thirdSurfaceScaleX * (( 2*crnc + nrnc)
- (2*crpc + nrpc));
n[1] = - halfSurfaceScaleY *(( nrpc + 2*nrcc + nrnc)
- (crpc + 2*crcc + crnc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
p++;
crpc = crcc;
nrpc = nrcc;
crcc = crnc;
nrcc = nrnc;
}
if ((xloc < x+w) &&
(xloc == srcRect.x+srcRect.width-1)) {
// Last pixel of top row
final double [] n = NRow[xloc-x];
n[0] = - twoThirdSurfaceScaleX *(( 2*crcc + nrcc)
- (2*crpc + nrpc));
n[1] = - twoThirdSurfaceScaleY *(( 2*nrcc + nrpc)
- (2*crcc + crpc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
}
yloc++;
}
for (; yloc<yEnd; yloc++) {
double [][] NRow = N[yloc-y];
int p = offset + scanStride*(yloc-srcRect.y);
int xloc=x;
if (xloc < srcRect.x)
xloc = srcRect.x;
p += xloc-srcRect.x;
prcc = (pixels[p - scanStride] >>> 24)*pixelScale;
crcc = (pixels[p] >>> 24)*pixelScale;
nrcc = (pixels[p + scanStride] >>> 24)*pixelScale;
if (xloc != srcRect.x) {
prpc = (pixels[p - scanStridePP] >>> 24)*pixelScale;
crpc = (pixels[p - 1] >>> 24)*pixelScale;
nrpc = (pixels[p + scanStrideMM] >>> 24)*pixelScale;
}
else {
// Now, process left column, from (0, 1) to (0, h-1)
crnc = (pixels[p+1] >>> 24)*pixelScale;
prnc = (pixels[p - scanStrideMM] >>> 24)*pixelScale;
nrnc = (pixels[p + scanStridePP] >>> 24)*pixelScale;
final double [] n = NRow[xloc-x];
n[0] = - halfSurfaceScaleX *(( prnc + 2*crnc + nrnc)
- (prcc + 2*crcc + nrcc));
n[1] = - thirdSurfaceScaleY *(( 2*prcc + prnc)
- ( 2*crcc + crnc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
p++;
xloc++;
prpc = prcc;
crpc = crcc;
nrpc = nrcc;
prcc = prnc;
crcc = crnc;
nrcc = nrnc;
}
for (; xloc<xEnd; xloc++) {
// Middle Middle row...
prnc = (pixels[p - scanStrideMM] >>> 24)*pixelScale;
crnc = (pixels[p+1] >>> 24)*pixelScale;
nrnc = (pixels[p + scanStridePP] >>> 24)*pixelScale;
final double [] n = NRow[xloc-x];
n[0] = - quarterSurfaceScaleX *(( prnc + 2*crnc + nrnc)
- (prpc + 2*crpc + nrpc));
n[1] = - quarterSurfaceScaleY *(( nrpc + 2*nrcc + nrnc)
- (prpc + 2*prcc + prnc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
p++;
prpc = prcc;
crpc = crcc;
nrpc = nrcc;
prcc = prnc;
crcc = crnc;
nrcc = nrnc;
}
if ((xloc < x+w) &&
(xloc == srcRect.x+srcRect.width-1)) {
// Now, proces right column, from (w-1, 1) to (w-1, h-1)
final double [] n = NRow[xloc-x];
n[0] = - halfSurfaceScaleX *(( prcc + 2*crcc + nrcc)
- (prpc + 2*crpc + nrpc));
n[1] = - thirdSurfaceScaleY *(( nrpc + 2*nrcc)
- ( prpc + 2*prcc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
}
}
if ((yloc < y+h) &&
(yloc == srcRect.y+srcRect.height-1)) {
double [][] NRow = N[yloc-y];
int p = offset + scanStride*(yloc-srcRect.y);
int xloc=x;
if (xloc < srcRect.x)
xloc = srcRect.x;
p += xloc-srcRect.x;
crcc = (pixels[p] >>> 24)*pixelScale;
prcc = (pixels[p - scanStride] >>> 24)*pixelScale;
if (xloc != srcRect.x) {
prpc = (pixels[p - scanStridePP] >>> 24)*pixelScale;
crpc = (pixels[p - 1] >>> 24)*pixelScale;
}
else {
// Process first pixel of last row
crnc = (pixels[p + 1] >>> 24)*pixelScale;
prnc = (pixels[p - scanStrideMM] >>> 24)*pixelScale;
final double [] n = NRow[xloc-x];
n[0] = - twoThirdSurfaceScaleX * ((2*crnc + prnc - 2*crcc - prcc));
n[1] = - twoThirdSurfaceScaleY * ((2*crcc + crnc - 2*prcc - prnc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
p++;
xloc++;
crpc = crcc;
prpc = prcc;
crcc = crnc;
prcc = prnc;
}
for (; xloc<xEnd; xloc++) {
// Middle of Bottom row...
crnc = (pixels[p + 1] >>> 24)*pixelScale;
prnc = (pixels[p - scanStrideMM] >>> 24)*pixelScale;
final double [] n = NRow[xloc-x];
n[0] = - thirdSurfaceScaleX *(( 2*crnc + prnc)
- (2*crpc + prpc));
n[1] = - halfSurfaceScaleY *(( crpc + 2*crcc + crnc)
- (prpc + 2*prcc + prnc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
p++;
crpc = crcc;
prpc = prcc;
crcc = crnc;
prcc = prnc;
}
if ((xloc < x+w) &&
(xloc == srcRect.x+srcRect.width-1)) {
// Bottom right corner
final double [] n = NRow[xloc-x];
n[0] = - twoThirdSurfaceScaleX *(( 2*crcc + prcc)
- (2*crpc + prpc));
n[1] = - twoThirdSurfaceScaleY *(( 2*crcc + crpc)
- (2*prcc + prpc));
invNorm = 1.0/Math.sqrt(n[0]*n[0] + n[1]*n[1] + 1);
n[0] *= invNorm;
n[1] *= invNorm;
n[2] = invNorm;
n[3] = crcc*surfaceScale;
}
}
return N;
}
}