/*
* #%L
* BroadleafCommerce Open Admin Platform
* %%
* Copyright (C) 2009 - 2013 Broadleaf Commerce
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.broadleafcommerce.openadmin.server.service.artifact.image.effects.chain.filter;
import org.broadleafcommerce.openadmin.server.service.artifact.image.Operation;
import org.broadleafcommerce.openadmin.server.service.artifact.image.effects.chain.UnmarshalledParameter;
import org.broadleafcommerce.openadmin.server.service.artifact.image.effects.chain.conversion.ParameterTypeEnum;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.ConvolveOp;
import java.awt.image.IndexColorModel;
import java.awt.image.Kernel;
import java.io.InputStream;
import java.util.Map;
public class GaussianBlur extends BaseFilter {
public static final int NUM_KERNELS = 16;
public static final float[][] GAUSSIAN_BLUR_KERNELS = generateGaussianBlurKernels(NUM_KERNELS);
public static float[][] generateGaussianBlurKernels(int level) {
float[][] pascalsTriangle = generatePascalsTriangle(level);
float[][] gaussianTriangle = new float[pascalsTriangle.length][];
for (int i = 0; i < gaussianTriangle.length; i++) {
float total = 0.0f;
gaussianTriangle[i] = new float[pascalsTriangle[i].length];
for (int j = 0; j < pascalsTriangle[i].length; j++) {
total += pascalsTriangle[i][j];
}
float coefficient = 1 / total;
for (int j = 0; j < pascalsTriangle[i].length; j++) {
gaussianTriangle[i][j] = coefficient * pascalsTriangle[i][j];
}
float checksum = 0.0f;
for (int j = 0; j < gaussianTriangle[i].length; j++) {
checksum += gaussianTriangle[i][j];
}
if (checksum == 1.0) {
// hurrah
}
}
return gaussianTriangle;
}
public static float[][] generatePascalsTriangle(int level) {
if (level < 2) {
level = 2;
}
float[][] triangle = new float[level][];
triangle[0] = new float[1];
triangle[1] = new float[2];
triangle[0][0] = 1.0f;
triangle[1][0] = 1.0f;
triangle[1][1] = 1.0f;
for (int i = 2; i < level; i++) {
triangle[i] = new float[i + 1];
triangle[i][0] = 1.0f;
triangle[i][i] = 1.0f;
for (int j = 1; j < triangle[i].length - 1; j++) {
triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
}
}
return triangle;
}
private int kernelSize;
private int numOfPasses;
public GaussianBlur() {
//do nothing
}
public GaussianBlur(int kernelSize, int numOfPasses, RenderingHints hints) {
this.kernelSize = kernelSize;
this.numOfPasses = numOfPasses;
}
@Override
public Operation buildOperation(Map<String, String> parameterMap, InputStream artifactStream, String mimeType) {
String key = FilterTypeEnum.GAUSSIANBLUR.toString().toLowerCase();
if (!containsMyFilterParams(key, parameterMap)) {
return null;
}
Operation operation = new Operation();
operation.setName(key);
String factor = parameterMap.get(key + "-factor");
operation.setFactor(factor==null?null:Double.valueOf(factor));
UnmarshalledParameter kernelSize = new UnmarshalledParameter();
String kernelSizeApplyFactor = parameterMap.get(key + "-kernel-size-apply-factor");
kernelSize.setApplyFactor(kernelSizeApplyFactor == null ? false : Boolean.valueOf(kernelSizeApplyFactor));
kernelSize.setName("kernel-size");
kernelSize.setType(ParameterTypeEnum.INT.toString());
kernelSize.setValue(parameterMap.get(key + "-kernel-size-amount"));
UnmarshalledParameter numOfPasses = new UnmarshalledParameter();
String numOfPassesApplyFactor = parameterMap.get(key + "-num-passes-apply-factor");
numOfPasses.setApplyFactor(numOfPassesApplyFactor == null ? false : Boolean.valueOf(numOfPassesApplyFactor));
numOfPasses.setName("num-passes");
numOfPasses.setType(ParameterTypeEnum.INT.toString());
numOfPasses.setValue(parameterMap.get(key + "-num-passes-amount"));
operation.setParameters(new UnmarshalledParameter[]{kernelSize, numOfPasses});
return operation;
}
public BufferedImage filter(BufferedImage src, BufferedImage dst) {
if (kernelSize < 1 || kernelSize > NUM_KERNELS) {
return src;
}
if (numOfPasses < 1) {
return src;
}
if (src == null) {
throw new NullPointerException("src image is null");
}
if (src == dst) {
throw new IllegalArgumentException("src image cannot be the same as the dst image");
}
boolean needToConvert = false;
ColorModel srcCM = src.getColorModel();
ColorModel dstCM;
BufferedImage origDst = dst;
if (srcCM instanceof IndexColorModel) {
IndexColorModel icm = (IndexColorModel) srcCM;
src = icm.convertToIntDiscrete(src.getRaster(), false);
srcCM = src.getColorModel();
}
if (dst == null) {
dst = createCompatibleDestImage(src, null);
dstCM = srcCM;
origDst = dst;
} else {
dstCM = dst.getColorModel();
if (srcCM.getColorSpace().getType() !=
dstCM.getColorSpace().getType())
{
needToConvert = true;
dst = createCompatibleDestImage(src, null);
dstCM = dst.getColorModel();
}
else if (dstCM instanceof IndexColorModel) {
dst = createCompatibleDestImage(src, null);
dstCM = dst.getColorModel();
}
}
float[] matrix = GAUSSIAN_BLUR_KERNELS[kernelSize - 1];
Kernel gaussianBlur1 = new Kernel(matrix.length, 1, matrix);
Kernel gaussianBlur2 = new Kernel(1, matrix.length, matrix);
ConvolveOp gaussianOp1 = new ConvolveOp(gaussianBlur1, ConvolveOp.EDGE_NO_OP, null);
ConvolveOp gaussianOp2 = new ConvolveOp(gaussianBlur2, ConvolveOp.EDGE_NO_OP, null);
BufferedImage tempImage = new BufferedImage(src.getWidth(),
src.getHeight(), src.getType());
dst = new BufferedImage(src.getWidth(),
src.getHeight(), src.getType());
BufferedImage nextSource = src;
for (int i = 0; i < numOfPasses; i++) {
tempImage = gaussianOp1.filter(nextSource, tempImage);
dst = gaussianOp2.filter(tempImage, dst);
nextSource = dst;
}
if (needToConvert) {
ColorConvertOp ccop = new ColorConvertOp(hints);
ccop.filter(dst, origDst);
}
else if (origDst != dst) {
java.awt.Graphics2D g = origDst.createGraphics();
try {
g.drawImage(dst, 0, 0, null);
} finally {
g.dispose();
}
}
return origDst;
}
}