001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.awt.*;
020import java.awt.image.*;
021
022/**
023 * A class to emboss an image.
024 */
025public class EmbossFilter extends WholeImageFilter {
026
027        private final static float pixelScale = 255.9f;
028
029        private float azimuth = 135.0f * ImageMath.PI / 180.0f, elevation = 30.0f * ImageMath.PI / 180f;
030        private boolean emboss = false;
031        private float width45 = 3.0f;
032
033        public EmbossFilter() {
034        }
035
036        public void setAzimuth(float azimuth) {
037                this.azimuth = azimuth;
038        }
039        
040        public float getAzimuth() {
041                return azimuth;
042        }
043        
044        public void setElevation(float elevation) {
045                this.elevation = elevation;
046        }
047        
048        public float getElevation() {
049                return elevation;
050        }
051        
052        public void setBumpHeight(float bumpHeight) {
053                this.width45 = 3 * bumpHeight;
054        }
055
056        public float getBumpHeight() {
057                return width45 / 3;
058        }
059
060        public void setEmboss(boolean emboss) {
061                this.emboss = emboss;
062        }
063        
064        public boolean getEmboss() {
065                return emboss;
066        }
067        
068        protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
069                int index = 0;
070                int[] outPixels = new int[width * height];
071
072                int[] bumpPixels;
073                int bumpMapWidth, bumpMapHeight;
074                
075                bumpMapWidth = width;
076                bumpMapHeight = height;
077                bumpPixels = new int[bumpMapWidth * bumpMapHeight];
078                for (int i = 0; i < inPixels.length; i++)
079                        bumpPixels[i] = PixelUtils.brightness(inPixels[i]);
080
081                int Nx, Ny, Nz, Lx, Ly, Lz, Nz2, NzLz, NdotL;
082                int shade, background;
083
084                Lx = (int)(Math.cos(azimuth) * Math.cos(elevation) * pixelScale);
085                Ly = (int)(Math.sin(azimuth) * Math.cos(elevation) * pixelScale);
086                Lz = (int)(Math.sin(elevation) * pixelScale);
087
088                Nz = (int)(6 * 255 / width45);
089                Nz2 = Nz * Nz;
090                NzLz = Nz * Lz;
091
092                background = Lz;
093
094                int bumpIndex = 0;
095                
096                for (int y = 0; y < height; y++, bumpIndex += bumpMapWidth) {
097                        int s1 = bumpIndex;
098                        int s2 = s1 + bumpMapWidth;
099                        int s3 = s2 + bumpMapWidth;
100
101                        for (int x = 0; x < width; x++, s1++, s2++, s3++) {
102                                if (y != 0 && y < height-2 && x != 0 && x < width-2) {
103                                        Nx = bumpPixels[s1-1] + bumpPixels[s2-1] + bumpPixels[s3-1] - bumpPixels[s1+1] - bumpPixels[s2+1] - bumpPixels[s3+1];
104                                        Ny = bumpPixels[s3-1] + bumpPixels[s3] + bumpPixels[s3+1] - bumpPixels[s1-1] - bumpPixels[s1] - bumpPixels[s1+1];
105
106                                        if (Nx == 0 && Ny == 0)
107                                                shade = background;
108                                        else if ((NdotL = Nx*Lx + Ny*Ly + NzLz) < 0)
109                                                shade = 0;
110                                        else
111                                                shade = (int)(NdotL / Math.sqrt(Nx*Nx + Ny*Ny + Nz2));
112                                } else
113                                        shade = background;
114
115                                if (emboss) {
116                                        int rgb = inPixels[index];
117                                        int a = rgb & 0xff000000;
118                                        int r = (rgb >> 16) & 0xff;
119                                        int g = (rgb >> 8) & 0xff;
120                                        int b = rgb & 0xff;
121                                        r = (r*shade) >> 8;
122                                        g = (g*shade) >> 8;
123                                        b = (b*shade) >> 8;
124                                        outPixels[index++] = a | (r << 16) | (g << 8) | b;
125                                } else
126                                        outPixels[index++] = 0xff000000 | (shade << 16) | (shade << 8) | shade;
127                        }
128                }
129
130                return outPixels;
131        }
132
133        public String toString() {
134                return "Stylize/Emboss...";
135        }
136
137}