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}