| /***************************************************************************** |
| * 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; |
| } |
| } |
| |