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.image.*;
020import java.util.*;
021
022/**
023 * A filter which uses a another image as a ask to produce a halftoning effect.
024 */
025public class HalftoneFilter extends AbstractBufferedImageOp {
026        
027        private float softness = 0.1f;
028        private boolean invert;
029        private boolean monochrome;
030        private BufferedImage mask;
031
032        public HalftoneFilter() {
033        }
034
035        /**
036         * Set the softness of the effect in the range 0..1.
037         * @param softness the softness
038     * @min-value 0
039     * @max-value 1
040     * @see #getSoftness
041         */
042        public void setSoftness( float softness ) {
043                this.softness = softness;
044        }
045        
046        /**
047         * Get the softness of the effect.
048         * @return the softness
049     * @see #setSoftness
050         */
051        public float getSoftness() {
052                return softness;
053        }
054        
055        /**
056         * Set the halftone mask.
057         * @param mask the mask
058     * @see #getMask
059         */
060        public void setMask( BufferedImage mask ) {
061                this.mask = mask;
062        }
063        
064        /**
065         * Get the halftone mask.
066         * @return the mask
067     * @see #setMask
068         */
069        public BufferedImage getMask() {
070                return mask;
071        }
072        
073        public void setInvert( boolean invert ) {
074                this.invert = invert;
075        }
076        
077        public boolean getInvert() {
078                return invert;
079        }
080        
081        /**
082         * Set whether to do monochrome halftoning.
083         * @param monochrome true for monochrome halftoning
084     * @see #getMonochrome
085         */
086        public void setMonochrome(boolean monochrome) {
087                this.monochrome = monochrome;
088        }
089        
090        /**
091         * Get whether to do monochrome halftoning.
092         * @return true for monochrome halftoning
093     * @see #setMonochrome
094         */
095        public boolean getMonochrome() {
096                return monochrome;
097        }
098
099    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
100        int width = src.getWidth();
101        int height = src.getHeight();
102
103        if ( dst == null )
104            dst = createCompatibleDestImage( src, null );
105                if ( mask == null )
106                        return dst;
107
108        int maskWidth = mask.getWidth();
109        int maskHeight = mask.getHeight();
110
111        float s = 255*softness;
112
113                int[] inPixels = new int[width];
114                int[] maskPixels = new int[maskWidth];
115
116        for ( int y = 0; y < height; y++ ) {
117                        getRGB( src, 0, y, width, 1, inPixels );
118                        getRGB( mask, 0, y % maskHeight, maskWidth, 1, maskPixels );
119
120                        for ( int x = 0; x < width; x++ ) {
121                                int maskRGB = maskPixels[x % maskWidth];
122                                int inRGB = inPixels[x];
123                                if ( invert )
124                    maskRGB ^= 0xffffff;
125                if ( monochrome ) {
126                    int v = PixelUtils.brightness( maskRGB );
127                    int iv = PixelUtils.brightness( inRGB );
128                    float f = 1-ImageMath.smoothStep( iv-s, iv+s, v );
129                    int a = (int)(255 * f);
130                    inPixels[x] = (inRGB & 0xff000000) | (a << 16) | (a << 8) | a;
131                } else {
132                    int ir = (inRGB >> 16) & 0xff;
133                    int ig = (inRGB >> 8) & 0xff;
134                    int ib = inRGB & 0xff;
135                    int mr = (maskRGB >> 16) & 0xff;
136                    int mg = (maskRGB >> 8) & 0xff;
137                    int mb = maskRGB & 0xff;
138                    int r = (int)(255 * (1-ImageMath.smoothStep( ir-s, ir+s, mr )));
139                    int g = (int)(255 * (1-ImageMath.smoothStep( ig-s, ig+s, mg )));
140                    int b = (int)(255 * (1-ImageMath.smoothStep( ib-s, ib+s, mb )));
141                    inPixels[x] = (inRGB & 0xff000000) | (r << 16) | (g << 8) | b;
142                }
143            }
144
145                        setRGB( dst, 0, y, width, 1, inPixels );
146        }
147
148        return dst;
149    }
150
151        public String toString() {
152                return "Stylize/Halftone...";
153        }
154}