1 /* 2 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.scenario.effect.impl.state; 27 28 import java.nio.FloatBuffer; 29 import com.sun.scenario.effect.Effect.AccelType; 30 import com.sun.scenario.effect.FilterContext; 31 import com.sun.scenario.effect.impl.BufferUtil; 32 import com.sun.scenario.effect.impl.EffectPeer; 33 import com.sun.scenario.effect.impl.Renderer; 34 35 /** 36 * The state and implementation class for calculating 1 dimensional 37 * linear convolution kernels for performing multi-pass box blurs. 38 */ 39 public class BoxBlurState extends HVSeparableKernel { 40 private int hsize; 41 private int vsize; 42 private int blurPasses; 43 private FloatBuffer weights; 44 45 public int getHsize() { 46 return hsize; 47 } 48 49 public void setHsize(int hsize) { 50 if (hsize < 0 || hsize > 255) { 51 throw new IllegalArgumentException("Blur size must be in the range [0,255]"); 52 } 53 this.hsize = hsize; 54 } 55 56 public int getVsize() { 57 return vsize; 58 } 59 60 public void setVsize(int vsize) { 61 if (vsize < 0 || vsize > 255) { 62 throw new IllegalArgumentException("Blur size must be in the range [0,255]"); 63 } 64 this.vsize = vsize; 65 } 66 67 public int getBlurPasses() { 68 return blurPasses; 69 } 70 71 public void setBlurPasses(int blurPasses) { 72 if (blurPasses < 0 || blurPasses > 3) { 73 throw new IllegalArgumentException("Number of passes must be in the range [0,3]"); 74 } 75 this.blurPasses = blurPasses; 76 } 77 78 public float getSpread() { 79 return 0.0f; 80 } 81 82 @Override 83 public boolean isNop() { 84 return (blurPasses == 0 || (hsize <= 1 && vsize <= 1)); 85 } 86 87 @Override 88 public boolean isNop(int pass) { 89 return (blurPasses == 0 || ((pass == 0) ? hsize : vsize) <= 1); 90 } 91 92 public int getKernelSize(int pass) { 93 int ksize = pass == 0 ? hsize : vsize; 94 if (ksize < 1) ksize = 1; 95 ksize = (ksize-1) * blurPasses + 1; 96 ksize |= 1; 97 return ksize; 98 } 99 100 private int getScaleVal(int pass, boolean needScale) { 101 int ksize = getKernelSize(pass); 102 int scale = 0; 103 while (ksize > 128) { 104 ksize = ((ksize + 1) / 2) | 1; 105 scale--; 106 } 107 return needScale ? scale : ksize; 108 } 109 110 public int getPow2Scale(int pass) { 111 return getScaleVal(pass, true); 112 } 113 114 @Override 115 public int getScaledKernelSize(int pass) { 116 return getScaleVal(pass, false); 117 } 118 119 @Override 120 public int getPow2ScaleX() { 121 return getPow2Scale(0); 122 } 123 124 @Override 125 public int getPow2ScaleY() { 126 return getPow2Scale(1); 127 } 128 129 public FloatBuffer getWeights(int pass) { 130 int klen = pass == 0 ? hsize : vsize; 131 if (klen < 1 || blurPasses == 0) klen = 1; 132 long ik[] = new long[klen]; 133 for (int i = 0; i < klen; i++) { 134 ik[i] = 1; 135 } 136 for (int p = 1; p < blurPasses; p++) { 137 long ik2[] = new long[ik.length + klen-1]; 138 for (int i = 0; i < ik.length; i++) { 139 for (int k = 0; k < klen; k++) { 140 ik2[i+k] += ik[i]; 141 } 142 } 143 ik = ik2; 144 } 145 if ((ik.length & 1) == 0) { 146 // If kernel length is odd then it is centered on a pixel. 147 // If kernel length is even, then it is not centered on a pixel 148 // and the weights are applied to a sample between pixels. 149 // Instead of trying to sample between pixels we instead 150 // distribute the weights by half a pixel by averaging 151 // adjacent values together and lengthening the kernel by a pixel. 152 long ik2[] = new long[ik.length + 1]; 153 for (int i = 0; i < ik.length; i++) { 154 ik2[i] += ik[i]; 155 ik2[i+1] += ik[i]; 156 } 157 ik = ik2; 158 } 159 int scale = getPow2Scale(pass); 160 while (scale < 0) { 161 int newlen = ((ik.length + 1) / 2) | 1; 162 int skewi = (newlen * 2 - ik.length) / 2; 163 long ik2[] = new long[newlen]; 164 for (int i = 0; i < ik.length; i++) { 165 ik2[skewi / 2] += ik[i]; 166 skewi++; 167 ik2[skewi / 2] += ik[i]; 168 } 169 ik = ik2; 170 scale++; 171 } 172 double sum = 0.0; 173 for (int i = 0; i < ik.length; i++) { 174 sum += ik[i]; 175 } 176 // We need to apply the spread on only one pass 177 // Prefer pass1 if r1 is not trivial 178 // Otherwise use pass 0 so that it doesn't disappear 179 int spreadpass = (vsize > 1) ? 1 : 0; 180 float s = (pass == spreadpass) ? getSpread() : 0f; 181 sum += (ik[0] - sum) * s; 182 183 if (weights == null) { 184 // peersize(MAX_KERNEL_SIZE) rounded up to the next multiple of 4 185 int maxbufsize = LinearConvolveKernel.MAX_KERNEL_SIZE; 186 maxbufsize = LinearConvolveKernel.getPeerSize(maxbufsize); 187 maxbufsize = (maxbufsize + 3) & (~3); 188 weights = BufferUtil.newFloatBuffer(maxbufsize); 189 } 190 weights.clear(); 191 for (int i = 0; i < ik.length; i++) { 192 weights.put((float) (ik[i] / sum)); 193 } 194 int limit = getPeerSize(ik.length); 195 while (weights.position() < limit) { 196 weights.put(0f); 197 } 198 weights.limit(limit); 199 weights.rewind(); 200 return weights; 201 } 202 203 @Override 204 public EffectPeer getPeer(Renderer r, FilterContext fctx, int pass) { 205 int ksize = getScaledKernelSize(pass); 206 if (ksize <= 1) { 207 // The result of ksize being too <= 1 is a NOP 208 // so we return null here to skip the corresponding 209 // filter pass. 210 return null; 211 } 212 int psize = getPeerSize(ksize); 213 AccelType actype = r.getAccelType(); 214 String name; 215 switch (actype) { 216 case NONE: 217 case SIMD: 218 name = "BoxBlur"; 219 break; 220 default: 221 name = "LinearConvolve"; 222 break; 223 } 224 EffectPeer peer = r.getPeerInstance(fctx, name, psize); 225 return peer; 226 } 227 }