1. package com.gloomyfish.filter.study;
2.
3. import java.awt.image.BufferedImage;
4.
5. public class DisplaceFilter extends AbstractBufferedImageOp {
6. /**
7. * Treat pixels off the edge as zero.
8. */
9. public final static int ZERO = 0;
10.
11. /**
12. * Clamp pixels to the image edges.
13. */
14. public final static int CLAMP = 1;
15.
16. /**
17. * Wrap pixels off the edge onto the oppsoite edge.
18. */
19. public final static int WRAP = 2;
20.
21. /**
22. * Clamp pixels RGB to the image edges, but zero the alpha. This prevents gray borders on your image.
23. */
24. public final static int RGB_CLAMP = 3;
25.
26. protected int edgeAction = RGB_CLAMP;
27. private float amount = 1;
28. private BufferedImage textImage = null;
29. private int[] xmap, ymap;
30. private int dw, dh;
31.
32. public DisplaceFilter() {
33. this(RGB_CLAMP);
34. }
35.
36. public DisplaceFilter(int edgeAction) {
37. this.edgeAction = edgeAction;
38. }
39.
40. public float getAmount() {
41. return amount;
42. }
43.
44. public void setAmount(float amount) {
45. this.amount = amount;
46. }
47.
48. public BufferedImage getTextImage() {
49. return textImage;
50. }
51.
52. public void setTextImage(BufferedImage textInputImage) {
53. this.textImage = textInputImage;
54. }
55.
56. @Override
57. public BufferedImage filter(BufferedImage src, BufferedImage dest) {
58. BufferedImage dm = textImage != null ? textImage : src;
59. int width = src.getWidth();
60. int height = src.getHeight();
61. if ( dest == null )
62. dest = createCompatibleDestImage( src, null );
63. dw = dm.getWidth();
64. dh = dm.getHeight();
65. int[] inPixels = new int[width*height];
66. int[] outPixels = new int[width*height];
67. getRGB( src, 0, 0, width, height, inPixels );
68.
69. int[] textImagePixels = new int[dw*dh];
70. getRGB( dm, 0, 0, dw, dh, textImagePixels );
71. xmap = new int[dw*dh];
72. ymap = new int[dw*dh];
73.
74. int i = 0;
75. for ( int y = 0; y < dh; y++ ) {
76. for ( int x = 0; x < dw; x++ ) {
77. int rgb = textImagePixels[i];
78. int r = (rgb >> 16) & 0xff;
79. int g = (rgb >> 8) & 0xff;
80. int b = rgb & 0xff;
81. // An arbitrary scaling factor which gives a good range
82. // for "amount", 16 is another optional value
83. textImagePixels[i] = (r+g+b) / 8;
84. i++;
85. }
86. }
87.
88. // it is very important pre-process, extract the edge
89. i = 0;
90. for ( int y = 0; y < dh; y++ ) {
91. int j1 = ((y+dh-1) % dh) * dw;// row - 1
92. int j2 = y*dw; // row
93. int j3 = ((y+1) % dh) * dw; // row + 1
94. for ( int x = 0; x < dw; x++ ) {
95. int k1 = (x+dw-1) % dw; // column - 1
96. int k2 = x; // column
97. int k3 = (x+1) % dw; // column + 1
98. xmap[i] = textImagePixels[k1+j1] + textImagePixels[k1+j2] + textImagePixels[k1+j3] - textImagePixels[k3+j1] - textImagePixels[k3+j2] - textImagePixels[k3+j3];
99. ymap[i] = textImagePixels[k1+j3] + textImagePixels[k2+j3] + textImagePixels[k3+j3] - textImagePixels[k1+j1] - textImagePixels[k2+j1] - textImagePixels[k3+j1];
100. i++;
101. }
102. }
103. textImagePixels = null;
104.
105. // start to process input pixel here!!!!
106. float[] out = new float[2];
107. int srcWidth = width;
108. int srcHeight = height;
109. int srcWidth1 = width-1;
110. int srcHeight1 = height-1;
111. int outdex = 0;
112. for (int y = 0; y < height; y++) {
113. for (int x = 0; x < width; x++) {
114. outdex = y * width + x;
115. transformInverse(x, y, out);
116. int srcX = (int)Math.floor( out[0] );
117. int srcY = (int)Math.floor( out[1] );
118. float xWeight = out[0]-srcX;
119. float yWeight = out[1]-srcY;
120. int nw, ne, sw, se;
121.
122. if ( srcX >= 0 && srcX < srcWidth1 && srcY >= 0 && srcY < srcHeight1) {
123. // Easy case, all corners are in the image
124. i = srcWidth*srcY + srcX;
125. nw = inPixels[i];
126. ne = inPixels[i+1];
127. sw = inPixels[i+srcWidth];
128. se = inPixels[i+srcWidth+1];
129. } else {
130. // Some of the corners are off the image
131. nw = getPixel( inPixels, srcX, srcY, srcWidth, srcHeight );
132. ne = getPixel( inPixels, srcX+1, srcY, srcWidth, srcHeight );
133. sw = getPixel( inPixels, srcX, srcY+1, srcWidth, srcHeight );
134. se = getPixel( inPixels, srcX+1, srcY+1, srcWidth, srcHeight );
135. }
136. //双线性插值
137. outPixels[outdex] = ImageMath.bilinearInterpolate(xWeight, yWeight, nw, ne, sw, se);
138. }
139. }
140.
141. // 返回最终结果
142. setRGB( dest, 0, 0, width, height, outPixels );
143. xmap = ymap = null;
144. return dest;
145. }
146.
147. /**
148. *
149. * @param x
150. * @param y
151. * @param out
152. */
153. protected void transformInverse(int x, int y, float[] out) {
154. int i = (y % dh)*dw + x % dw;
155. out[0] = x + amount * xmap[i];
156. out[1] = y + amount * ymap[i];
157. }
158.
159. final private int getPixel( int[] pixels, int x, int y, int width, int height ) {
160. if (x < 0 || x >= width || y < 0 || y >= height) {
161. switch (edgeAction) {
162. case ZERO:
163. default:
164. return 0;
165. case WRAP:
166. return pixels[(ImageMath.mod(y, height) * width) + ImageMath.mod(x, width)];
167. case CLAMP:
168. return pixels[(ImageMath.clamp(y, 0, height-1) * width) + ImageMath.clamp(x, 0, width-1)];
169. case RGB_CLAMP:
170. return pixels[(ImageMath.clamp(y, 0, height-1) * width) + ImageMath.clamp(x, 0, width-1)] & 0x00ffffff;
171. }
172. }
173. return pixels[ y*width+x ];
174. }
175. }