All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.image.EmbossOp Maven / Gradle / Ivy

There is a newer version: 2024.9.17689.20240905T073330Z-240800
Show newest version
/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2012 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.day.image;

import java.awt.image.BandCombineOp;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

/**
 * The EmbossOp implements embossing of an image against another
 * image, the so called bump image. The embossing algorithm works like this :
 * 
    *
  1. Define the position of the light source using two angles, the azimut and * the elevation. The azimut defines the direction in the image plane from * where the light shines and the elevation defines the height above the * image plane of the light source. *
  2. Define a filtersize defining the spread of the embossing effect. We could * also say, this is sort of the size of the light. *
  3. Define the bump image. This image is scaled to gray and then used to * define the embossing piece. *
  4. For each pixel position in the images : *
      *
    • Define a plane E: ax + bx + cx + d = 0 where a is * the luminance difference between pixels above and below the * current pixel, b is the luminance difference between pixels left * and right of the current pixel and c is constant relative to * the filtersize. *
    • Calculate the scale factor to apply to the pixel in the image as * follows : *
        *
      • If factors a and b both are zero, the plane E is no plane * at all and the scale factor is the height of the light * source. *
      • Else it is the distance of the light source to the above * defined plane. *
      *
    • Multiply each pixel with the scale factor, ignoring the alpha * channels. *
    *
*

* The emobossing area is the smaller of the bump and the image to emboss. You * will get the best results from using an image as the bump image and some * monochromatic image as the image to emboss. * * @version $Revision$ * @author fmeschbe * @since coati * @audience wad */ public class EmbossOp extends AbstractBufferedImageOp { /** The bump image in terms of grayscale sample values for the pixels */ private final int[] bump; /** The width of the bump image */ private final int width; /** The height of the bump image */ private final int height; /** The direction of the light in the image plane in degrees */ private final float azimut; /** The elevation of the light source above the image plane in degrees */ private final float elevation; /** Size of the light filter defining the size of the light source */ private final float filtersize; /** * Creates a new EmbossOp filter instance with the given * parameters for embossing. The bump image is copied into the instance * so that subsequent changes to the bump image do not have an effect on * the embossing operation. * * @param bump the image being used for the 'bump' * @param azimut the light direction azimut in degrees. * @param elevation the light direction elevation in degrees. * @param filtersize the filtersize for the embossing. * * @throws IllegalArgumentException if any of the azimut, elevation or * filtersize arguments is negative or Float.NaN. * @throws NullPointerException if bump is null. */ public EmbossOp(BufferedImage bump, float azimut, float elevation, float filtersize) { // initialize the base class super(null); // will throw IllegalArgumentException if NaN or negative checkValue("azimut", azimut); checkValue("elevation", elevation); checkValue("filtersize", filtersize); // copy and scale to gray the bump image, throw if null this.bump = GreyScalerHelper.filter(bump.getRaster()); this.width = bump.getWidth(); this.height = bump.getHeight(); // set the light source configuration this.azimut = azimut; this.elevation = elevation; this.filtersize = filtersize; } //---------- BufferedImageOp ----------------------------------------------- // no methods of the base class to overwrite //---------- internal ------------------------------------------------------ /** * Checks the float value whether it is NaN or negative. If so, the * IllegalArgumentException is thrown with the name as a hint * on what argument is illegal. * * @param parName The name of the parameter to name in the exception if * thrown * @param parValue The value of the parameter to check * * @throws IllegalArgumentException if parValue is NaN or negative */ private void checkValue(String parName, float parValue) { if (Float.isNaN(parValue) || parValue < 0f) { throw new IllegalArgumentException("Argument " + parName + " must not be negative or NaN"); } } //---------- protected ----------------------------------------------------- /** * Does the real embossing of the source image into the destination image. * * @param src The source image containing the image data to be embossed. * @param dst The destination image to emboss the source image into. */ protected void doFilter(BufferedImage src, BufferedImage dst) { int width = src.getWidth(); int height = src.getHeight(); // Get the raster we operate on Raster srcRas = src.getRaster(); WritableRaster dstRas = dst.getRaster(); double pixelScale = 255.9; double azi = Math.toRadians(azimut); double ele = Math.toRadians(elevation); // coordinates of the light source int lx = (int) (Math.cos(azi) * Math.cos(ele) * pixelScale); int ly = (int) (Math.sin(azi) * Math.cos(ele) * pixelScale); int lz = (int) (Math.sin(ele) * pixelScale); // constant z component of image surface normal - this depends on the // image slope we wish to associate with an angle of 45 degrees, which // depends on the width of the filter used to produce the source image. int nz = (int)((6 * 255) / filtersize); int nz2 = nz * nz; int nzlz = nz * lz; // The bump image dimensions int bw = this.width; int bh = this.height; // The texture dimensions int tw = width; int th = height; // The operational dimensions are the smaller of src and bump int nw = (tw < bw) ? tw : bw; int nh = (th < bh) ? th : bh; // data buffer for one scan line int[] srcRed = new int[ nw ]; int[] srcGrn = new int[ nw ]; int[] srcBlu = new int[ nw ]; // optimization for vertical normals: L.[0 0 1] int background = lz; for (int y=0; y < nh; y++) { // get the next chunk if needed srcRas.getSamples(0, y, nw, 1, 0, srcRed); srcRas.getSamples(0, y, nw, 1, 1, srcGrn); srcRas.getSamples(0, y, nw, 1, 2, srcBlu); // chunkY = 0; // chunkTop = y; int s1 = bw * y + 1; int s2 = s1 + ((y < nh-1) ? bw : 0); int s3 = s2 + ((y < nh-2) ? bw : 0); for (int x=1, toff=1; x < nw-1; x++, s1++, s2++, s3++) { int nx = bump[s1-1] + bump[s2-1] + bump[s3-1] - bump[s1+1] - bump[s2+1] - bump[s3+1]; int ny = bump[s3-1] + bump[s3] + bump[s3+1] - bump[s1-1] - bump[s1] - bump[s1+1]; int ndotl = 0; int shade = 0; // shade with distant light source if ( nx == 0 && ny == 0 ) { shade = background; } else { ndotl = nx*lx + ny*ly + nzlz; if (ndotl < 0 ) { shade = 0; } else { shade = (int) (ndotl / Math.sqrt(nx*nx + ny*ny + nz2)); } } // do something with the shading result srcRed[toff] = (srcRed[toff] * shade) >>> 8; srcBlu[toff] = (srcBlu[toff] * shade) >>> 8; srcGrn[toff] = (srcGrn[toff] * shade) >>> 8; toff++; // Next pixel } dstRas.setSamples(0, y, nw, 1, 0, srcRed); dstRas.setSamples(0, y, nw, 1, 1, srcBlu); dstRas.setSamples(0, y, nw, 1, 2, srcGrn); // copy alpha from src using srcRed array as buffer dstRas.setSamples(0, y, nw, 1, 3, srcRas.getSamples(0, y, nw, 1, 3, srcRed)); } } /** * The GreyScaleHelper class is a simple helper class to * implement a simple gray scaling algorithm for the bump image. */ private static final class GreyScalerHelper { /** * The matrix for the grayscale operation has the luminance factors * for RGB in a linear color space. * * @see Matrix * Operations for Image Processing, Converting to * Luminance */ private static final float[][] bopEl = { { 0.0f, 0.0f, 0.0f, 0.0f, 0x0}, { 0.0f, 0.0f, 0.0f, 0.0f, 0x0}, { 0.0f, 0.0f, 0.0f, 0.0f, 0x0}, { -.3086f, -.6094f, -.0820f, 0.0f, 255} }; /** The grayscaling operation based on above matrix */ private static final BandCombineOp op = new BandCombineOp(bopEl, null); /** * Performs the grayscaling operation. * @param input The input Raster to scale to gray. This raster is not * changed. * @return The Alpha channel of the gray scaled image containing the * grey levels. * @throws NullPointerException if input is null. */ private static final int[] filter(Raster input) { Raster tmp = op.filter(input, null); return tmp.getSamples(0, 0, tmp.getWidth(), tmp.getHeight(), 3, (int[])null); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy