1 /* Copyright (C) 2005-2011 Fabio Riccardi */ 2 3 package com.lightcrafts.jai.opimage; 4 5 import com.lightcrafts.mediax.jai.PointOpImage; 6 import com.lightcrafts.mediax.jai.ImageLayout; 7 import com.lightcrafts.mediax.jai.RasterAccessor; 8 import com.lightcrafts.mediax.jai.RasterFormatTag; 9 import com.lightcrafts.jai.JAIContext; 10 import com.lightcrafts.utils.ColorScience; 11 import com.lightcrafts.utils.HSB; 12 13 import java.awt.image.DataBuffer; 14 import java.awt.image.RenderedImage; 15 import java.awt.image.Raster; 16 import java.awt.image.WritableRaster; 17 import java.awt.*; 18 import java.awt.color.ICC_ProfileRGB; 19 import java.util.Map; 20 21 import Jama.Matrix; 22 23 /** 24 * Copyright (C) Light Crafts, Inc. 25 * User: fabio 26 * Date: Mar 20, 2007 27 * Time: 4:32:46 PM 28 */ 29 public class FilteredGrayscaleOpImage extends PointOpImage { 30 private final float[][] toLinearsRGB; 31 private final float[] filter; 32 private final float angle; 33 FilteredGrayscaleOpImage(RenderedImage source, float filter[], float angle, float strength, Map config)34 public FilteredGrayscaleOpImage(RenderedImage source, float filter[], float angle, float strength, Map config) { 35 super(source, new ImageLayout(source), config, true); 36 permitInPlaceOperation(); 37 ICC_ProfileRGB sRGB = (ICC_ProfileRGB) JAIContext.sRGBColorProfile; 38 ICC_ProfileRGB linRGB = (ICC_ProfileRGB) JAIContext.linearProfile; 39 toLinearsRGB = new Matrix(sRGB.getMatrix()).inverse().times(new Matrix(linRGB.getMatrix())).getArrayFloat(); 40 41 this.filter = filter.clone(); 42 this.angle = angle; 43 } 44 45 @Override computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)46 protected void computeRect(Raster[] sources, 47 WritableRaster dest, 48 Rectangle destRect) { 49 // Retrieve format tags. 50 RasterFormatTag[] formatTags = getFormatTags(); 51 52 RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0], 53 getSourceImage(0).getColorModel()); 54 RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); 55 56 switch (dst.getDataType()) { 57 case DataBuffer.TYPE_USHORT: 58 ushortLoop(src, dst); 59 break; 60 default: 61 throw new UnsupportedOperationException("Unsupported data type: " + dst.getDataType()); 62 } 63 64 if (dst.needsClamping()) { 65 dst.clampDataArrays(); 66 } 67 dst.copyDataToRaster(); 68 } 69 angleDiff(float a, float b)70 private static float angleDiff(float a, float b) { 71 float result = Math.abs(a - b); 72 if (result > Math.PI) 73 result = (float) (2 * Math.PI - result); 74 return result; 75 } 76 ushortLoop(RasterAccessor src, RasterAccessor dst)77 protected void ushortLoop(RasterAccessor src, RasterAccessor dst) { 78 int width = src.getWidth(); 79 int height = src.getHeight(); 80 81 short dstData[] = dst.getShortDataArray(0); 82 int dstBandOffsets[] = dst.getBandOffsets(); 83 int dstLineStride = dst.getScanlineStride(); 84 int dstPixelStride = dst.getPixelStride(); 85 86 short srcData[] = src.getShortDataArray(0); 87 int srcBandOffsets[] = src.getBandOffsets(); 88 int srcLineStride = src.getScanlineStride(); 89 int srcPixelStride = src.getPixelStride(); 90 91 int srcROffset = srcBandOffsets[0]; 92 int srcGOffset = srcBandOffsets[1]; 93 int srcBOffset = srcBandOffsets[2]; 94 95 int dstROffset = dstBandOffsets[0]; 96 int dstGOffset = dstBandOffsets[1]; 97 int dstBOffset = dstBandOffsets[2]; 98 99 float filterHue, filterSat; 100 { 101 float hsb[] = new float[3]; 102 HSB.fromRGB(filter, hsb); 103 filterHue = (float) (2 * Math.PI * hsb[0] - Math.PI); 104 filterSat = hsb[1]; 105 } 106 107 float rgb[] = new float[3]; 108 float hsb[] = new float[3]; 109 110 for (int row = 0; row < height; row++) { 111 for (int col = 0; col < width; col++) { 112 int srcPixOffset = srcPixelStride * col + row * srcLineStride; 113 int r = 0xffff & srcData[srcPixOffset + srcROffset]; 114 int g = 0xffff & srcData[srcPixOffset + srcGOffset]; 115 int b = 0xffff & srcData[srcPixOffset + srcBOffset]; 116 117 rgb[0] = toLinearsRGB[0][0] * r + toLinearsRGB[0][1] * g + toLinearsRGB[0][2] * b; 118 rgb[1] = toLinearsRGB[1][0] * r + toLinearsRGB[1][1] * g + toLinearsRGB[1][2] * b; 119 rgb[2] = toLinearsRGB[2][0] * r + toLinearsRGB[2][1] * g + toLinearsRGB[2][2] * b; 120 121 HSB.fromRGB(rgb, hsb); 122 float hue = (float) (2 * Math.PI * hsb[0] - Math.PI); 123 124 float hueDiff = angleDiff(hue, filterHue); 125 float mask = (float) Math.cos(Math.PI * hueDiff / angle); 126 // float mask = (float) Math.abs(1 - hueDiff / angle); 127 128 mask = (1-filterSat) + filterSat * mask; 129 130 float gray = ColorScience.Wr * r + ColorScience.Wg * g + ColorScience.Wb * b; 131 132 int rr = (int) (gray * mask); 133 int gg = (int) (gray * mask); 134 int bb = (int) (gray * mask); 135 136 int dstPixOffset = dstPixelStride * col + row * dstLineStride; 137 dstData[dstPixOffset + dstROffset] = (short) (rr < 0 ? 0 : rr > 0xffff ? 0xffff : rr); 138 dstData[dstPixOffset + dstGOffset] = (short) (gg < 0 ? 0 : gg > 0xffff ? 0xffff : gg); 139 dstData[dstPixOffset + dstBOffset] = (short) (bb < 0 ? 0 : bb > 0xffff ? 0xffff : bb); 140 } 141 } 142 } 143 } 144