picrate 0.0.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +47 -0
  3. data/.mvn/extensions.xml +9 -0
  4. data/.mvn/wrapper/maven-wrapper.properties +1 -0
  5. data/.travis.yml +10 -0
  6. data/CHANGELOG.md +4 -0
  7. data/LICENSE.md +165 -0
  8. data/README.md +51 -0
  9. data/Rakefile +59 -0
  10. data/bin/picrate +8 -0
  11. data/docs/.gitignore +6 -0
  12. data/docs/_config.yml +30 -0
  13. data/docs/_includes/footer.html +38 -0
  14. data/docs/_includes/head.html +16 -0
  15. data/docs/_includes/header.html +27 -0
  16. data/docs/_includes/icon-github.html +1 -0
  17. data/docs/_includes/icon-github.svg +1 -0
  18. data/docs/_includes/icon-twitter.html +1 -0
  19. data/docs/_includes/icon-twitter.svg +1 -0
  20. data/docs/_includes/navigation.html +24 -0
  21. data/docs/_layouts/default.html +20 -0
  22. data/docs/_layouts/page.html +14 -0
  23. data/docs/_layouts/post.html +15 -0
  24. data/docs/_posts/2018-05-06-getting_started.md +8 -0
  25. data/docs/_posts/2018-05-06-install_jruby.md +35 -0
  26. data/docs/_sass/_base.scss +206 -0
  27. data/docs/_sass/_layout.scss +242 -0
  28. data/docs/_sass/_syntax-highlighting.scss +71 -0
  29. data/docs/about.md +10 -0
  30. data/docs/css/main.scss +38 -0
  31. data/docs/favicon.ico +0 -0
  32. data/docs/feed.xml +30 -0
  33. data/docs/index.html +38 -0
  34. data/lib/picrate.rb +10 -0
  35. data/lib/picrate/app.rb +187 -0
  36. data/lib/picrate/creators/sketch_class.rb +57 -0
  37. data/lib/picrate/creators/sketch_factory.rb +12 -0
  38. data/lib/picrate/creators/sketch_writer.rb +21 -0
  39. data/lib/picrate/helper_methods.rb +214 -0
  40. data/lib/picrate/helpers/numeric.rb +9 -0
  41. data/lib/picrate/library.rb +69 -0
  42. data/lib/picrate/library_loader.rb +53 -0
  43. data/lib/picrate/native_folder.rb +35 -0
  44. data/lib/picrate/native_loader.rb +27 -0
  45. data/lib/picrate/runner.rb +81 -0
  46. data/lib/picrate/version.rb +4 -0
  47. data/library/boids/boids.rb +209 -0
  48. data/library/chooser/chooser.rb +19 -0
  49. data/library/control_panel/control_panel.rb +182 -0
  50. data/library/library_proxy/README.md +99 -0
  51. data/library/library_proxy/library_proxy.rb +14 -0
  52. data/library/slider/slider.rb +42 -0
  53. data/library/vector_utils/vector_utils.rb +69 -0
  54. data/library/video_event/video_event.rb +3 -0
  55. data/license.txt +508 -0
  56. data/picrate.gemspec +35 -0
  57. data/pom.rb +122 -0
  58. data/pom.xml +214 -0
  59. data/src/main/java/japplemenubar/JAppleMenuBar.java +88 -0
  60. data/src/main/java/japplemenubar/libjAppleMenuBar.jnilib +0 -0
  61. data/src/main/java/monkstone/ColorUtil.java +115 -0
  62. data/src/main/java/monkstone/MathToolModule.java +236 -0
  63. data/src/main/java/monkstone/PicrateLibrary.java +47 -0
  64. data/src/main/java/monkstone/core/LibraryProxy.java +127 -0
  65. data/src/main/java/monkstone/fastmath/Deglut.java +122 -0
  66. data/src/main/java/monkstone/fastmath/package-info.java +6 -0
  67. data/src/main/java/monkstone/filechooser/Chooser.java +48 -0
  68. data/src/main/java/monkstone/noise/SimplexNoise.java +465 -0
  69. data/src/main/java/monkstone/slider/CustomHorizontalSlider.java +168 -0
  70. data/src/main/java/monkstone/slider/CustomVerticalSlider.java +182 -0
  71. data/src/main/java/monkstone/slider/SimpleHorizontalSlider.java +149 -0
  72. data/src/main/java/monkstone/slider/SimpleSlider.java +196 -0
  73. data/src/main/java/monkstone/slider/SimpleVerticalSlider.java +163 -0
  74. data/src/main/java/monkstone/slider/Slider.java +67 -0
  75. data/src/main/java/monkstone/slider/SliderBar.java +277 -0
  76. data/src/main/java/monkstone/slider/SliderGroup.java +78 -0
  77. data/src/main/java/monkstone/slider/WheelHandler.java +35 -0
  78. data/src/main/java/monkstone/vecmath/AppRender.java +87 -0
  79. data/src/main/java/monkstone/vecmath/JRender.java +56 -0
  80. data/src/main/java/monkstone/vecmath/ShapeRender.java +87 -0
  81. data/src/main/java/monkstone/vecmath/package-info.java +20 -0
  82. data/src/main/java/monkstone/vecmath/vec2/Vec2.java +757 -0
  83. data/src/main/java/monkstone/vecmath/vec2/package-info.java +6 -0
  84. data/src/main/java/monkstone/vecmath/vec3/Vec3.java +727 -0
  85. data/src/main/java/monkstone/vecmath/vec3/package-info.java +6 -0
  86. data/src/main/java/monkstone/videoevent/VideoInterface.java +42 -0
  87. data/src/main/java/monkstone/videoevent/package-info.java +20 -0
  88. data/src/main/java/processing/awt/PGraphicsJava2D.java +3098 -0
  89. data/src/main/java/processing/awt/PShapeJava2D.java +401 -0
  90. data/src/main/java/processing/awt/PSurfaceAWT.java +1660 -0
  91. data/src/main/java/processing/core/PApplet.java +17647 -0
  92. data/src/main/java/processing/core/PConstants.java +1033 -0
  93. data/src/main/java/processing/core/PFont.java +1250 -0
  94. data/src/main/java/processing/core/PGraphics.java +9614 -0
  95. data/src/main/java/processing/core/PImage.java +3608 -0
  96. data/src/main/java/processing/core/PMatrix.java +347 -0
  97. data/src/main/java/processing/core/PMatrix2D.java +694 -0
  98. data/src/main/java/processing/core/PMatrix3D.java +1153 -0
  99. data/src/main/java/processing/core/PShape.java +4332 -0
  100. data/src/main/java/processing/core/PShapeOBJ.java +544 -0
  101. data/src/main/java/processing/core/PShapeSVG.java +1987 -0
  102. data/src/main/java/processing/core/PStyle.java +208 -0
  103. data/src/main/java/processing/core/PSurface.java +242 -0
  104. data/src/main/java/processing/core/PSurfaceNone.java +479 -0
  105. data/src/main/java/processing/core/PVector.java +1140 -0
  106. data/src/main/java/processing/data/FloatDict.java +829 -0
  107. data/src/main/java/processing/data/FloatList.java +912 -0
  108. data/src/main/java/processing/data/IntDict.java +796 -0
  109. data/src/main/java/processing/data/IntList.java +913 -0
  110. data/src/main/java/processing/data/JSONArray.java +1260 -0
  111. data/src/main/java/processing/data/JSONObject.java +2282 -0
  112. data/src/main/java/processing/data/JSONTokener.java +435 -0
  113. data/src/main/java/processing/data/Sort.java +46 -0
  114. data/src/main/java/processing/data/StringDict.java +601 -0
  115. data/src/main/java/processing/data/StringList.java +775 -0
  116. data/src/main/java/processing/data/Table.java +4923 -0
  117. data/src/main/java/processing/data/TableRow.java +198 -0
  118. data/src/main/java/processing/data/XML.java +1149 -0
  119. data/src/main/java/processing/event/Event.java +108 -0
  120. data/src/main/java/processing/event/KeyEvent.java +70 -0
  121. data/src/main/java/processing/event/MouseEvent.java +149 -0
  122. data/src/main/java/processing/event/TouchEvent.java +57 -0
  123. data/src/main/java/processing/javafx/PGraphicsFX2D.java +354 -0
  124. data/src/main/java/processing/opengl/FontTexture.java +379 -0
  125. data/src/main/java/processing/opengl/FrameBuffer.java +503 -0
  126. data/src/main/java/processing/opengl/LinePath.java +623 -0
  127. data/src/main/java/processing/opengl/LineStroker.java +685 -0
  128. data/src/main/java/processing/opengl/PGL.java +3366 -0
  129. data/src/main/java/processing/opengl/PGraphics2D.java +615 -0
  130. data/src/main/java/processing/opengl/PGraphics3D.java +281 -0
  131. data/src/main/java/processing/opengl/PGraphicsOpenGL.java +13634 -0
  132. data/src/main/java/processing/opengl/PJOGL.java +1966 -0
  133. data/src/main/java/processing/opengl/PShader.java +1478 -0
  134. data/src/main/java/processing/opengl/PShapeOpenGL.java +5234 -0
  135. data/src/main/java/processing/opengl/PSurfaceJOGL.java +1315 -0
  136. data/src/main/java/processing/opengl/Texture.java +1670 -0
  137. data/src/main/java/processing/opengl/VertexBuffer.java +88 -0
  138. data/src/main/java/processing/opengl/cursors/arrow.png +0 -0
  139. data/src/main/java/processing/opengl/cursors/cross.png +0 -0
  140. data/src/main/java/processing/opengl/cursors/hand.png +0 -0
  141. data/src/main/java/processing/opengl/cursors/license.txt +27 -0
  142. data/src/main/java/processing/opengl/cursors/move.png +0 -0
  143. data/src/main/java/processing/opengl/cursors/text.png +0 -0
  144. data/src/main/java/processing/opengl/cursors/wait.png +0 -0
  145. data/src/main/java/processing/opengl/shaders/ColorFrag.glsl +32 -0
  146. data/src/main/java/processing/opengl/shaders/ColorVert.glsl +34 -0
  147. data/src/main/java/processing/opengl/shaders/LightFrag.glsl +33 -0
  148. data/src/main/java/processing/opengl/shaders/LightVert-vc4.glsl +154 -0
  149. data/src/main/java/processing/opengl/shaders/LightVert.glsl +151 -0
  150. data/src/main/java/processing/opengl/shaders/LineFrag.glsl +32 -0
  151. data/src/main/java/processing/opengl/shaders/LineVert.glsl +100 -0
  152. data/src/main/java/processing/opengl/shaders/MaskFrag.glsl +40 -0
  153. data/src/main/java/processing/opengl/shaders/PointFrag.glsl +32 -0
  154. data/src/main/java/processing/opengl/shaders/PointVert.glsl +56 -0
  155. data/src/main/java/processing/opengl/shaders/TexFrag.glsl +37 -0
  156. data/src/main/java/processing/opengl/shaders/TexLightFrag.glsl +37 -0
  157. data/src/main/java/processing/opengl/shaders/TexLightVert-vc4.glsl +160 -0
  158. data/src/main/java/processing/opengl/shaders/TexLightVert.glsl +157 -0
  159. data/src/main/java/processing/opengl/shaders/TexVert.glsl +38 -0
  160. data/src/main/resources/icon/icon-1024.png +0 -0
  161. data/src/main/resources/icon/icon-128.png +0 -0
  162. data/src/main/resources/icon/icon-16.png +0 -0
  163. data/src/main/resources/icon/icon-256.png +0 -0
  164. data/src/main/resources/icon/icon-32.png +0 -0
  165. data/src/main/resources/icon/icon-48.png +0 -0
  166. data/src/main/resources/icon/icon-512.png +0 -0
  167. data/src/main/resources/icon/icon-64.png +0 -0
  168. data/src/main/resources/license.txt +508 -0
  169. data/test/create_test.rb +68 -0
  170. data/test/deglut_spec_test.rb +24 -0
  171. data/test/helper_methods_test.rb +58 -0
  172. data/test/math_tool_test.rb +75 -0
  173. data/test/respond_to_test.rb +215 -0
  174. data/test/sketches/key_event.rb +37 -0
  175. data/test/sketches/library/my_library/my_library.rb +32 -0
  176. data/test/test_helper.rb +3 -0
  177. data/test/vecmath_spec_test.rb +522 -0
  178. data/vendors/Rakefile +127 -0
  179. metadata +289 -0
@@ -0,0 +1,3608 @@
1
+ /* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
+
3
+ /*
4
+ Part of the Processing project - http://processing.org
5
+
6
+ Copyright (c) 2004-14 Ben Fry and Casey Reas
7
+ Copyright (c) 2001-04 Massachusetts Institute of Technology
8
+
9
+ This library is free software; you can redistribute it and/or
10
+ modify it under the terms of the GNU Lesser General Public
11
+ License as published by the Free Software Foundation; either
12
+ version 2.1 of the License, or (at your option) any later version.
13
+
14
+ This library is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
+ Lesser General Public License for more details.
18
+
19
+ You should have received a copy of the GNU Lesser General
20
+ Public License along with this library; if not, write to the
21
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
22
+ Boston, MA 02111-1307 USA
23
+ */
24
+
25
+ package processing.core;
26
+
27
+ import java.awt.*;
28
+ import java.awt.image.*;
29
+ import java.io.*;
30
+ import java.util.Iterator;
31
+
32
+ import javax.imageio.*;
33
+ import javax.imageio.metadata.*;
34
+
35
+
36
+ /**
37
+ * ( begin auto-generated from PImage.xml )
38
+ *
39
+ * Datatype for storing images. Processing can display <b>.gif</b>,
40
+ * <b>.jpg</b>, <b>.tga</b>, and <b>.png</b> images. Images may be
41
+ * displayed in 2D and 3D space. Before an image is used, it must be loaded
42
+ * with the <b>loadImage()</b> function. The <b>PImage</b> class contains
43
+ * fields for the <b>width</b> and <b>height</b> of the image, as well as
44
+ * an array called <b>pixels[]</b> that contains the values for every pixel
45
+ * in the image. The methods described below allow easy access to the
46
+ * image's pixels and alpha channel and simplify the process of compositing.<br/>
47
+ * <br/> using the <b>pixels[]</b> array, be sure to use the
48
+ * <b>loadPixels()</b> method on the image to make sure that the pixel data
49
+ * is properly loaded.<br/>
50
+ * <br/> create a new image, use the <b>createImage()</b> function. Do not
51
+ * use the syntax <b>new PImage()</b>.
52
+ *
53
+ * ( end auto-generated )
54
+ *
55
+ * @webref image
56
+ * @usage Web &amp; Application
57
+ * @instanceName pimg any object of type PImage
58
+ * @see PApplet#loadImage(String)
59
+ * @see PApplet#imageMode(int)
60
+ * @see PApplet#createImage(int, int, int)
61
+ */
62
+ public class PImage implements PConstants, Cloneable {
63
+
64
+ /**
65
+ * Format for this image, one of RGB, ARGB or ALPHA.
66
+ * note that RGB images still require 0xff in the high byte
67
+ * because of how they'll be manipulated by other functions
68
+ */
69
+ public int format;
70
+
71
+ /**
72
+ * ( begin auto-generated from pixels.xml )
73
+ *
74
+ * Array containing the values for all the pixels in the display window.
75
+ * These values are of the color datatype. This array is the size of the
76
+ * display window. For example, if the image is 100x100 pixels, there will
77
+ * be 10000 values and if the window is 200x300 pixels, there will be 60000
78
+ * values. The <b>index</b> value defines the position of a value within
79
+ * the array. For example, the statement <b>color b = pixels[230]</b> will
80
+ * set the variable <b>b</b> to be equal to the value at that location in
81
+ * the array.<br />
82
+ * <br />
83
+ * Before accessing this array, the data must loaded with the
84
+ * <b>loadPixels()</b> function. After the array data has been modified,
85
+ * the <b>updatePixels()</b> function must be run to update the changes.
86
+ * Without <b>loadPixels()</b>, running the code may (or will in future
87
+ * releases) result in a NullPointerException.
88
+ *
89
+ * ( end auto-generated )
90
+ *
91
+ * @webref image:pixels
92
+ * @usage web_application
93
+ * @brief Array containing the color of every pixel in the image
94
+ */
95
+ public int[] pixels;
96
+
97
+ /** 1 for most images, 2 for hi-dpi/retina */
98
+ public int pixelDensity = 1;
99
+
100
+ /** Actual dimensions of pixels array, taking into account the 2x setting. */
101
+ public int pixelWidth;
102
+
103
+ /**
104
+ *
105
+ */
106
+ public int pixelHeight;
107
+
108
+ /**
109
+ * ( begin auto-generated from PImage_width.xml )
110
+ *
111
+ * The width of the image in units of pixels.
112
+ *
113
+ * ( end auto-generated )
114
+ * @webref pimage:field
115
+ * @usage web_application
116
+ * @brief Image width
117
+ */
118
+ public int width;
119
+
120
+ /**
121
+ * ( begin auto-generated from PImage_height.xml )
122
+ *
123
+ * The height of the image in units of pixels.
124
+ *
125
+ * ( end auto-generated )
126
+ * @webref pimage:field
127
+ * @usage web_application
128
+ * @brief Image height
129
+ */
130
+ public int height;
131
+
132
+ /**
133
+ * Path to parent object that will be used with save().
134
+ * This prevents users from needing savePath() to use PImage.save().
135
+ */
136
+ public PApplet parent;
137
+
138
+
139
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
140
+
141
+
142
+ /** modified portion of the image */
143
+ protected boolean modified;
144
+
145
+ /**
146
+ *
147
+ */
148
+ protected int mx1,
149
+
150
+ /**
151
+ *
152
+ */
153
+ my1,
154
+
155
+ /**
156
+ *
157
+ */
158
+ mx2,
159
+
160
+ /**
161
+ *
162
+ */
163
+ my2;
164
+
165
+ /** Loaded pixels flag */
166
+ public boolean loaded = false;
167
+
168
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
169
+
170
+
171
+ // private fields
172
+ private int fracU, ifU, fracV, ifV, u1, u2, v1, v2, sX, sY, iw, iw1, ih1;
173
+ private int ul, ll, ur, lr, cUL, cLL, cUR, cLR;
174
+ private int srcXOffset, srcYOffset;
175
+ private int r, g, b, a;
176
+ private int[] srcBuffer;
177
+
178
+ // fixed point precision is limited to 15 bits!!
179
+ static final int PRECISIONB = 15;
180
+ static final int PRECISIONF = 1 << PRECISIONB;
181
+ static final int PREC_MAXVAL = PRECISIONF-1;
182
+ static final int PREC_ALPHA_SHIFT = 24-PRECISIONB;
183
+ static final int PREC_RED_SHIFT = 16-PRECISIONB;
184
+
185
+ // internal kernel stuff for the gaussian blur filter
186
+ private int blurRadius;
187
+ private int blurKernelSize;
188
+ private int[] blurKernel;
189
+ private int[][] blurMult;
190
+
191
+ // colour component bitmasks (moved from PConstants in 2.0b7)
192
+
193
+ /**
194
+ *
195
+ */
196
+ public static final int ALPHA_MASK = 0xff000000;
197
+
198
+ /**
199
+ *
200
+ */
201
+ public static final int RED_MASK = 0x00ff0000;
202
+
203
+ /**
204
+ *
205
+ */
206
+ public static final int GREEN_MASK = 0x0000ff00;
207
+
208
+ /**
209
+ *
210
+ */
211
+ public static final int BLUE_MASK = 0x000000ff;
212
+
213
+
214
+ //////////////////////////////////////////////////////////////
215
+
216
+
217
+ /**
218
+ * ( begin auto-generated from PImage.xml )
219
+ *
220
+ * Datatype for storing images. Processing can display <b>.gif</b>,
221
+ * <b>.jpg</b>, <b>.tga</b>, and <b>.png</b> images. Images may be
222
+ * displayed in 2D and 3D space. Before an image is used, it must be loaded
223
+ * with the <b>loadImage()</b> function. The <b>PImage</b> object contains
224
+ * fields for the <b>width</b> and <b>height</b> of the image, as well as
225
+ * an array called <b>pixels[]</b> which contains the values for every
226
+ * pixel in the image. A group of methods, described below, allow easy
227
+ * access to the image's pixels and alpha channel and simplify the process
228
+ * of compositing.
229
+ * <br/> <br/>
230
+ * Before using the <b>pixels[]</b> array, be sure to use the
231
+ * <b>loadPixels()</b> method on the image to make sure that the pixel data
232
+ * is properly loaded.
233
+ * <br/> <br/>
234
+ * To create a new image, use the <b>createImage()</b> function (do not use
235
+ * <b>new PImage()</b>).
236
+ * ( end auto-generated )
237
+ * @nowebref
238
+ * @usage web_application
239
+ * @see PApplet#loadImage(String, String)
240
+ * @see PApplet#imageMode(int)
241
+ * @see PApplet#createImage(int, int, int)
242
+ */
243
+ public PImage() {
244
+ format = ARGB; // default to ARGB images for release 0116
245
+ pixelDensity = 1;
246
+ }
247
+
248
+
249
+ /**
250
+ * @nowebref
251
+ * @param width image width
252
+ * @param height image height
253
+ */
254
+ public PImage(int width, int height) {
255
+ init(width, height, RGB, 1);
256
+
257
+ // toxi: is it maybe better to init the image with max alpha enabled?
258
+ //for(int i=0; i<pixels.length; i++) pixels[i]=0xffffffff;
259
+ // fry: i'm opting for the full transparent image, which is how
260
+ // photoshop works, and our audience oughta be familiar with.
261
+ // also, i want to avoid having to set all those pixels since
262
+ // in java it's super slow, and most using this fxn will be
263
+ // setting all the pixels anyway.
264
+ // toxi: agreed and same reasons why i left it out ;)
265
+ }
266
+
267
+
268
+ /**
269
+ * @param width
270
+ * @param height
271
+ * @nowebref
272
+ * @param format Either RGB, ARGB, ALPHA (grayscale alpha channel)
273
+ */
274
+ public PImage(int width, int height, int format) {
275
+ init(width, height, format, 1);
276
+ }
277
+
278
+ /**
279
+ *
280
+ * @param width
281
+ * @param height
282
+ * @param format
283
+ * @param factor
284
+ */
285
+ public PImage(int width, int height, int format, int factor) {
286
+ init(width, height, format, factor);
287
+ }
288
+
289
+
290
+ /**
291
+ * Do not remove, see notes in the other variant.
292
+ * @param width
293
+ * @param height
294
+ * @param format
295
+ */
296
+ public void init(int width, int height, int format) { // ignore
297
+ init(width, height, format, 1);
298
+ }
299
+
300
+
301
+ /**
302
+ * Function to be used by subclasses of PImage to init later than
303
+ * at the constructor, or re-init later when things changes.
304
+ * Used by Capture and Movie classes (and perhaps others),
305
+ * because the width/height will not be known when super() is called.
306
+ * (Leave this public so that other libraries can do the same.)
307
+ * @param width
308
+ * @param factor
309
+ * @param height
310
+ * @param format
311
+ */
312
+ public void init(int width, int height, int format, int factor) { // ignore
313
+ this.width = width;
314
+ this.height = height;
315
+ this.format = format;
316
+ this.pixelDensity = factor;
317
+
318
+ pixelWidth = width * pixelDensity;
319
+ pixelHeight = height * pixelDensity;
320
+ this.pixels = new int[pixelWidth * pixelHeight];
321
+ }
322
+
323
+
324
+ /**
325
+ * Check the alpha on an image, using a really primitive loop.
326
+ */
327
+ protected void checkAlpha() {
328
+ if (pixels == null) return;
329
+
330
+ for (int i = 0; i < pixels.length; i++) {
331
+ // since transparency is often at corners, hopefully this
332
+ // will find a non-transparent pixel quickly and exit
333
+ if ((pixels[i] & 0xff000000) != 0xff000000) {
334
+ format = ARGB;
335
+ break;
336
+ }
337
+ }
338
+ }
339
+
340
+
341
+ //////////////////////////////////////////////////////////////
342
+
343
+
344
+ /**
345
+ * Construct a new PImage from a java.awt.Image. This constructor assumes
346
+ * that you've done the work of making sure a MediaTracker has been used
347
+ * to fully download the data and that the img is valid.
348
+ *
349
+ * @nowebref
350
+ * @param img assumes a MediaTracker has been used to fully download
351
+ * the data and the img is valid
352
+ */
353
+ public PImage(Image img) {
354
+ format = RGB;
355
+ if (img instanceof BufferedImage) {
356
+ BufferedImage bi = (BufferedImage) img;
357
+ width = bi.getWidth();
358
+ height = bi.getHeight();
359
+ int type = bi.getType();
360
+ if (type == BufferedImage.TYPE_3BYTE_BGR ||
361
+ type == BufferedImage.TYPE_4BYTE_ABGR) {
362
+ pixels = new int[width * height];
363
+ bi.getRGB(0, 0, width, height, pixels, 0, width);
364
+ if (type == BufferedImage.TYPE_4BYTE_ABGR) {
365
+ format = ARGB;
366
+ } else {
367
+ opaque();
368
+ }
369
+ } else {
370
+ DataBuffer db = bi.getRaster().getDataBuffer();
371
+ if (db instanceof DataBufferInt) {
372
+ pixels = ((DataBufferInt) db).getData();
373
+ if (type == BufferedImage.TYPE_INT_ARGB) {
374
+ format = ARGB;
375
+ } else if (type == BufferedImage.TYPE_INT_RGB) {
376
+ opaque();
377
+ }
378
+ }
379
+ }
380
+ }
381
+ // Implements fall-through if not DataBufferInt above, or not a
382
+ // known type, or not DataBufferInt for the data itself.
383
+ if (pixels == null) { // go the old school Java 1.0 route
384
+ width = img.getWidth(null);
385
+ height = img.getHeight(null);
386
+ pixels = new int[width * height];
387
+ PixelGrabber pg =
388
+ new PixelGrabber(img, 0, 0, width, height, pixels, 0, width);
389
+ try {
390
+ pg.grabPixels();
391
+ } catch (InterruptedException e) { }
392
+ }
393
+ pixelDensity = 1;
394
+ pixelWidth = width;
395
+ pixelHeight = height;
396
+ }
397
+
398
+
399
+ /**
400
+ * Use the getNative() method instead, which allows library interfaces to be
401
+ * written in a cross-platform fashion for desktop, Android, and others.
402
+ * This is still included for PGraphics objects, which may need the image.
403
+ * @return
404
+ */
405
+ public Image getImage() { // ignore
406
+ return (Image) getNative();
407
+ }
408
+
409
+
410
+ /**
411
+ * Returns a native BufferedImage from this PImage.
412
+ * @return
413
+ */
414
+ public Object getNative() { // ignore
415
+ loadPixels();
416
+ int type = (format == RGB) ?
417
+ BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
418
+ BufferedImage image = new BufferedImage(pixelWidth, pixelHeight, type);
419
+ WritableRaster wr = image.getRaster();
420
+ wr.setDataElements(0, 0, pixelWidth, pixelHeight, pixels);
421
+ return image;
422
+ }
423
+
424
+
425
+ //////////////////////////////////////////////////////////////
426
+
427
+ // MARKING IMAGE AS MODIFIED / FOR USE w/ GET/SET
428
+
429
+ /**
430
+ *
431
+ * @return
432
+ */
433
+
434
+
435
+ public boolean isModified() { // ignore
436
+ return modified;
437
+ }
438
+
439
+ /**
440
+ *
441
+ */
442
+ public void setModified() { // ignore
443
+ modified = true;
444
+ mx1 = 0;
445
+ my1 = 0;
446
+ mx2 = pixelWidth;
447
+ my2 = pixelHeight;
448
+ }
449
+
450
+ /**
451
+ *
452
+ * @param m
453
+ */
454
+ public void setModified(boolean m) { // ignore
455
+ modified = m;
456
+ }
457
+
458
+ /**
459
+ *
460
+ * @return
461
+ */
462
+ public int getModifiedX1() { // ignore
463
+ return mx1;
464
+ }
465
+
466
+ /**
467
+ *
468
+ * @return
469
+ */
470
+ public int getModifiedX2() { // ignore
471
+ return mx2;
472
+ }
473
+
474
+ /**
475
+ *
476
+ * @return
477
+ */
478
+ public int getModifiedY1() { // ignore
479
+ return my1;
480
+ }
481
+
482
+ /**
483
+ *
484
+ * @return
485
+ */
486
+ public int getModifiedY2() { // ignore
487
+ return my2;
488
+ }
489
+
490
+
491
+ /**
492
+ * ( begin auto-generated from PImage_loadPixels.xml )
493
+ *
494
+ * Loads the pixel data for the image into its <b>pixels[]</b> array. This
495
+ * function must always be called before reading from or writing to <b>pixels[]</b>.
496
+ * <br/><br/> renderers may or may not seem to require <b>loadPixels()</b>
497
+ * or <b>updatePixels()</b>. However, the rule is that any time you want to
498
+ * manipulate the <b>pixels[]</b> array, you must first call
499
+ * <b>loadPixels()</b>, and after changes have been made, call
500
+ * <b>updatePixels()</b>. Even if the renderer may not seem to use this
501
+ * function in the current Processing release, this will always be subject
502
+ * to change.
503
+ *
504
+ * ( end auto-generated )
505
+ *
506
+ * <h3>Advanced</h3>
507
+ * Call this when you want to mess with the pixels[] array.
508
+ * <p/>
509
+ * For subclasses where the pixels[] buffer isn't set by default,
510
+ * this should copy all data into the pixels[] array
511
+ *
512
+ * @webref pimage:pixels
513
+ * @brief Loads the pixel data for the image into its pixels[] array
514
+ * @usage web_application
515
+ */
516
+ public void loadPixels() { // ignore
517
+ if (pixels == null || pixels.length != pixelWidth*pixelHeight) {
518
+ pixels = new int[pixelWidth*pixelHeight];
519
+ }
520
+ setLoaded();
521
+ }
522
+
523
+ /**
524
+ *
525
+ */
526
+ public void updatePixels() { // ignore
527
+ updatePixels(0, 0, pixelWidth, pixelHeight);
528
+ }
529
+
530
+
531
+ /**
532
+ * ( begin auto-generated from PImage_updatePixels.xml )
533
+ *
534
+ * Updates the image with the data in its <b>pixels[]</b> array. Use in
535
+ * conjunction with <b>loadPixels()</b>. If you're only reading pixels from
536
+ * the array, there's no need to call <b>updatePixels()</b>.
537
+ * <br/><br/> renderers may or may not seem to require <b>loadPixels()</b>
538
+ * or <b>updatePixels()</b>. However, the rule is that any time you want to
539
+ * manipulate the <b>pixels[]</b> array, you must first call
540
+ * <b>loadPixels()</b>, and after changes have been made, call
541
+ * <b>updatePixels()</b>. Even if the renderer may not seem to use this
542
+ * function in the current Processing release, this will always be subject
543
+ * to change.
544
+ * <br/> <br/>
545
+ * Currently, none of the renderers use the additional parameters to
546
+ * <b>updatePixels()</b>, however this may be implemented in the future.
547
+ *
548
+ * ( end auto-generated )
549
+ * <h3>Advanced</h3>
550
+ * Mark the pixels in this region as needing an update.
551
+ * This is not currently used by any of the renderers, however the api
552
+ * is structured this way in the hope of being able to use this to
553
+ * speed things up in the future.
554
+ * @webref pimage:pixels
555
+ * @brief Updates the image with the data in its pixels[] array
556
+ * @usage web_application
557
+ * @param x x-coordinate of the upper-left corner
558
+ * @param y y-coordinate of the upper-left corner
559
+ * @param w width
560
+ * @param h height
561
+ */
562
+ public void updatePixels(int x, int y, int w, int h) { // ignore
563
+ int x2 = x + w;
564
+ int y2 = y + h;
565
+
566
+ if (!modified) {
567
+ mx1 = PApplet.max(0, x);
568
+ mx2 = PApplet.min(pixelWidth, x2);
569
+ my1 = PApplet.max(0, y);
570
+ my2 = PApplet.min(pixelHeight, y2);
571
+ modified = true;
572
+
573
+ } else {
574
+ if (x < mx1) mx1 = PApplet.max(0, x);
575
+ if (x > mx2) mx2 = PApplet.min(pixelWidth, x);
576
+ if (y < my1) my1 = PApplet.max(0, y);
577
+ if (y > my2) my2 = PApplet.min(pixelHeight, y);
578
+
579
+ if (x2 < mx1) mx1 = PApplet.max(0, x2);
580
+ if (x2 > mx2) mx2 = PApplet.min(pixelWidth, x2);
581
+ if (y2 < my1) my1 = PApplet.max(0, y2);
582
+ if (y2 > my2) my2 = PApplet.min(pixelHeight, y2);
583
+ }
584
+ }
585
+
586
+
587
+ //////////////////////////////////////////////////////////////
588
+
589
+ // COPYING IMAGE DATA
590
+
591
+
592
+ /**
593
+ * Duplicate an image, returns new PImage object.
594
+ * The pixels[] array for the new object will be unique
595
+ * and recopied from the source image. This is implemented as an
596
+ * override of Object.clone(). We recommend using get() instead,
597
+ * because it prevents you from needing to catch the
598
+ * CloneNotSupportedException, and from doing a cast from the result.
599
+ * @return
600
+ * @throws java.lang.CloneNotSupportedException
601
+ */
602
+ @Override
603
+ public Object clone() throws CloneNotSupportedException { // ignore
604
+ return get();
605
+ }
606
+
607
+
608
+ /**
609
+ * ( begin auto-generated from PImage_resize.xml )
610
+ *
611
+ * Resize the image to a new width and height. To make the image scale
612
+ * proportionally, use 0 as the value for the <b>wide</b> or <b>high</b>
613
+ * parameter. For instance, to make the width of an image 150 pixels, and
614
+ * change the height using the same proportion, use resize(150, 0).<br />
615
+ * <br />
616
+ * Even though a PGraphics is technically a PImage, it is not possible to
617
+ * rescale the image data found in a PGraphics. (It's simply not possible
618
+ * to do this consistently across renderers: technically infeasible with
619
+ * P3D, or what would it even do with PDF?) If you want to resize PGraphics
620
+ * content, first get a copy of its image data using the <b>get()</b>
621
+ * method, and call <b>resize()</b> on the PImage that is returned.
622
+ *
623
+ * ( end auto-generated )
624
+ * @webref pimage:method
625
+ * @brief Changes the size of an image to a new width and height
626
+ * @usage web_application
627
+ * @param w the resized image width
628
+ * @param h the resized image height
629
+ * @see PImage#get(int, int, int, int)
630
+ */
631
+ public void resize(int w, int h) { // ignore
632
+ if (w <= 0 && h <= 0) {
633
+ throw new IllegalArgumentException("width or height must be > 0 for resize");
634
+ }
635
+
636
+ if (w == 0) { // Use height to determine relative size
637
+ float diff = (float) h / (float) height;
638
+ w = (int) (width * diff);
639
+ } else if (h == 0) { // Use the width to determine relative size
640
+ float diff = (float) w / (float) width;
641
+ h = (int) (height * diff);
642
+ }
643
+
644
+ BufferedImage img =
645
+ shrinkImage((BufferedImage) getNative(), w*pixelDensity, h*pixelDensity);
646
+
647
+ PImage temp = new PImage(img);
648
+ this.pixelWidth = temp.width;
649
+ this.pixelHeight = temp.height;
650
+
651
+ // Get the resized pixel array
652
+ this.pixels = temp.pixels;
653
+
654
+ this.width = pixelWidth / pixelDensity;
655
+ this.height = pixelHeight / pixelDensity;
656
+
657
+ // Mark the pixels array as altered
658
+ updatePixels();
659
+ }
660
+
661
+
662
+ // Adapted from getFasterScaledInstance() method from page 111 of
663
+ // "Filthy Rich Clients" by Chet Haase and Romain Guy
664
+ // Additional modifications and simplifications have been added,
665
+ // plus a fix to deal with an infinite loop if images are expanded.
666
+ // http://code.google.com/p/processing/issues/detail?id=1463
667
+ static private BufferedImage shrinkImage(BufferedImage img,
668
+ int targetWidth, int targetHeight) {
669
+ int type = (img.getTransparency() == Transparency.OPAQUE) ?
670
+ BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
671
+ BufferedImage outgoing = img;
672
+ BufferedImage scratchImage = null;
673
+ Graphics2D g2 = null;
674
+ int prevW = outgoing.getWidth();
675
+ int prevH = outgoing.getHeight();
676
+ boolean isTranslucent = img.getTransparency() != Transparency.OPAQUE;
677
+
678
+ // Use multi-step technique: start with original size, then scale down in
679
+ // multiple passes with drawImage() until the target size is reached
680
+ int w = img.getWidth();
681
+ int h = img.getHeight();
682
+
683
+ do {
684
+ if (w > targetWidth) {
685
+ w /= 2;
686
+ // if this is the last step, do the exact size
687
+ if (w < targetWidth) {
688
+ w = targetWidth;
689
+ }
690
+ } else if (targetWidth >= w) {
691
+ w = targetWidth;
692
+ }
693
+ if (h > targetHeight) {
694
+ h /= 2;
695
+ if (h < targetHeight) {
696
+ h = targetHeight;
697
+ }
698
+ } else if (targetHeight >= h) {
699
+ h = targetHeight;
700
+ }
701
+ if (scratchImage == null || isTranslucent) {
702
+ // Use a single scratch buffer for all iterations and then copy
703
+ // to the final, correctly-sized image before returning
704
+ scratchImage = new BufferedImage(w, h, type);
705
+ g2 = scratchImage.createGraphics();
706
+ }
707
+ g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
708
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
709
+ g2.drawImage(outgoing, 0, 0, w, h, 0, 0, prevW, prevH, null);
710
+ prevW = w;
711
+ prevH = h;
712
+ outgoing = scratchImage;
713
+ } while (w != targetWidth || h != targetHeight);
714
+
715
+ if (g2 != null) {
716
+ g2.dispose();
717
+ }
718
+
719
+ // If we used a scratch buffer that is larger than our target size,
720
+ // create an image of the right size and copy the results into it
721
+ if (targetWidth != outgoing.getWidth() ||
722
+ targetHeight != outgoing.getHeight()) {
723
+ scratchImage = new BufferedImage(targetWidth, targetHeight, type);
724
+ g2 = scratchImage.createGraphics();
725
+ g2.drawImage(outgoing, 0, 0, null);
726
+ g2.dispose();
727
+ outgoing = scratchImage;
728
+ }
729
+ return outgoing;
730
+ }
731
+
732
+
733
+ //////////////////////////////////////////////////////////////
734
+
735
+ // MARKING IMAGE AS LOADED / FOR USE IN RENDERERS
736
+
737
+ /**
738
+ *
739
+ * @return
740
+ */
741
+
742
+
743
+ public boolean isLoaded() { // ignore
744
+ return loaded;
745
+ }
746
+
747
+ /**
748
+ *
749
+ */
750
+ public void setLoaded() { // ignore
751
+ loaded = true;
752
+ }
753
+
754
+ /**
755
+ *
756
+ * @param l
757
+ */
758
+ public void setLoaded(boolean l) { // ignore
759
+ loaded = l;
760
+ }
761
+
762
+
763
+ //////////////////////////////////////////////////////////////
764
+
765
+ // GET/SET PIXELS
766
+
767
+
768
+ /**
769
+ * ( begin auto-generated from PImage_get.xml )
770
+ *
771
+ * Reads the color of any pixel or grabs a section of an image. If no
772
+ * parameters are specified, the entire image is returned. Use the <b>x</b>
773
+ * and <b>y</b> parameters to get the value of one pixel. Get a section of
774
+ * the display window by specifying an additional <b>width</b> and
775
+ * <b>height</b> parameter. When getting an image, the <b>x</b> and
776
+ * <b>y</b> parameters define the coordinates for the upper-left corner of
777
+ * the image, regardless of the current <b>imageMode()</b>.<br />
778
+ * <br />
779
+ * If the pixel requested is outside of the image window, black is
780
+ * returned. The numbers returned are scaled according to the current color
781
+ * ranges, but only RGB values are returned by this function. For example,
782
+ * even though you may have drawn a shape with <b>colorMode(HSB)</b>, the
783
+ * numbers returned will be in RGB format.<br />
784
+ * <br />
785
+ * Getting the color of a single pixel with <b>get(x, y)</b> is easy, but
786
+ * not as fast as grabbing the data directly from <b>pixels[]</b>. The
787
+ * equivalent statement to <b>get(x, y)</b> using <b>pixels[]</b> is
788
+ * <b>pixels[y*width+x]</b>. See the reference for <b>pixels[]</b> for more information.
789
+ *
790
+ * ( end auto-generated )
791
+ *
792
+ * <h3>Advanced</h3>
793
+ * Returns an ARGB "color" type (a packed 32 bit int with the color.
794
+ * If the coordinate is outside the image, zero is returned
795
+ * (black, but completely transparent).
796
+ * <P>
797
+ * If the image is in RGB format (i.e. on a PVideo object),
798
+ * the value will get its high bits set, just to avoid cases where
799
+ * they haven't been set already.
800
+ * <P>
801
+ * If the image is in ALPHA format, this returns a white with its
802
+ * alpha value set.
803
+ * <P>
804
+ * This function is included primarily for beginners. It is quite
805
+ * slow because it has to check to see if the x, y that was provided
806
+ * is inside the bounds, and then has to check to see what image
807
+ * type it is. If you want things to be more efficient, access the
808
+ * pixels[] array directly.
809
+ *
810
+ * @return
811
+ * @webref image:pixels
812
+ * @brief Reads the color of any pixel or grabs a rectangle of pixels
813
+ * @usage web_application
814
+ * @param x x-coordinate of the pixel
815
+ * @param y y-coordinate of the pixel
816
+ * @see PApplet#set(int, int, int)
817
+ * @see PApplet#pixels
818
+ * @see PApplet#copy(PImage, int, int, int, int, int, int, int, int)
819
+ */
820
+ public int get(int x, int y) {
821
+ if ((x < 0) || (y < 0) || (x >= pixelWidth) || (y >= pixelHeight)) return 0;
822
+
823
+ switch (format) {
824
+ case RGB:
825
+ return pixels[y*pixelWidth + x] | 0xff000000;
826
+
827
+ case ARGB:
828
+ return pixels[y*pixelWidth + x];
829
+
830
+ case ALPHA:
831
+ return (pixels[y*pixelWidth + x] << 24) | 0xffffff;
832
+ }
833
+ return 0;
834
+ }
835
+
836
+
837
+ /**
838
+ * @param x
839
+ * @param y
840
+ * @param w width of pixel rectangle to get
841
+ * @param h height of pixel rectangle to get
842
+ * @return
843
+ */
844
+ public PImage get(int x, int y, int w, int h) {
845
+ int targetX = 0;
846
+ int targetY = 0;
847
+ int targetWidth = w;
848
+ int targetHeight = h;
849
+ boolean cropped = false;
850
+
851
+ if (x < 0) {
852
+ w += x; // x is negative, removes the left edge from the width
853
+ targetX = -x;
854
+ cropped = true;
855
+ x = 0;
856
+ }
857
+ if (y < 0) {
858
+ h += y; // y is negative, clip the number of rows
859
+ targetY = -y;
860
+ cropped = true;
861
+ y = 0;
862
+ }
863
+
864
+ if (x + w > pixelWidth) {
865
+ w = pixelWidth - x;
866
+ cropped = true;
867
+ }
868
+ if (y + h > pixelHeight) {
869
+ h = pixelHeight - y;
870
+ cropped = true;
871
+ }
872
+
873
+ if (w < 0) {
874
+ w = 0;
875
+ }
876
+ if (h < 0) {
877
+ h = 0;
878
+ }
879
+
880
+ int targetFormat = format;
881
+ if (cropped && format == RGB) {
882
+ targetFormat = ARGB;
883
+ }
884
+
885
+ PImage target = new PImage(targetWidth / pixelDensity,
886
+ targetHeight / pixelDensity,
887
+ targetFormat, pixelDensity);
888
+ target.parent = parent; // parent may be null so can't use createImage()
889
+ if (w > 0 && h > 0) {
890
+ getImpl(x, y, w, h, target, targetX, targetY);
891
+ }
892
+ return target;
893
+ }
894
+
895
+
896
+ /**
897
+ * Returns a copy of this PImage. Equivalent to get(0, 0, width, height).
898
+ * Deprecated, just use copy() instead.
899
+ * @return
900
+ */
901
+ public PImage get() {
902
+ // Formerly this used clone(), which caused memory problems.
903
+ // http://code.google.com/p/processing/issues/detail?id=42
904
+ return get(0, 0, pixelWidth, pixelHeight);
905
+ }
906
+
907
+ /**
908
+ *
909
+ * @return
910
+ */
911
+ public PImage copy() {
912
+ return get(0, 0, pixelWidth, pixelHeight);
913
+ }
914
+
915
+
916
+ /**
917
+ * Internal function to actually handle getting a block of pixels that
918
+ * has already been properly cropped to a valid region. That is, x/y/w/h
919
+ * are guaranteed to be inside the image space, so the implementation can
920
+ * use the fastest possible pixel copying method.
921
+ * @param sourceX
922
+ * @param sourceY
923
+ * @param sourceWidth
924
+ * @param target
925
+ * @param sourceHeight
926
+ * @param targetX
927
+ * @param targetY
928
+ */
929
+ protected void getImpl(int sourceX, int sourceY,
930
+ int sourceWidth, int sourceHeight,
931
+ PImage target, int targetX, int targetY) {
932
+ int sourceIndex = sourceY*pixelWidth + sourceX;
933
+ int targetIndex = targetY*target.pixelWidth + targetX;
934
+ for (int row = 0; row < sourceHeight; row++) {
935
+ System.arraycopy(pixels, sourceIndex, target.pixels, targetIndex, sourceWidth);
936
+ sourceIndex += pixelWidth;
937
+ targetIndex += target.pixelWidth;
938
+ }
939
+ }
940
+
941
+
942
+ /**
943
+ * ( begin auto-generated from PImage_set.xml )
944
+ *
945
+ * Changes the color of any pixel or writes an image directly into the
946
+ * display window.<br />
947
+ * <br />
948
+ * The <b>x</b> and <b>y</b> parameters specify the pixel to change and the
949
+ * <b>color</b> parameter specifies the color value. The color parameter is
950
+ * affected by the current color mode (the default is RGB values from 0 to
951
+ * 255). When setting an image, the <b>x</b> and <b>y</b> parameters define
952
+ * the coordinates for the upper-left corner of the image, regardless of
953
+ * the current <b>imageMode()</b>.
954
+ * <br /><br />
955
+ * Setting the color of a single pixel with <b>set(x, y)</b> is easy, but
956
+ * not as fast as putting the data directly into <b>pixels[]</b>. The
957
+ * equivalent statement to <b>set(x, y, #000000)</b> using <b>pixels[]</b>
958
+ * is <b>pixels[y*width+x] = #000000</b>. See the reference for
959
+ * <b>pixels[]</b> for more information.
960
+ *
961
+ * ( end auto-generated )
962
+ *
963
+ * @webref image:pixels
964
+ * @brief writes a color to any pixel or writes an image into another
965
+ * @usage web_application
966
+ * @param x x-coordinate of the pixel
967
+ * @param y y-coordinate of the pixel
968
+ * @param c any value of the color datatype
969
+ * @see PImage#get(int, int, int, int)
970
+ * @see PImage#pixels
971
+ * @see PImage#copy(PImage, int, int, int, int, int, int, int, int)
972
+ */
973
+ public void set(int x, int y, int c) {
974
+ if ((x < 0) || (y < 0) || (x >= pixelWidth) || (y >= pixelHeight)) return;
975
+ pixels[y*pixelWidth + x] = c;
976
+ updatePixels(x, y, 1, 1); // slow...
977
+ }
978
+
979
+
980
+ /**
981
+ * <h3>Advanced</h3>
982
+ * Efficient method of drawing an image's pixels directly to this surface.
983
+ * No variations are employed, meaning that any scale, tint, or imageMode
984
+ * settings will be ignored.
985
+ *
986
+ * @param x
987
+ * @param img image to copy into the original image
988
+ * @param y
989
+ */
990
+ public void set(int x, int y, PImage img) {
991
+ int sx = 0;
992
+ int sy = 0;
993
+ int sw = img.pixelWidth;
994
+ int sh = img.pixelHeight;
995
+
996
+ if (x < 0) { // off left edge
997
+ sx -= x;
998
+ sw += x;
999
+ x = 0;
1000
+ }
1001
+ if (y < 0) { // off top edge
1002
+ sy -= y;
1003
+ sh += y;
1004
+ y = 0;
1005
+ }
1006
+ if (x + sw > pixelWidth) { // off right edge
1007
+ sw = pixelWidth - x;
1008
+ }
1009
+ if (y + sh > pixelHeight) { // off bottom edge
1010
+ sh = pixelHeight - y;
1011
+ }
1012
+
1013
+ // this could be nonexistent
1014
+ if ((sw <= 0) || (sh <= 0)) return;
1015
+
1016
+ setImpl(img, sx, sy, sw, sh, x, y);
1017
+ }
1018
+
1019
+
1020
+ /**
1021
+ * Internal function to actually handle setting a block of pixels that
1022
+ * has already been properly cropped from the image to a valid region.
1023
+ * @param sourceImage
1024
+ * @param sourceX
1025
+ * @param targetY
1026
+ * @param sourceHeight
1027
+ * @param sourceY
1028
+ * @param sourceWidth
1029
+ * @param targetX
1030
+ */
1031
+ protected void setImpl(PImage sourceImage,
1032
+ int sourceX, int sourceY,
1033
+ int sourceWidth, int sourceHeight,
1034
+ int targetX, int targetY) {
1035
+ int sourceOffset = sourceY * sourceImage.pixelWidth + sourceX;
1036
+ int targetOffset = targetY * pixelWidth + targetX;
1037
+
1038
+ for (int y = sourceY; y < sourceY + sourceHeight; y++) {
1039
+ System.arraycopy(sourceImage.pixels, sourceOffset, pixels, targetOffset, sourceWidth);
1040
+ sourceOffset += sourceImage.pixelWidth;
1041
+ targetOffset += pixelWidth;
1042
+ }
1043
+
1044
+ //updatePixelsImpl(targetX, targetY, sourceWidth, sourceHeight);
1045
+ updatePixels(targetX, targetY, sourceWidth, sourceHeight);
1046
+ }
1047
+
1048
+
1049
+
1050
+ //////////////////////////////////////////////////////////////
1051
+
1052
+ // ALPHA CHANNEL
1053
+
1054
+
1055
+ /**
1056
+ * @param maskArray array of integers used as the alpha channel, needs to be
1057
+ * the same length as the image's pixel array.
1058
+ */
1059
+ public void mask(int maskArray[]) { // ignore
1060
+ loadPixels();
1061
+ // don't execute if mask image is different size
1062
+ if (maskArray.length != pixels.length) {
1063
+ throw new IllegalArgumentException("mask() can only be used with an image that's the same size.");
1064
+ }
1065
+ for (int i = 0; i < pixels.length; i++) {
1066
+ pixels[i] = ((maskArray[i] & 0xff) << 24) | (pixels[i] & 0xffffff);
1067
+ }
1068
+ format = ARGB;
1069
+ updatePixels();
1070
+ }
1071
+
1072
+
1073
+ /**
1074
+ * ( begin auto-generated from PImage_mask.xml )
1075
+ *
1076
+ * Masks part of an image from displaying by loading another image and
1077
+ * using it as an alpha channel. This mask image should only contain
1078
+ * grayscale data, but only the blue color channel is used. The mask image
1079
+ * needs to be the same size as the image to which it is applied.<br />
1080
+ * <br />
1081
+ * In addition to using a mask image, an integer array containing the alpha
1082
+ * channel data can be specified directly. This method is useful for
1083
+ * creating dynamically generated alpha masks. This array must be of the
1084
+ * same length as the target image's pixels array and should contain only
1085
+ * grayscale data of values between 0-255.
1086
+ *
1087
+ * ( end auto-generated )
1088
+ *
1089
+ * <h3>Advanced</h3>
1090
+ *
1091
+ * Set alpha channel for an image. Black colors in the source
1092
+ * image will make the destination image completely transparent,
1093
+ * and white will make things fully opaque. Gray values will
1094
+ * be in-between steps.
1095
+ * <P>
1096
+ * Strictly speaking the "blue" value from the source image is
1097
+ * used as the alpha color. For a fully grayscale image, this
1098
+ * is correct, but for a color image it's not 100% accurate.
1099
+ * For a more accurate conversion, first use filter(GRAY)
1100
+ * which will make the image into a "correct" grayscale by
1101
+ * performing a proper luminance-based conversion.
1102
+ *
1103
+ * @webref pimage:method
1104
+ * @usage web_application
1105
+ * @param img image to use as the mask
1106
+ * @brief Masks part of an image with another image as an alpha channel
1107
+ */
1108
+ public void mask(PImage img) {
1109
+ img.loadPixels();
1110
+ mask(img.pixels);
1111
+ }
1112
+
1113
+
1114
+
1115
+ //////////////////////////////////////////////////////////////
1116
+
1117
+ // IMAGE FILTERS
1118
+
1119
+ /**
1120
+ *
1121
+ * @param kind
1122
+ */
1123
+
1124
+
1125
+ public void filter(int kind) {
1126
+ loadPixels();
1127
+
1128
+ switch (kind) {
1129
+ case BLUR:
1130
+ // TODO write basic low-pass filter blur here
1131
+ // what does photoshop do on the edges with this guy?
1132
+ // better yet.. why bother? just use gaussian with radius 1
1133
+ filter(BLUR, 1);
1134
+ break;
1135
+
1136
+ case GRAY:
1137
+ if (format == ALPHA) {
1138
+ // for an alpha image, convert it to an opaque grayscale
1139
+ for (int i = 0; i < pixels.length; i++) {
1140
+ int col = 255 - pixels[i];
1141
+ pixels[i] = 0xff000000 | (col << 16) | (col << 8) | col;
1142
+ }
1143
+ format = RGB;
1144
+
1145
+ } else {
1146
+ // Converts RGB image data into grayscale using
1147
+ // weighted RGB components, and keeps alpha channel intact.
1148
+ // [toxi 040115]
1149
+ for (int i = 0; i < pixels.length; i++) {
1150
+ int col = pixels[i];
1151
+ // luminance = 0.3*red + 0.59*green + 0.11*blue
1152
+ // 0.30 * 256 = 77
1153
+ // 0.59 * 256 = 151
1154
+ // 0.11 * 256 = 28
1155
+ int lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8;
1156
+ pixels[i] = (col & ALPHA_MASK) | lum<<16 | lum<<8 | lum;
1157
+ }
1158
+ }
1159
+ break;
1160
+
1161
+ case INVERT:
1162
+ for (int i = 0; i < pixels.length; i++) {
1163
+ //pixels[i] = 0xff000000 |
1164
+ pixels[i] ^= 0xffffff;
1165
+ }
1166
+ break;
1167
+
1168
+ case POSTERIZE:
1169
+ throw new RuntimeException("Use filter(POSTERIZE, int levels) " +
1170
+ "instead of filter(POSTERIZE)");
1171
+
1172
+ case OPAQUE:
1173
+ for (int i = 0; i < pixels.length; i++) {
1174
+ pixels[i] |= 0xff000000;
1175
+ }
1176
+ format = RGB;
1177
+ break;
1178
+
1179
+ case THRESHOLD:
1180
+ filter(THRESHOLD, 0.5f);
1181
+ break;
1182
+
1183
+ // [toxi 050728] added new filters
1184
+ case ERODE:
1185
+ erode(); // former dilate(true);
1186
+ break;
1187
+
1188
+ case DILATE:
1189
+ dilate(); // former dilate(false);
1190
+ break;
1191
+ }
1192
+ updatePixels(); // mark as modified
1193
+ }
1194
+
1195
+
1196
+ /**
1197
+ * ( begin auto-generated from PImage_filter.xml )
1198
+ *
1199
+ * Filters an image as defined by one of the following modes:<br /><br
1200
+ * />THRESHOLD - converts the image to black and white pixels depending if
1201
+ * they are above or below the threshold defined by the level parameter.
1202
+ * The level must be between 0.0 (black) and 1.0(white). If no level is
1203
+ * specified, 0.5 is used.<br />
1204
+ * <br />
1205
+ * GRAY - converts any colors in the image to grayscale equivalents<br />
1206
+ * <br />
1207
+ * INVERT - sets each pixel to its inverse value<br />
1208
+ * <br />
1209
+ * POSTERIZE - limits each channel of the image to the number of colors
1210
+ * specified as the level parameter<br />
1211
+ * <br />
1212
+ * BLUR - executes a Guassian blur with the level parameter specifying the
1213
+ * extent of the blurring. If no level parameter is used, the blur is
1214
+ * equivalent to Guassian blur of radius 1<br />
1215
+ * <br />
1216
+ * OPAQUE - sets the alpha channel to entirely opaque<br />
1217
+ * <br />
1218
+ * ERODE - reduces the light areas with the amount defined by the level
1219
+ * parameter<br />
1220
+ * <br />
1221
+ * DILATE - increases the light areas with the amount defined by the level parameter
1222
+ *
1223
+ * ( end auto-generated )
1224
+ *
1225
+ * <h3>Advanced</h3>
1226
+ * Method to apply a variety of basic filters to this image.
1227
+ * <P>
1228
+ * <UL>
1229
+ * <LI>filter(BLUR) provides a basic blur.
1230
+ * <LI>filter(GRAY) converts the image to grayscale based on luminance.
1231
+ * <LI>filter(INVERT) will invert the color components in the image.
1232
+ * <LI>filter(OPAQUE) set all the high bits in the image to opaque
1233
+ * <LI>filter(THRESHOLD) converts the image to black and white.
1234
+ * <LI>filter(DILATE) grow white/light areas
1235
+ * <LI>filter(ERODE) shrink white/light areas
1236
+ * </UL>
1237
+ * Luminance conversion code contributed by
1238
+ * <A HREF="http://www.toxi.co.uk">toxi</A>
1239
+ * <P/>
1240
+ * Gaussian blur code contributed by
1241
+ * <A HREF="http://incubator.quasimondo.com">Mario Klingemann</A>
1242
+ *
1243
+ * @webref image:pixels
1244
+ * @brief Converts the image to grayscale or black and white
1245
+ * @usage web_application
1246
+ * @param kind Either THRESHOLD, GRAY, OPAQUE, INVERT, POSTERIZE, BLUR, ERODE, or DILATE
1247
+ * @param param unique for each, see above
1248
+ */
1249
+ public void filter(int kind, float param) {
1250
+ loadPixels();
1251
+
1252
+ switch (kind) {
1253
+ case BLUR:
1254
+ if (format == ALPHA)
1255
+ blurAlpha(param);
1256
+ else if (format == ARGB)
1257
+ blurARGB(param);
1258
+ else
1259
+ blurRGB(param);
1260
+ break;
1261
+
1262
+ case GRAY:
1263
+ throw new RuntimeException("Use filter(GRAY) instead of " +
1264
+ "filter(GRAY, param)");
1265
+
1266
+ case INVERT:
1267
+ throw new RuntimeException("Use filter(INVERT) instead of " +
1268
+ "filter(INVERT, param)");
1269
+
1270
+ case OPAQUE:
1271
+ throw new RuntimeException("Use filter(OPAQUE) instead of " +
1272
+ "filter(OPAQUE, param)");
1273
+
1274
+ case POSTERIZE:
1275
+ int levels = (int)param;
1276
+ if ((levels < 2) || (levels > 255)) {
1277
+ throw new RuntimeException("Levels must be between 2 and 255 for " +
1278
+ "filter(POSTERIZE, levels)");
1279
+ }
1280
+ int levels1 = levels - 1;
1281
+ for (int i = 0; i < pixels.length; i++) {
1282
+ int rlevel = (pixels[i] >> 16) & 0xff;
1283
+ int glevel = (pixels[i] >> 8) & 0xff;
1284
+ int blevel = pixels[i] & 0xff;
1285
+ rlevel = (((rlevel * levels) >> 8) * 255) / levels1;
1286
+ glevel = (((glevel * levels) >> 8) * 255) / levels1;
1287
+ blevel = (((blevel * levels) >> 8) * 255) / levels1;
1288
+ pixels[i] = ((0xff000000 & pixels[i]) |
1289
+ (rlevel << 16) |
1290
+ (glevel << 8) |
1291
+ blevel);
1292
+ }
1293
+ break;
1294
+
1295
+ case THRESHOLD: // greater than or equal to the threshold
1296
+ int thresh = (int) (param * 255);
1297
+ for (int i = 0; i < pixels.length; i++) {
1298
+ int max = Math.max((pixels[i] & RED_MASK) >> 16,
1299
+ Math.max((pixels[i] & GREEN_MASK) >> 8,
1300
+ (pixels[i] & BLUE_MASK)));
1301
+ pixels[i] = (pixels[i] & ALPHA_MASK) |
1302
+ ((max < thresh) ? 0x000000 : 0xffffff);
1303
+ }
1304
+ break;
1305
+
1306
+ // [toxi20050728] added new filters
1307
+ case ERODE:
1308
+ throw new RuntimeException("Use filter(ERODE) instead of " +
1309
+ "filter(ERODE, param)");
1310
+ case DILATE:
1311
+ throw new RuntimeException("Use filter(DILATE) instead of " +
1312
+ "filter(DILATE, param)");
1313
+ }
1314
+ updatePixels(); // mark as modified
1315
+ }
1316
+
1317
+
1318
+ /** Set the high bits of all pixels to opaque. */
1319
+ protected void opaque() {
1320
+ for (int i = 0; i < pixels.length; i++) {
1321
+ pixels[i] = 0xFF000000 | pixels[i];
1322
+ }
1323
+ }
1324
+
1325
+
1326
+ /**
1327
+ * Optimized code for building the blur kernel.
1328
+ * further optimized blur code (approx. 15% for radius=20)
1329
+ * bigger speed gains for larger radii (~30%)
1330
+ * added support for various image types (ALPHA, RGB, ARGB)
1331
+ * [toxi 050728]
1332
+ * @param r
1333
+ */
1334
+ protected void buildBlurKernel(float r) {
1335
+ int radius = (int) (r * 3.5f);
1336
+ radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
1337
+ if (blurRadius != radius) {
1338
+ blurRadius = radius;
1339
+ blurKernelSize = 1 + blurRadius<<1;
1340
+ blurKernel = new int[blurKernelSize];
1341
+ blurMult = new int[blurKernelSize][256];
1342
+
1343
+ int bk,bki;
1344
+ int[] bm,bmi;
1345
+
1346
+ for (int i = 1, radiusi = radius - 1; i < radius; i++) {
1347
+ blurKernel[radius+i] = blurKernel[radiusi] = bki = radiusi * radiusi;
1348
+ bm=blurMult[radius+i];
1349
+ bmi=blurMult[radiusi--];
1350
+ for (int j = 0; j < 256; j++)
1351
+ bm[j] = bmi[j] = bki*j;
1352
+ }
1353
+ bk = blurKernel[radius] = radius * radius;
1354
+ bm = blurMult[radius];
1355
+ for (int j = 0; j < 256; j++)
1356
+ bm[j] = bk*j;
1357
+ }
1358
+ }
1359
+
1360
+ /**
1361
+ *
1362
+ * @param r
1363
+ */
1364
+ protected void blurAlpha(float r) {
1365
+ int sum, cb;
1366
+ int read, ri, ym, ymi, bk0;
1367
+ int b2[] = new int[pixels.length];
1368
+ int yi = 0;
1369
+
1370
+ buildBlurKernel(r);
1371
+
1372
+ for (int y = 0; y < pixelHeight; y++) {
1373
+ for (int x = 0; x < pixelWidth; x++) {
1374
+ //cb = cg = cr = sum = 0;
1375
+ cb = sum = 0;
1376
+ read = x - blurRadius;
1377
+ if (read<0) {
1378
+ bk0=-read;
1379
+ read=0;
1380
+ } else {
1381
+ if (read >= pixelWidth)
1382
+ break;
1383
+ bk0=0;
1384
+ }
1385
+ for (int i = bk0; i < blurKernelSize; i++) {
1386
+ if (read >= pixelWidth)
1387
+ break;
1388
+ int c = pixels[read + yi];
1389
+ int[] bm = blurMult[i];
1390
+ cb += bm[c & BLUE_MASK];
1391
+ sum += blurKernel[i];
1392
+ read++;
1393
+ }
1394
+ ri = yi + x;
1395
+ b2[ri] = cb / sum;
1396
+ }
1397
+ yi += pixelWidth;
1398
+ }
1399
+
1400
+ yi = 0;
1401
+ ym = -blurRadius;
1402
+ ymi = ym * pixelWidth;
1403
+
1404
+ for (int y = 0; y < pixelHeight; y++) {
1405
+ for (int x = 0; x < pixelWidth; x++) {
1406
+ cb = sum = 0;
1407
+ if (ym < 0) {
1408
+ bk0 = ri = -ym;
1409
+ read = x;
1410
+ } else {
1411
+ if (ym >= pixelHeight)
1412
+ break;
1413
+ bk0 = 0;
1414
+ ri = ym;
1415
+ read = x + ymi;
1416
+ }
1417
+ for (int i = bk0; i < blurKernelSize; i++) {
1418
+ if (ri >= pixelHeight)
1419
+ break;
1420
+ int[] bm = blurMult[i];
1421
+ cb += bm[b2[read]];
1422
+ sum += blurKernel[i];
1423
+ ri++;
1424
+ read += pixelWidth;
1425
+ }
1426
+ pixels[x+yi] = (cb/sum);
1427
+ }
1428
+ yi += pixelWidth;
1429
+ ymi += pixelWidth;
1430
+ ym++;
1431
+ }
1432
+ }
1433
+
1434
+ /**
1435
+ *
1436
+ * @param r
1437
+ */
1438
+ protected void blurRGB(float r) {
1439
+ int sum, cr, cg, cb; //, k;
1440
+ int /*pixel,*/ read, ri, /*roff,*/ ym, ymi, /*riw,*/ bk0;
1441
+ int r2[] = new int[pixels.length];
1442
+ int g2[] = new int[pixels.length];
1443
+ int b2[] = new int[pixels.length];
1444
+ int yi = 0;
1445
+
1446
+ buildBlurKernel(r);
1447
+
1448
+ for (int y = 0; y < pixelHeight; y++) {
1449
+ for (int x = 0; x < pixelWidth; x++) {
1450
+ cb = cg = cr = sum = 0;
1451
+ read = x - blurRadius;
1452
+ if (read < 0) {
1453
+ bk0 = -read;
1454
+ read = 0;
1455
+ } else {
1456
+ if (read >= pixelWidth) {
1457
+ break;
1458
+ }
1459
+ bk0 = 0;
1460
+ }
1461
+ for (int i = bk0; i < blurKernelSize; i++) {
1462
+ if (read >= pixelWidth) {
1463
+ break;
1464
+ }
1465
+ int c = pixels[read + yi];
1466
+ int[] bm = blurMult[i];
1467
+ cr += bm[(c & RED_MASK) >> 16];
1468
+ cg += bm[(c & GREEN_MASK) >> 8];
1469
+ cb += bm[c & BLUE_MASK];
1470
+ sum += blurKernel[i];
1471
+ read++;
1472
+ }
1473
+ ri = yi + x;
1474
+ r2[ri] = cr / sum;
1475
+ g2[ri] = cg / sum;
1476
+ b2[ri] = cb / sum;
1477
+ }
1478
+ yi += pixelWidth;
1479
+ }
1480
+
1481
+ yi = 0;
1482
+ ym = -blurRadius;
1483
+ ymi = ym * pixelWidth;
1484
+
1485
+ for (int y = 0; y < pixelHeight; y++) {
1486
+ for (int x = 0; x < pixelWidth; x++) {
1487
+ cb = cg = cr = sum = 0;
1488
+ if (ym < 0) {
1489
+ bk0 = ri = -ym;
1490
+ read = x;
1491
+ } else {
1492
+ if (ym >= pixelHeight) {
1493
+ break;
1494
+ }
1495
+ bk0 = 0;
1496
+ ri = ym;
1497
+ read = x + ymi;
1498
+ }
1499
+ for (int i = bk0; i < blurKernelSize; i++) {
1500
+ if (ri >= pixelHeight) {
1501
+ break;
1502
+ }
1503
+ int[] bm = blurMult[i];
1504
+ cr += bm[r2[read]];
1505
+ cg += bm[g2[read]];
1506
+ cb += bm[b2[read]];
1507
+ sum += blurKernel[i];
1508
+ ri++;
1509
+ read += pixelWidth;
1510
+ }
1511
+ pixels[x+yi] = 0xff000000 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum);
1512
+ }
1513
+ yi += pixelWidth;
1514
+ ymi += pixelWidth;
1515
+ ym++;
1516
+ }
1517
+ }
1518
+
1519
+ /**
1520
+ *
1521
+ * @param r
1522
+ */
1523
+ protected void blurARGB(float r) {
1524
+ int sum, cr, cg, cb, ca;
1525
+ int /*pixel,*/ read, ri, /*roff,*/ ym, ymi, /*riw,*/ bk0;
1526
+ int wh = pixels.length;
1527
+ int r2[] = new int[wh];
1528
+ int g2[] = new int[wh];
1529
+ int b2[] = new int[wh];
1530
+ int a2[] = new int[wh];
1531
+ int yi = 0;
1532
+
1533
+ buildBlurKernel(r);
1534
+
1535
+ for (int y = 0; y < pixelHeight; y++) {
1536
+ for (int x = 0; x < pixelWidth; x++) {
1537
+ cb = cg = cr = ca = sum = 0;
1538
+ read = x - blurRadius;
1539
+ if (read < 0) {
1540
+ bk0 = -read;
1541
+ read = 0;
1542
+ } else {
1543
+ if (read >= pixelWidth) {
1544
+ break;
1545
+ }
1546
+ bk0=0;
1547
+ }
1548
+ for (int i = bk0; i < blurKernelSize; i++) {
1549
+ if (read >= pixelWidth) {
1550
+ break;
1551
+ }
1552
+ int c = pixels[read + yi];
1553
+ int[] bm=blurMult[i];
1554
+ ca += bm[(c & ALPHA_MASK) >>> 24];
1555
+ cr += bm[(c & RED_MASK) >> 16];
1556
+ cg += bm[(c & GREEN_MASK) >> 8];
1557
+ cb += bm[c & BLUE_MASK];
1558
+ sum += blurKernel[i];
1559
+ read++;
1560
+ }
1561
+ ri = yi + x;
1562
+ a2[ri] = ca / sum;
1563
+ r2[ri] = cr / sum;
1564
+ g2[ri] = cg / sum;
1565
+ b2[ri] = cb / sum;
1566
+ }
1567
+ yi += pixelWidth;
1568
+ }
1569
+
1570
+ yi = 0;
1571
+ ym = -blurRadius;
1572
+ ymi = ym * pixelWidth;
1573
+
1574
+ for (int y = 0; y < pixelHeight; y++) {
1575
+ for (int x = 0; x < pixelWidth; x++) {
1576
+ cb = cg = cr = ca = sum = 0;
1577
+ if (ym < 0) {
1578
+ bk0 = ri = -ym;
1579
+ read = x;
1580
+ } else {
1581
+ if (ym >= pixelHeight) {
1582
+ break;
1583
+ }
1584
+ bk0 = 0;
1585
+ ri = ym;
1586
+ read = x + ymi;
1587
+ }
1588
+ for (int i = bk0; i < blurKernelSize; i++) {
1589
+ if (ri >= pixelHeight) {
1590
+ break;
1591
+ }
1592
+ int[] bm=blurMult[i];
1593
+ ca += bm[a2[read]];
1594
+ cr += bm[r2[read]];
1595
+ cg += bm[g2[read]];
1596
+ cb += bm[b2[read]];
1597
+ sum += blurKernel[i];
1598
+ ri++;
1599
+ read += pixelWidth;
1600
+ }
1601
+ pixels[x+yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum);
1602
+ }
1603
+ yi += pixelWidth;
1604
+ ymi += pixelWidth;
1605
+ ym++;
1606
+ }
1607
+ }
1608
+
1609
+
1610
+ /**
1611
+ * Generic dilate/erode filter using luminance values
1612
+ * as decision factor. [toxi 050728]
1613
+ */
1614
+ protected void dilate() { // formerly dilate(false)
1615
+ int index = 0;
1616
+ int maxIndex = pixels.length;
1617
+ int[] outgoing = new int[maxIndex];
1618
+
1619
+ // erosion (grow light areas)
1620
+ while (index < maxIndex) {
1621
+ int curRowIndex = index;
1622
+ int maxRowIndex = index + pixelWidth;
1623
+ while (index < maxRowIndex) {
1624
+ int orig = pixels[index];
1625
+ int result = orig;
1626
+ int idxLeft = index - 1;
1627
+ int idxRight = index + 1;
1628
+ int idxUp = index - pixelWidth;
1629
+ int idxDown = index + pixelWidth;
1630
+ if (idxLeft < curRowIndex) {
1631
+ idxLeft = index;
1632
+ }
1633
+ if (idxRight >= maxRowIndex) {
1634
+ idxRight = index;
1635
+ }
1636
+ if (idxUp < 0) {
1637
+ idxUp = index;
1638
+ }
1639
+ if (idxDown >= maxIndex) {
1640
+ idxDown = index;
1641
+ }
1642
+
1643
+ int colUp = pixels[idxUp];
1644
+ int colLeft = pixels[idxLeft];
1645
+ int colDown = pixels[idxDown];
1646
+ int colRight = pixels[idxRight];
1647
+
1648
+ // compute luminance
1649
+ int currLum =
1650
+ 77*(orig>>16&0xff) + 151*(orig>>8&0xff) + 28*(orig&0xff);
1651
+ int lumLeft =
1652
+ 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
1653
+ int lumRight =
1654
+ 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
1655
+ int lumUp =
1656
+ 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
1657
+ int lumDown =
1658
+ 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
1659
+
1660
+ if (lumLeft > currLum) {
1661
+ result = colLeft;
1662
+ currLum = lumLeft;
1663
+ }
1664
+ if (lumRight > currLum) {
1665
+ result = colRight;
1666
+ currLum = lumRight;
1667
+ }
1668
+ if (lumUp > currLum) {
1669
+ result = colUp;
1670
+ currLum = lumUp;
1671
+ }
1672
+ if (lumDown > currLum) {
1673
+ result = colDown;
1674
+ currLum = lumDown;
1675
+ }
1676
+ outgoing[index++] = result;
1677
+ }
1678
+ }
1679
+ System.arraycopy(outgoing, 0, pixels, 0, maxIndex);
1680
+ }
1681
+
1682
+ /**
1683
+ *
1684
+ */
1685
+ protected void erode() { // formerly dilate(true)
1686
+ int index = 0;
1687
+ int maxIndex = pixels.length;
1688
+ int[] outgoing = new int[maxIndex];
1689
+
1690
+ // dilate (grow dark areas)
1691
+ while (index < maxIndex) {
1692
+ int curRowIndex = index;
1693
+ int maxRowIndex = index + pixelWidth;
1694
+ while (index < maxRowIndex) {
1695
+ int orig = pixels[index];
1696
+ int result = orig;
1697
+ int idxLeft = index - 1;
1698
+ int idxRight = index + 1;
1699
+ int idxUp = index - pixelWidth;
1700
+ int idxDown = index + pixelWidth;
1701
+ if (idxLeft < curRowIndex) {
1702
+ idxLeft = index;
1703
+ }
1704
+ if (idxRight >= maxRowIndex) {
1705
+ idxRight = index;
1706
+ }
1707
+ if (idxUp < 0) {
1708
+ idxUp = index;
1709
+ }
1710
+ if (idxDown >= maxIndex) {
1711
+ idxDown = index;
1712
+ }
1713
+
1714
+ int colUp = pixels[idxUp];
1715
+ int colLeft = pixels[idxLeft];
1716
+ int colDown = pixels[idxDown];
1717
+ int colRight = pixels[idxRight];
1718
+
1719
+ // compute luminance
1720
+ int currLum =
1721
+ 77*(orig>>16&0xff) + 151*(orig>>8&0xff) + 28*(orig&0xff);
1722
+ int lumLeft =
1723
+ 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
1724
+ int lumRight =
1725
+ 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
1726
+ int lumUp =
1727
+ 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
1728
+ int lumDown =
1729
+ 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
1730
+
1731
+ if (lumLeft < currLum) {
1732
+ result = colLeft;
1733
+ currLum = lumLeft;
1734
+ }
1735
+ if (lumRight < currLum) {
1736
+ result = colRight;
1737
+ currLum = lumRight;
1738
+ }
1739
+ if (lumUp < currLum) {
1740
+ result = colUp;
1741
+ currLum = lumUp;
1742
+ }
1743
+ if (lumDown < currLum) {
1744
+ result = colDown;
1745
+ currLum = lumDown;
1746
+ }
1747
+ outgoing[index++] = result;
1748
+ }
1749
+ }
1750
+ System.arraycopy(outgoing, 0, pixels, 0, maxIndex);
1751
+ }
1752
+
1753
+
1754
+
1755
+ //////////////////////////////////////////////////////////////
1756
+
1757
+ // COPY
1758
+
1759
+
1760
+ /**
1761
+ * ( begin auto-generated from PImage_copy.xml )
1762
+ *
1763
+ * Copies a region of pixels from one image into another. If the source and
1764
+ * destination regions aren't the same size, it will automatically resize
1765
+ * source pixels to fit the specified target region. No alpha information
1766
+ * is used in the process, however if the source image has an alpha channel
1767
+ * set, it will be copied as well.
1768
+ * <br /><br />
1769
+ * As of release 0149, this function ignores <b>imageMode()</b>.
1770
+ *
1771
+ * ( end auto-generated )
1772
+ *
1773
+ * @webref image:pixels
1774
+ * @brief Copies the entire image
1775
+ * @usage web_application
1776
+ * @param sx X coordinate of the source's upper left corner
1777
+ * @param sy Y coordinate of the source's upper left corner
1778
+ * @param sw source image width
1779
+ * @param sh source image height
1780
+ * @param dx X coordinate of the destination's upper left corner
1781
+ * @param dy Y coordinate of the destination's upper left corner
1782
+ * @param dw destination image width
1783
+ * @param dh destination image height
1784
+ * @see PGraphics#alpha(int)
1785
+ * @see PImage#blend(PImage, int, int, int, int, int, int, int, int, int)
1786
+ */
1787
+ public void copy(int sx, int sy, int sw, int sh,
1788
+ int dx, int dy, int dw, int dh) {
1789
+ blend(this, sx, sy, sw, sh, dx, dy, dw, dh, REPLACE);
1790
+ }
1791
+
1792
+
1793
+ /**
1794
+ * @param src an image variable referring to the source image.
1795
+ * @param sx
1796
+ * @param sy
1797
+ * @param dh
1798
+ * @param sw
1799
+ * @param dx
1800
+ * @param sh
1801
+ * @param dy
1802
+ * @param dw
1803
+ */
1804
+ public void copy(PImage src,
1805
+ int sx, int sy, int sw, int sh,
1806
+ int dx, int dy, int dw, int dh) {
1807
+ blend(src, sx, sy, sw, sh, dx, dy, dw, dh, REPLACE);
1808
+ }
1809
+
1810
+
1811
+
1812
+ //////////////////////////////////////////////////////////////
1813
+
1814
+ // BLEND
1815
+
1816
+
1817
+ /**
1818
+ * ( begin auto-generated from blendColor.xml )
1819
+ *
1820
+ * Blends two color values together based on the blending mode given as the
1821
+ * <b>MODE</b> parameter. The possible modes are described in the reference
1822
+ * for the <b>blend()</b> function.
1823
+ *
1824
+ * ( end auto-generated )
1825
+ * <h3>Advanced</h3>
1826
+ * <UL>
1827
+ * <LI>REPLACE - destination colour equals colour of source pixel: C = A.
1828
+ * Sometimes called "Normal" or "Copy" in other software.
1829
+ *
1830
+ * <LI>BLEND - linear interpolation of colours:
1831
+ * <TT>C = A*factor + B</TT>
1832
+ *
1833
+ * <LI>ADD - additive blending with white clip:
1834
+ * <TT>C = min(A*factor + B, 255)</TT>.
1835
+ * Clipped to 0..255, Photoshop calls this "Linear Burn",
1836
+ * and Director calls it "Add Pin".
1837
+ *
1838
+ * <LI>SUBTRACT - substractive blend with black clip:
1839
+ * <TT>C = max(B - A*factor, 0)</TT>.
1840
+ * Clipped to 0..255, Photoshop calls this "Linear Dodge",
1841
+ * and Director calls it "Subtract Pin".
1842
+ *
1843
+ * <LI>DARKEST - only the darkest colour succeeds:
1844
+ * <TT>C = min(A*factor, B)</TT>.
1845
+ * Illustrator calls this "Darken".
1846
+ *
1847
+ * <LI>LIGHTEST - only the lightest colour succeeds:
1848
+ * <TT>C = max(A*factor, B)</TT>.
1849
+ * Illustrator calls this "Lighten".
1850
+ *
1851
+ * <LI>DIFFERENCE - subtract colors from underlying image.
1852
+ *
1853
+ * <LI>EXCLUSION - similar to DIFFERENCE, but less extreme.
1854
+ *
1855
+ * <LI>MULTIPLY - Multiply the colors, result will always be darker.
1856
+ *
1857
+ * <LI>SCREEN - Opposite multiply, uses inverse values of the colors.
1858
+ *
1859
+ * <LI>OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values,
1860
+ * and screens light values.
1861
+ *
1862
+ * <LI>HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
1863
+ *
1864
+ * <LI>SOFT_LIGHT - Mix of DARKEST and LIGHTEST.
1865
+ * Works like OVERLAY, but not as harsh.
1866
+ *
1867
+ * <LI>DODGE - Lightens light tones and increases contrast, ignores darks.
1868
+ * Called "Color Dodge" in Illustrator and Photoshop.
1869
+ *
1870
+ * <LI>BURN - Darker areas are applied, increasing contrast, ignores lights.
1871
+ * Called "Color Burn" in Illustrator and Photoshop.
1872
+ * </UL>
1873
+ * <P>A useful reference for blending modes and their algorithms can be
1874
+ * found in the <A HREF="http://www.w3.org/TR/SVG12/rendering.html">SVG</A>
1875
+ * specification.</P>
1876
+ * <P>It is important to note that Processing uses "fast" code, not
1877
+ * necessarily "correct" code. No biggie, most software does. A nitpicker
1878
+ * can find numerous "off by 1 division" problems in the blend code where
1879
+ * <TT>&gt;&gt;8</TT> or <TT>&gt;&gt;7</TT> is used when strictly speaking
1880
+ * <TT>/255.0</T> or <TT>/127.0</TT> should have been used.</P>
1881
+ * <P>For instance, exclusion (not intended for real-time use) reads
1882
+ * <TT>r1 + r2 - ((2 * r1 * r2) / 255)</TT> because <TT>255 == 1.0</TT>
1883
+ * not <TT>256 == 1.0</TT>. In other words, <TT>(255*255)>>8</TT> is not
1884
+ * the same as <TT>(255*255)/255</TT>. But for real-time use the shifts
1885
+ * are preferrable, and the difference is insignificant for applications
1886
+ * built with Processing.</P>
1887
+ *
1888
+ * @return
1889
+ * @webref color:creating_reading
1890
+ * @usage web_application
1891
+ * @param c1 the first color to blend
1892
+ * @param c2 the second color to blend
1893
+ * @param mode either BLEND, ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, or BURN
1894
+ * @see PImage#blend(PImage, int, int, int, int, int, int, int, int, int)
1895
+ * @see PApplet#color(float, float, float, float)
1896
+ */
1897
+ static public int blendColor(int c1, int c2, int mode) { // ignore
1898
+ switch (mode) {
1899
+ case REPLACE: return c2;
1900
+ case BLEND: return blend_blend(c1, c2);
1901
+
1902
+ case ADD: return blend_add_pin(c1, c2);
1903
+ case SUBTRACT: return blend_sub_pin(c1, c2);
1904
+
1905
+ case LIGHTEST: return blend_lightest(c1, c2);
1906
+ case DARKEST: return blend_darkest(c1, c2);
1907
+
1908
+ case DIFFERENCE: return blend_difference(c1, c2);
1909
+ case EXCLUSION: return blend_exclusion(c1, c2);
1910
+
1911
+ case MULTIPLY: return blend_multiply(c1, c2);
1912
+ case SCREEN: return blend_screen(c1, c2);
1913
+
1914
+ case HARD_LIGHT: return blend_hard_light(c1, c2);
1915
+ case SOFT_LIGHT: return blend_soft_light(c1, c2);
1916
+ case OVERLAY: return blend_overlay(c1, c2);
1917
+
1918
+ case DODGE: return blend_dodge(c1, c2);
1919
+ case BURN: return blend_burn(c1, c2);
1920
+ }
1921
+ return 0;
1922
+ }
1923
+
1924
+ /**
1925
+ *
1926
+ * @param sx
1927
+ * @param sy
1928
+ * @param sw
1929
+ * @param sh
1930
+ * @param dx
1931
+ * @param dy
1932
+ * @param dw
1933
+ * @param dh
1934
+ * @param mode
1935
+ */
1936
+ public void blend(int sx, int sy, int sw, int sh,
1937
+ int dx, int dy, int dw, int dh, int mode) {
1938
+ blend(this, sx, sy, sw, sh, dx, dy, dw, dh, mode);
1939
+ }
1940
+
1941
+
1942
+ /**
1943
+ * ( begin auto-generated from PImage_blend.xml )
1944
+ *
1945
+ * Blends a region of pixels into the image specified by the <b>img</b>
1946
+ * parameter. These copies utilize full alpha channel support and a choice
1947
+ * of the following modes to blend the colors of source pixels (A) with the
1948
+ * ones of pixels in the destination image (B):<br />
1949
+ * <br />
1950
+ * BLEND - linear interpolation of colours: C = A*factor + B<br />
1951
+ * <br />
1952
+ * ADD - additive blending with white clip: C = min(A*factor + B, 255)<br />
1953
+ * <br />
1954
+ * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor,
1955
+ * 0)<br />
1956
+ * <br />
1957
+ * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)<br />
1958
+ * <br />
1959
+ * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)<br />
1960
+ * <br />
1961
+ * DIFFERENCE - subtract colors from underlying image.<br />
1962
+ * <br />
1963
+ * EXCLUSION - similar to DIFFERENCE, but less extreme.<br />
1964
+ * <br />
1965
+ * MULTIPLY - Multiply the colors, result will always be darker.<br />
1966
+ * <br />
1967
+ * SCREEN - Opposite multiply, uses inverse values of the colors.<br />
1968
+ * <br />
1969
+ * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values,
1970
+ * and screens light values.<br />
1971
+ * <br />
1972
+ * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.<br />
1973
+ * <br />
1974
+ * SOFT_LIGHT - Mix of DARKEST and LIGHTEST.
1975
+ * Works like OVERLAY, but not as harsh.<br />
1976
+ * <br />
1977
+ * DODGE - Lightens light tones and increases contrast, ignores darks.
1978
+ * Called "Color Dodge" in Illustrator and Photoshop.<br />
1979
+ * <br />
1980
+ * BURN - Darker areas are applied, increasing contrast, ignores lights.
1981
+ * Called "Color Burn" in Illustrator and Photoshop.<br />
1982
+ * <br />
1983
+ * All modes use the alpha information (highest byte) of source image
1984
+ * pixels as the blending factor. If the source and destination regions are
1985
+ * different sizes, the image will be automatically resized to match the
1986
+ * destination size. If the <b>srcImg</b> parameter is not used, the
1987
+ * display window is used as the source image.<br />
1988
+ * <br />
1989
+ * As of release 0149, this function ignores <b>imageMode()</b>.
1990
+ *
1991
+ * ( end auto-generated )
1992
+ *
1993
+ * @webref image:pixels
1994
+ * @brief Copies a pixel or rectangle of pixels using different blending modes
1995
+ * @param src an image variable referring to the source image
1996
+ * @param sx X coordinate of the source's upper left corner
1997
+ * @param sy Y coordinate of the source's upper left corner
1998
+ * @param sw source image width
1999
+ * @param sh source image height
2000
+ * @param dx X coordinate of the destinations's upper left corner
2001
+ * @param dy Y coordinate of the destinations's upper left corner
2002
+ * @param dw destination image width
2003
+ * @param dh destination image height
2004
+ * @param mode Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
2005
+ *
2006
+ * @see PApplet#alpha(int)
2007
+ * @see PImage#copy(PImage, int, int, int, int, int, int, int, int)
2008
+ * @see PImage#blendColor(int,int,int)
2009
+ */
2010
+ public void blend(PImage src,
2011
+ int sx, int sy, int sw, int sh,
2012
+ int dx, int dy, int dw, int dh, int mode) {
2013
+ int sx2 = sx + sw;
2014
+ int sy2 = sy + sh;
2015
+ int dx2 = dx + dw;
2016
+ int dy2 = dy + dh;
2017
+
2018
+ loadPixels();
2019
+ if (src == this) {
2020
+ if (intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
2021
+ blit_resize(get(sx, sy, sw, sh),
2022
+ 0, 0, sw, sh,
2023
+ pixels, pixelWidth, pixelHeight, dx, dy, dx2, dy2, mode);
2024
+ } else {
2025
+ // same as below, except skip the loadPixels() because it'd be redundant
2026
+ blit_resize(src, sx, sy, sx2, sy2,
2027
+ pixels, pixelWidth, pixelHeight, dx, dy, dx2, dy2, mode);
2028
+ }
2029
+ } else {
2030
+ src.loadPixels();
2031
+ blit_resize(src, sx, sy, sx2, sy2,
2032
+ pixels, pixelWidth, pixelHeight, dx, dy, dx2, dy2, mode);
2033
+ //src.updatePixels();
2034
+ }
2035
+ updatePixels();
2036
+ }
2037
+
2038
+
2039
+ /**
2040
+ * Check to see if two rectangles intersect one another
2041
+ */
2042
+ private boolean intersect(int sx1, int sy1, int sx2, int sy2,
2043
+ int dx1, int dy1, int dx2, int dy2) {
2044
+ int sw = sx2 - sx1 + 1;
2045
+ int sh = sy2 - sy1 + 1;
2046
+ int dw = dx2 - dx1 + 1;
2047
+ int dh = dy2 - dy1 + 1;
2048
+
2049
+ if (dx1 < sx1) {
2050
+ dw += dx1 - sx1;
2051
+ if (dw > sw) {
2052
+ dw = sw;
2053
+ }
2054
+ } else {
2055
+ int w = sw + sx1 - dx1;
2056
+ if (dw > w) {
2057
+ dw = w;
2058
+ }
2059
+ }
2060
+ if (dy1 < sy1) {
2061
+ dh += dy1 - sy1;
2062
+ if (dh > sh) {
2063
+ dh = sh;
2064
+ }
2065
+ } else {
2066
+ int h = sh + sy1 - dy1;
2067
+ if (dh > h) {
2068
+ dh = h;
2069
+ }
2070
+ }
2071
+ return !(dw <= 0 || dh <= 0);
2072
+ }
2073
+
2074
+
2075
+ //////////////////////////////////////////////////////////////
2076
+
2077
+
2078
+ /**
2079
+ * Internal blitter/resizer/copier from toxi.
2080
+ * Uses bilinear filtering if smooth() has been enabled
2081
+ * 'mode' determines the blending mode used in the process.
2082
+ */
2083
+ private void blit_resize(PImage img,
2084
+ int srcX1, int srcY1, int srcX2, int srcY2,
2085
+ int[] destPixels, int screenW, int screenH,
2086
+ int destX1, int destY1, int destX2, int destY2,
2087
+ int mode) {
2088
+ if (srcX1 < 0) srcX1 = 0;
2089
+ if (srcY1 < 0) srcY1 = 0;
2090
+ if (srcX2 > img.pixelWidth) srcX2 = img.pixelWidth;
2091
+ if (srcY2 > img.pixelHeight) srcY2 = img.pixelHeight;
2092
+
2093
+ int srcW = srcX2 - srcX1;
2094
+ int srcH = srcY2 - srcY1;
2095
+ int destW = destX2 - destX1;
2096
+ int destH = destY2 - destY1;
2097
+
2098
+ boolean smooth = true; // may as well go with the smoothing these days
2099
+
2100
+ if (!smooth) {
2101
+ srcW++; srcH++;
2102
+ }
2103
+
2104
+ if (destW <= 0 || destH <= 0 ||
2105
+ srcW <= 0 || srcH <= 0 ||
2106
+ destX1 >= screenW || destY1 >= screenH ||
2107
+ srcX1 >= img.pixelWidth || srcY1 >= img.pixelHeight) {
2108
+ return;
2109
+ }
2110
+
2111
+ int dx = (int) (srcW / (float) destW * PRECISIONF);
2112
+ int dy = (int) (srcH / (float) destH * PRECISIONF);
2113
+
2114
+ srcXOffset = destX1 < 0 ? -destX1 * dx : srcX1 * PRECISIONF;
2115
+ srcYOffset = destY1 < 0 ? -destY1 * dy : srcY1 * PRECISIONF;
2116
+
2117
+ if (destX1 < 0) {
2118
+ destW += destX1;
2119
+ destX1 = 0;
2120
+ }
2121
+ if (destY1 < 0) {
2122
+ destH += destY1;
2123
+ destY1 = 0;
2124
+ }
2125
+
2126
+ destW = min(destW, screenW - destX1);
2127
+ destH = min(destH, screenH - destY1);
2128
+
2129
+ int destOffset = destY1 * screenW + destX1;
2130
+ srcBuffer = img.pixels;
2131
+
2132
+ if (smooth) {
2133
+ // use bilinear filtering
2134
+ iw = img.pixelWidth;
2135
+ iw1 = img.pixelWidth - 1;
2136
+ ih1 = img.pixelHeight - 1;
2137
+
2138
+ switch (mode) {
2139
+
2140
+ case BLEND:
2141
+ for (int y = 0; y < destH; y++) {
2142
+ filter_new_scanline();
2143
+ for (int x = 0; x < destW; x++) {
2144
+ // davbol - renamed old blend_multiply to blend_blend
2145
+ destPixels[destOffset + x] =
2146
+ blend_blend(destPixels[destOffset + x], filter_bilinear());
2147
+ sX += dx;
2148
+ }
2149
+ destOffset += screenW;
2150
+ srcYOffset += dy;
2151
+ }
2152
+ break;
2153
+
2154
+ case ADD:
2155
+ for (int y = 0; y < destH; y++) {
2156
+ filter_new_scanline();
2157
+ for (int x = 0; x < destW; x++) {
2158
+ destPixels[destOffset + x] =
2159
+ blend_add_pin(destPixels[destOffset + x], filter_bilinear());
2160
+ sX += dx;
2161
+ }
2162
+ destOffset += screenW;
2163
+ srcYOffset += dy;
2164
+ }
2165
+ break;
2166
+
2167
+ case SUBTRACT:
2168
+ for (int y = 0; y < destH; y++) {
2169
+ filter_new_scanline();
2170
+ for (int x = 0; x < destW; x++) {
2171
+ destPixels[destOffset + x] =
2172
+ blend_sub_pin(destPixels[destOffset + x], filter_bilinear());
2173
+ sX += dx;
2174
+ }
2175
+ destOffset += screenW;
2176
+ srcYOffset += dy;
2177
+ }
2178
+ break;
2179
+
2180
+ case LIGHTEST:
2181
+ for (int y = 0; y < destH; y++) {
2182
+ filter_new_scanline();
2183
+ for (int x = 0; x < destW; x++) {
2184
+ destPixels[destOffset + x] =
2185
+ blend_lightest(destPixels[destOffset + x], filter_bilinear());
2186
+ sX += dx;
2187
+ }
2188
+ destOffset += screenW;
2189
+ srcYOffset += dy;
2190
+ }
2191
+ break;
2192
+
2193
+ case DARKEST:
2194
+ for (int y = 0; y < destH; y++) {
2195
+ filter_new_scanline();
2196
+ for (int x = 0; x < destW; x++) {
2197
+ destPixels[destOffset + x] =
2198
+ blend_darkest(destPixels[destOffset + x], filter_bilinear());
2199
+ sX += dx;
2200
+ }
2201
+ destOffset += screenW;
2202
+ srcYOffset += dy;
2203
+ }
2204
+ break;
2205
+
2206
+ case REPLACE:
2207
+ for (int y = 0; y < destH; y++) {
2208
+ filter_new_scanline();
2209
+ for (int x = 0; x < destW; x++) {
2210
+ destPixels[destOffset + x] = filter_bilinear();
2211
+ sX += dx;
2212
+ }
2213
+ destOffset += screenW;
2214
+ srcYOffset += dy;
2215
+ }
2216
+ break;
2217
+
2218
+ case DIFFERENCE:
2219
+ for (int y = 0; y < destH; y++) {
2220
+ filter_new_scanline();
2221
+ for (int x = 0; x < destW; x++) {
2222
+ destPixels[destOffset + x] =
2223
+ blend_difference(destPixels[destOffset + x], filter_bilinear());
2224
+ sX += dx;
2225
+ }
2226
+ destOffset += screenW;
2227
+ srcYOffset += dy;
2228
+ }
2229
+ break;
2230
+
2231
+ case EXCLUSION:
2232
+ for (int y = 0; y < destH; y++) {
2233
+ filter_new_scanline();
2234
+ for (int x = 0; x < destW; x++) {
2235
+ destPixels[destOffset + x] =
2236
+ blend_exclusion(destPixels[destOffset + x], filter_bilinear());
2237
+ sX += dx;
2238
+ }
2239
+ destOffset += screenW;
2240
+ srcYOffset += dy;
2241
+ }
2242
+ break;
2243
+
2244
+ case MULTIPLY:
2245
+ for (int y = 0; y < destH; y++) {
2246
+ filter_new_scanline();
2247
+ for (int x = 0; x < destW; x++) {
2248
+ destPixels[destOffset + x] =
2249
+ blend_multiply(destPixels[destOffset + x], filter_bilinear());
2250
+ sX += dx;
2251
+ }
2252
+ destOffset += screenW;
2253
+ srcYOffset += dy;
2254
+ }
2255
+ break;
2256
+
2257
+ case SCREEN:
2258
+ for (int y = 0; y < destH; y++) {
2259
+ filter_new_scanline();
2260
+ for (int x = 0; x < destW; x++) {
2261
+ destPixels[destOffset + x] =
2262
+ blend_screen(destPixels[destOffset + x], filter_bilinear());
2263
+ sX += dx;
2264
+ }
2265
+ destOffset += screenW;
2266
+ srcYOffset += dy;
2267
+ }
2268
+ break;
2269
+
2270
+ case OVERLAY:
2271
+ for (int y = 0; y < destH; y++) {
2272
+ filter_new_scanline();
2273
+ for (int x = 0; x < destW; x++) {
2274
+ destPixels[destOffset + x] =
2275
+ blend_overlay(destPixels[destOffset + x], filter_bilinear());
2276
+ sX += dx;
2277
+ }
2278
+ destOffset += screenW;
2279
+ srcYOffset += dy;
2280
+ }
2281
+ break;
2282
+
2283
+ case HARD_LIGHT:
2284
+ for (int y = 0; y < destH; y++) {
2285
+ filter_new_scanline();
2286
+ for (int x = 0; x < destW; x++) {
2287
+ destPixels[destOffset + x] =
2288
+ blend_hard_light(destPixels[destOffset + x], filter_bilinear());
2289
+ sX += dx;
2290
+ }
2291
+ destOffset += screenW;
2292
+ srcYOffset += dy;
2293
+ }
2294
+ break;
2295
+
2296
+ case SOFT_LIGHT:
2297
+ for (int y = 0; y < destH; y++) {
2298
+ filter_new_scanline();
2299
+ for (int x = 0; x < destW; x++) {
2300
+ destPixels[destOffset + x] =
2301
+ blend_soft_light(destPixels[destOffset + x], filter_bilinear());
2302
+ sX += dx;
2303
+ }
2304
+ destOffset += screenW;
2305
+ srcYOffset += dy;
2306
+ }
2307
+ break;
2308
+
2309
+ // davbol - proposed 2007-01-09
2310
+ case DODGE:
2311
+ for (int y = 0; y < destH; y++) {
2312
+ filter_new_scanline();
2313
+ for (int x = 0; x < destW; x++) {
2314
+ destPixels[destOffset + x] =
2315
+ blend_dodge(destPixels[destOffset + x], filter_bilinear());
2316
+ sX += dx;
2317
+ }
2318
+ destOffset += screenW;
2319
+ srcYOffset += dy;
2320
+ }
2321
+ break;
2322
+
2323
+ case BURN:
2324
+ for (int y = 0; y < destH; y++) {
2325
+ filter_new_scanline();
2326
+ for (int x = 0; x < destW; x++) {
2327
+ destPixels[destOffset + x] =
2328
+ blend_burn(destPixels[destOffset + x], filter_bilinear());
2329
+ sX += dx;
2330
+ }
2331
+ destOffset += screenW;
2332
+ srcYOffset += dy;
2333
+ }
2334
+ break;
2335
+
2336
+ }
2337
+
2338
+ } else {
2339
+ // nearest neighbour scaling (++fast!)
2340
+ switch (mode) {
2341
+
2342
+ case BLEND:
2343
+ for (int y = 0; y < destH; y++) {
2344
+ sX = srcXOffset;
2345
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2346
+ for (int x = 0; x < destW; x++) {
2347
+ // davbol - renamed old blend_multiply to blend_blend
2348
+ destPixels[destOffset + x] =
2349
+ blend_blend(destPixels[destOffset + x],
2350
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2351
+ sX += dx;
2352
+ }
2353
+ destOffset += screenW;
2354
+ srcYOffset += dy;
2355
+ }
2356
+ break;
2357
+
2358
+ case ADD:
2359
+ for (int y = 0; y < destH; y++) {
2360
+ sX = srcXOffset;
2361
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2362
+ for (int x = 0; x < destW; x++) {
2363
+ destPixels[destOffset + x] =
2364
+ blend_add_pin(destPixels[destOffset + x],
2365
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2366
+ sX += dx;
2367
+ }
2368
+ destOffset += screenW;
2369
+ srcYOffset += dy;
2370
+ }
2371
+ break;
2372
+
2373
+ case SUBTRACT:
2374
+ for (int y = 0; y < destH; y++) {
2375
+ sX = srcXOffset;
2376
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2377
+ for (int x = 0; x < destW; x++) {
2378
+ destPixels[destOffset + x] =
2379
+ blend_sub_pin(destPixels[destOffset + x],
2380
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2381
+ sX += dx;
2382
+ }
2383
+ destOffset += screenW;
2384
+ srcYOffset += dy;
2385
+ }
2386
+ break;
2387
+
2388
+ case LIGHTEST:
2389
+ for (int y = 0; y < destH; y++) {
2390
+ sX = srcXOffset;
2391
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2392
+ for (int x = 0; x < destW; x++) {
2393
+ destPixels[destOffset + x] =
2394
+ blend_lightest(destPixels[destOffset + x],
2395
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2396
+ sX += dx;
2397
+ }
2398
+ destOffset += screenW;
2399
+ srcYOffset += dy;
2400
+ }
2401
+ break;
2402
+
2403
+ case DARKEST:
2404
+ for (int y = 0; y < destH; y++) {
2405
+ sX = srcXOffset;
2406
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2407
+ for (int x = 0; x < destW; x++) {
2408
+ destPixels[destOffset + x] =
2409
+ blend_darkest(destPixels[destOffset + x],
2410
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2411
+ sX += dx;
2412
+ }
2413
+ destOffset += screenW;
2414
+ srcYOffset += dy;
2415
+ }
2416
+ break;
2417
+
2418
+ case REPLACE:
2419
+ for (int y = 0; y < destH; y++) {
2420
+ sX = srcXOffset;
2421
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2422
+ for (int x = 0; x < destW; x++) {
2423
+ destPixels[destOffset + x] = srcBuffer[sY + (sX >> PRECISIONB)];
2424
+ sX += dx;
2425
+ }
2426
+ destOffset += screenW;
2427
+ srcYOffset += dy;
2428
+ }
2429
+ break;
2430
+
2431
+ case DIFFERENCE:
2432
+ for (int y = 0; y < destH; y++) {
2433
+ sX = srcXOffset;
2434
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2435
+ for (int x = 0; x < destW; x++) {
2436
+ destPixels[destOffset + x] =
2437
+ blend_difference(destPixels[destOffset + x],
2438
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2439
+ sX += dx;
2440
+ }
2441
+ destOffset += screenW;
2442
+ srcYOffset += dy;
2443
+ }
2444
+ break;
2445
+
2446
+ case EXCLUSION:
2447
+ for (int y = 0; y < destH; y++) {
2448
+ sX = srcXOffset;
2449
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2450
+ for (int x = 0; x < destW; x++) {
2451
+ destPixels[destOffset + x] =
2452
+ blend_exclusion(destPixels[destOffset + x],
2453
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2454
+ sX += dx;
2455
+ }
2456
+ destOffset += screenW;
2457
+ srcYOffset += dy;
2458
+ }
2459
+ break;
2460
+
2461
+ case MULTIPLY:
2462
+ for (int y = 0; y < destH; y++) {
2463
+ sX = srcXOffset;
2464
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2465
+ for (int x = 0; x < destW; x++) {
2466
+ destPixels[destOffset + x] =
2467
+ blend_multiply(destPixels[destOffset + x],
2468
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2469
+ sX += dx;
2470
+ }
2471
+ destOffset += screenW;
2472
+ srcYOffset += dy;
2473
+ }
2474
+ break;
2475
+
2476
+ case SCREEN:
2477
+ for (int y = 0; y < destH; y++) {
2478
+ sX = srcXOffset;
2479
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2480
+ for (int x = 0; x < destW; x++) {
2481
+ destPixels[destOffset + x] =
2482
+ blend_screen(destPixels[destOffset + x],
2483
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2484
+ sX += dx;
2485
+ }
2486
+ destOffset += screenW;
2487
+ srcYOffset += dy;
2488
+ }
2489
+ break;
2490
+
2491
+ case OVERLAY:
2492
+ for (int y = 0; y < destH; y++) {
2493
+ sX = srcXOffset;
2494
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2495
+ for (int x = 0; x < destW; x++) {
2496
+ destPixels[destOffset + x] =
2497
+ blend_overlay(destPixels[destOffset + x],
2498
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2499
+ sX += dx;
2500
+ }
2501
+ destOffset += screenW;
2502
+ srcYOffset += dy;
2503
+ }
2504
+ break;
2505
+
2506
+ case HARD_LIGHT:
2507
+ for (int y = 0; y < destH; y++) {
2508
+ sX = srcXOffset;
2509
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2510
+ for (int x = 0; x < destW; x++) {
2511
+ destPixels[destOffset + x] =
2512
+ blend_hard_light(destPixels[destOffset + x],
2513
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2514
+ sX += dx;
2515
+ }
2516
+ destOffset += screenW;
2517
+ srcYOffset += dy;
2518
+ }
2519
+ break;
2520
+
2521
+ case SOFT_LIGHT:
2522
+ for (int y = 0; y < destH; y++) {
2523
+ sX = srcXOffset;
2524
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2525
+ for (int x = 0; x < destW; x++) {
2526
+ destPixels[destOffset + x] =
2527
+ blend_soft_light(destPixels[destOffset + x],
2528
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2529
+ sX += dx;
2530
+ }
2531
+ destOffset += screenW;
2532
+ srcYOffset += dy;
2533
+ }
2534
+ break;
2535
+
2536
+ // davbol - proposed 2007-01-09
2537
+ case DODGE:
2538
+ for (int y = 0; y < destH; y++) {
2539
+ sX = srcXOffset;
2540
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2541
+ for (int x = 0; x < destW; x++) {
2542
+ destPixels[destOffset + x] =
2543
+ blend_dodge(destPixels[destOffset + x],
2544
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2545
+ sX += dx;
2546
+ }
2547
+ destOffset += screenW;
2548
+ srcYOffset += dy;
2549
+ }
2550
+ break;
2551
+
2552
+ case BURN:
2553
+ for (int y = 0; y < destH; y++) {
2554
+ sX = srcXOffset;
2555
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
2556
+ for (int x = 0; x < destW; x++) {
2557
+ destPixels[destOffset + x] =
2558
+ blend_burn(destPixels[destOffset + x],
2559
+ srcBuffer[sY + (sX >> PRECISIONB)]);
2560
+ sX += dx;
2561
+ }
2562
+ destOffset += screenW;
2563
+ srcYOffset += dy;
2564
+ }
2565
+ break;
2566
+
2567
+ }
2568
+ }
2569
+ }
2570
+
2571
+
2572
+ private void filter_new_scanline() {
2573
+ sX = srcXOffset;
2574
+ fracV = srcYOffset & PREC_MAXVAL;
2575
+ ifV = PREC_MAXVAL - fracV + 1;
2576
+ v1 = (srcYOffset >> PRECISIONB) * iw;
2577
+ v2 = min((srcYOffset >> PRECISIONB) + 1, ih1) * iw;
2578
+ }
2579
+
2580
+
2581
+ private int filter_bilinear() {
2582
+ fracU = sX & PREC_MAXVAL;
2583
+ ifU = PREC_MAXVAL - fracU + 1;
2584
+ ul = (ifU * ifV) >> PRECISIONB;
2585
+ ll = ifU - ul;
2586
+ ur = ifV - ul;
2587
+ lr = PREC_MAXVAL + 1 - ul - ll - ur;
2588
+ u1 = (sX >> PRECISIONB);
2589
+ u2 = min(u1 + 1, iw1);
2590
+
2591
+ // get color values of the 4 neighbouring texels
2592
+ cUL = srcBuffer[v1 + u1];
2593
+ cUR = srcBuffer[v1 + u2];
2594
+ cLL = srcBuffer[v2 + u1];
2595
+ cLR = srcBuffer[v2 + u2];
2596
+
2597
+ r = ((ul*((cUL&RED_MASK)>>16) + ll*((cLL&RED_MASK)>>16) +
2598
+ ur*((cUR&RED_MASK)>>16) + lr*((cLR&RED_MASK)>>16))
2599
+ << PREC_RED_SHIFT) & RED_MASK;
2600
+
2601
+ g = ((ul*(cUL&GREEN_MASK) + ll*(cLL&GREEN_MASK) +
2602
+ ur*(cUR&GREEN_MASK) + lr*(cLR&GREEN_MASK))
2603
+ >>> PRECISIONB) & GREEN_MASK;
2604
+
2605
+ b = (ul*(cUL&BLUE_MASK) + ll*(cLL&BLUE_MASK) +
2606
+ ur*(cUR&BLUE_MASK) + lr*(cLR&BLUE_MASK))
2607
+ >>> PRECISIONB;
2608
+
2609
+ a = ((ul*((cUL&ALPHA_MASK)>>>24) + ll*((cLL&ALPHA_MASK)>>>24) +
2610
+ ur*((cUR&ALPHA_MASK)>>>24) + lr*((cLR&ALPHA_MASK)>>>24))
2611
+ << PREC_ALPHA_SHIFT) & ALPHA_MASK;
2612
+
2613
+ return a | r | g | b;
2614
+ }
2615
+
2616
+
2617
+
2618
+ //////////////////////////////////////////////////////////////
2619
+
2620
+ // internal blending methods
2621
+
2622
+
2623
+ private static int min(int a, int b) {
2624
+ return (a < b) ? a : b;
2625
+ }
2626
+
2627
+
2628
+ private static int max(int a, int b) {
2629
+ return (a > b) ? a : b;
2630
+ }
2631
+
2632
+
2633
+ /////////////////////////////////////////////////////////////
2634
+
2635
+ // BLEND MODE IMPLEMENTATIONS
2636
+
2637
+ /*
2638
+ * Jakub Valtar
2639
+ *
2640
+ * All modes use SRC alpha to interpolate between DST and the result of
2641
+ * the operation:
2642
+ *
2643
+ * R = (1 - SRC_ALPHA) * DST + SRC_ALPHA * <RESULT OF THE OPERATION>
2644
+ *
2645
+ * Comments above each mode only specify the formula of its operation.
2646
+ *
2647
+ * These implementations treat alpha 127 (=255/2) as a perfect 50 % mix.
2648
+ *
2649
+ * One alpha value between 126 and 127 is intentionally left out,
2650
+ * so the step 126 -> 127 is twice as big compared to other steps.
2651
+ * This is because our colors are in 0..255 range, but we divide
2652
+ * by right shifting 8 places (=256) which is much faster than
2653
+ * (correct) float division by 255.0f. The missing value was placed
2654
+ * between 126 and 127, because limits of the range (near 0 and 255) and
2655
+ * the middle value (127) have to blend correctly.
2656
+ *
2657
+ * Below you will often see RED and BLUE channels (RB) manipulated together
2658
+ * and GREEN channel (GN) manipulated separately. It is sometimes possible
2659
+ * because the operation won't use more than 16 bits, so we process the RED
2660
+ * channel in the upper 16 bits and BLUE channel in the lower 16 bits. This
2661
+ * decreases the number of operations per pixel and thus makes things faster.
2662
+ *
2663
+ * Some of the modes are hand tweaked (various +1s etc.) to be more accurate
2664
+ * and to produce correct values in extremes. Below is a sketch you can use
2665
+ * to check any blending function for
2666
+ *
2667
+ * 1) Discrepancies between color channels:
2668
+ * - highlighted by the offending color
2669
+ * 2) Behavior at extremes (set colorCount to 256):
2670
+ * - values of all corners are printed to the console
2671
+ * 3) Rounding errors:
2672
+ * - set colorCount to lower value to better see color bands
2673
+ *
2674
+
2675
+ // use powers of 2 in range 2..256
2676
+ // to better see color bands
2677
+ final int colorCount = 256;
2678
+
2679
+ final int blockSize = 3;
2680
+
2681
+ void settings() {
2682
+ size(blockSize * 256, blockSize * 256);
2683
+ }
2684
+
2685
+ void setup() { }
2686
+
2687
+ void draw() {
2688
+ noStroke();
2689
+ colorMode(RGB, colorCount-1);
2690
+ int alpha = (mouseX / blockSize) << 24;
2691
+ int r, g, b, r2, g2, b2 = 0;
2692
+ for (int x = 0; x <= 0xFF; x++) {
2693
+ for (int y = 0; y <= 0xFF; y++) {
2694
+ int dst = (x << 16) | (x << 8) | x;
2695
+ int src = (y << 16) | (y << 8) | y | alpha;
2696
+ int result = testFunction(dst, src);
2697
+ r = r2 = (result >> 16 & 0xFF);
2698
+ g = g2 = (result >> 8 & 0xFF);
2699
+ b = b2 = (result >> 0 & 0xFF);
2700
+ if (r != g && r != b) r2 = (128 + r2) % 255;
2701
+ if (g != r && g != b) g2 = (128 + g2) % 255;
2702
+ if (b != r && b != g) b2 = (128 + b2) % 255;
2703
+ fill(r2 % colorCount, g2 % colorCount, b2 % colorCount);
2704
+ rect(x * blockSize, y * blockSize, blockSize, blockSize);
2705
+ }
2706
+ }
2707
+ println(
2708
+ "alpha:", mouseX/blockSize,
2709
+ "TL:", hex(get(0, 0)),
2710
+ "TR:", hex(get(width-1, 0)),
2711
+ "BR:", hex(get(width-1, height-1)),
2712
+ "BL:", hex(get(0, height-1)));
2713
+ }
2714
+
2715
+ int testFunction(int dst, int src) {
2716
+ // your function here
2717
+ return dst;
2718
+ }
2719
+
2720
+ *
2721
+ *
2722
+ */
2723
+
2724
+ private static final int RB_MASK = 0x00FF00FF;
2725
+ private static final int GN_MASK = 0x0000FF00;
2726
+
2727
+ /**
2728
+ * Blend
2729
+ * O = S
2730
+ */
2731
+ private static int blend_blend(int dst, int src) {
2732
+ int a = src >>> 24;
2733
+
2734
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2735
+ int d_a = 0x100 - s_a;
2736
+
2737
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2738
+ ((dst & RB_MASK) * d_a + (src & RB_MASK) * s_a) >>> 8 & RB_MASK |
2739
+ ((dst & GN_MASK) * d_a + (src & GN_MASK) * s_a) >>> 8 & GN_MASK;
2740
+ }
2741
+
2742
+
2743
+ /**
2744
+ * Add
2745
+ * O = MIN(D + S, 1)
2746
+ */
2747
+ private static int blend_add_pin(int dst, int src) {
2748
+ int a = src >>> 24;
2749
+
2750
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2751
+
2752
+ int rb = (dst & RB_MASK) + ((src & RB_MASK) * s_a >>> 8 & RB_MASK);
2753
+ int gn = (dst & GN_MASK) + ((src & GN_MASK) * s_a >>> 8);
2754
+
2755
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2756
+ min(rb & 0xFFFF0000, RED_MASK) |
2757
+ min(gn & 0x00FFFF00, GREEN_MASK) |
2758
+ min(rb & 0x0000FFFF, BLUE_MASK);
2759
+ }
2760
+
2761
+
2762
+ /**
2763
+ * Subtract
2764
+ * O = MAX(0, D - S)
2765
+ */
2766
+ private static int blend_sub_pin(int dst, int src) {
2767
+ int a = src >>> 24;
2768
+
2769
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2770
+
2771
+ int rb = ((src & RB_MASK) * s_a >>> 8);
2772
+ int gn = ((src & GREEN_MASK) * s_a >>> 8);
2773
+
2774
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2775
+ max((dst & RED_MASK) - (rb & RED_MASK), 0) |
2776
+ max((dst & GREEN_MASK) - (gn & GREEN_MASK), 0) |
2777
+ max((dst & BLUE_MASK) - (rb & BLUE_MASK), 0);
2778
+ }
2779
+
2780
+
2781
+ /**
2782
+ * Lightest
2783
+ * O = MAX(D, S)
2784
+ */
2785
+ private static int blend_lightest(int dst, int src) {
2786
+ int a = src >>> 24;
2787
+
2788
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2789
+ int d_a = 0x100 - s_a;
2790
+
2791
+ int rb = max(src & RED_MASK, dst & RED_MASK) |
2792
+ max(src & BLUE_MASK, dst & BLUE_MASK);
2793
+ int gn = max(src & GREEN_MASK, dst & GREEN_MASK);
2794
+
2795
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2796
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
2797
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
2798
+ }
2799
+
2800
+
2801
+ /**
2802
+ * Darkest
2803
+ * O = MIN(D, S)
2804
+ */
2805
+ private static int blend_darkest(int dst, int src) {
2806
+ int a = src >>> 24;
2807
+
2808
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2809
+ int d_a = 0x100 - s_a;
2810
+
2811
+ int rb = min(src & RED_MASK, dst & RED_MASK) |
2812
+ min(src & BLUE_MASK, dst & BLUE_MASK);
2813
+ int gn = min(src & GREEN_MASK, dst & GREEN_MASK);
2814
+
2815
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2816
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
2817
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
2818
+ }
2819
+
2820
+
2821
+ /**
2822
+ * Difference
2823
+ * O = ABS(D - S)
2824
+ */
2825
+ private static int blend_difference(int dst, int src) {
2826
+ int a = src >>> 24;
2827
+
2828
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2829
+ int d_a = 0x100 - s_a;
2830
+
2831
+ int r = (dst & RED_MASK) - (src & RED_MASK);
2832
+ int b = (dst & BLUE_MASK) - (src & BLUE_MASK);
2833
+ int g = (dst & GREEN_MASK) - (src & GREEN_MASK);
2834
+
2835
+ int rb = (r < 0 ? -r : r) |
2836
+ (b < 0 ? -b : b);
2837
+ int gn = (g < 0 ? -g : g);
2838
+
2839
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2840
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
2841
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
2842
+ }
2843
+
2844
+
2845
+ /**
2846
+ * Exclusion
2847
+ * O = (1 - S)D + S(1 - D)
2848
+ * O = D + S - 2DS
2849
+ */
2850
+ private static int blend_exclusion(int dst, int src) {
2851
+ int a = src >>> 24;
2852
+
2853
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2854
+ int d_a = 0x100 - s_a;
2855
+
2856
+ int d_rb = dst & RB_MASK;
2857
+ int d_gn = dst & GN_MASK;
2858
+
2859
+ int s_gn = src & GN_MASK;
2860
+
2861
+ int f_r = (dst & RED_MASK) >> 16;
2862
+ int f_b = (dst & BLUE_MASK);
2863
+
2864
+ int rb_sub =
2865
+ ((src & RED_MASK) * (f_r + (f_r >= 0x7F ? 1 : 0)) |
2866
+ (src & BLUE_MASK) * (f_b + (f_b >= 0x7F ? 1 : 0)))
2867
+ >>> 7 & 0x01FF01FF;
2868
+ int gn_sub = s_gn * (d_gn + (d_gn >= 0x7F00 ? 0x100 : 0))
2869
+ >>> 15 & 0x0001FF00;
2870
+
2871
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2872
+ (d_rb * d_a + (d_rb + (src & RB_MASK) - rb_sub) * s_a) >>> 8 & RB_MASK |
2873
+ (d_gn * d_a + (d_gn + s_gn - gn_sub) * s_a) >>> 8 & GN_MASK;
2874
+ }
2875
+
2876
+
2877
+ /*
2878
+ * Multiply
2879
+ * O = DS
2880
+ */
2881
+ private static int blend_multiply(int dst, int src) {
2882
+ int a = src >>> 24;
2883
+
2884
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2885
+ int d_a = 0x100 - s_a;
2886
+
2887
+ int d_gn = dst & GN_MASK;
2888
+
2889
+ int f_r = (dst & RED_MASK) >> 16;
2890
+ int f_b = (dst & BLUE_MASK);
2891
+
2892
+ int rb =
2893
+ ((src & RED_MASK) * (f_r + 1) |
2894
+ (src & BLUE_MASK) * (f_b + 1))
2895
+ >>> 8 & RB_MASK;
2896
+ int gn =
2897
+ (src & GREEN_MASK) * (d_gn + 0x100)
2898
+ >>> 16 & GN_MASK;
2899
+
2900
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2901
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
2902
+ (d_gn * d_a + gn * s_a) >>> 8 & GN_MASK;
2903
+ }
2904
+
2905
+
2906
+ /**
2907
+ * Screen
2908
+ * O = 1 - (1 - D)(1 - S)
2909
+ * O = D + S - DS
2910
+ */
2911
+ private static int blend_screen(int dst, int src) {
2912
+ int a = src >>> 24;
2913
+
2914
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2915
+ int d_a = 0x100 - s_a;
2916
+
2917
+ int d_rb = dst & RB_MASK;
2918
+ int d_gn = dst & GN_MASK;
2919
+
2920
+ int s_gn = src & GN_MASK;
2921
+
2922
+ int f_r = (dst & RED_MASK) >> 16;
2923
+ int f_b = (dst & BLUE_MASK);
2924
+
2925
+ int rb_sub =
2926
+ ((src & RED_MASK) * (f_r + 1) |
2927
+ (src & BLUE_MASK) * (f_b + 1))
2928
+ >>> 8 & RB_MASK;
2929
+ int gn_sub = s_gn * (d_gn + 0x100)
2930
+ >>> 16 & GN_MASK;
2931
+
2932
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2933
+ (d_rb * d_a + (d_rb + (src & RB_MASK) - rb_sub) * s_a) >>> 8 & RB_MASK |
2934
+ (d_gn * d_a + (d_gn + s_gn - gn_sub) * s_a) >>> 8 & GN_MASK;
2935
+ }
2936
+
2937
+
2938
+ /**
2939
+ * Overlay
2940
+ * O = 2 * MULTIPLY(D, S) = 2DS for D < 0.5
2941
+ * O = 2 * SCREEN(D, S) - 1 = 2(S + D - DS) - 1 otherwise
2942
+ */
2943
+ private static int blend_overlay(int dst, int src) {
2944
+ int a = src >>> 24;
2945
+
2946
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2947
+ int d_a = 0x100 - s_a;
2948
+
2949
+ int d_r = dst & RED_MASK;
2950
+ int d_g = dst & GREEN_MASK;
2951
+ int d_b = dst & BLUE_MASK;
2952
+
2953
+ int s_r = src & RED_MASK;
2954
+ int s_g = src & GREEN_MASK;
2955
+ int s_b = src & BLUE_MASK;
2956
+
2957
+ int r = (d_r < 0x800000) ?
2958
+ d_r * ((s_r >>> 16) + 1) >>> 7 :
2959
+ 0xFF0000 - ((0x100 - (s_r >>> 16)) * (RED_MASK - d_r) >>> 7);
2960
+ int g = (d_g < 0x8000) ?
2961
+ d_g * (s_g + 0x100) >>> 15 :
2962
+ (0xFF00 - ((0x10000 - s_g) * (GREEN_MASK - d_g) >>> 15));
2963
+ int b = (d_b < 0x80) ?
2964
+ d_b * (s_b + 1) >>> 7 :
2965
+ (0xFF00 - ((0x100 - s_b) * (BLUE_MASK - d_b) << 1)) >>> 8;
2966
+
2967
+ return min((dst >>> 24) + a, 0xFF) << 24 |
2968
+ ((dst & RB_MASK) * d_a + ((r | b) & RB_MASK) * s_a) >>> 8 & RB_MASK |
2969
+ ((dst & GN_MASK) * d_a + (g & GN_MASK) * s_a) >>> 8 & GN_MASK;
2970
+ }
2971
+
2972
+
2973
+ /**
2974
+ * Hard Light
2975
+ * O = OVERLAY(S, D)
2976
+ *
2977
+ * O = 2 * MULTIPLY(D, S) = 2DS for S < 0.5
2978
+ * O = 2 * SCREEN(D, S) - 1 = 2(S + D - DS) - 1 otherwise
2979
+ */
2980
+ private static int blend_hard_light(int dst, int src) {
2981
+ int a = src >>> 24;
2982
+
2983
+ int s_a = a + (a >= 0x7F ? 1 : 0);
2984
+ int d_a = 0x100 - s_a;
2985
+
2986
+ int d_r = dst & RED_MASK;
2987
+ int d_g = dst & GREEN_MASK;
2988
+ int d_b = dst & BLUE_MASK;
2989
+
2990
+ int s_r = src & RED_MASK;
2991
+ int s_g = src & GREEN_MASK;
2992
+ int s_b = src & BLUE_MASK;
2993
+
2994
+ int r = (s_r < 0x800000) ?
2995
+ s_r * ((d_r >>> 16) + 1) >>> 7 :
2996
+ 0xFF0000 - ((0x100 - (d_r >>> 16)) * (RED_MASK - s_r) >>> 7);
2997
+ int g = (s_g < 0x8000) ?
2998
+ s_g * (d_g + 0x100) >>> 15 :
2999
+ (0xFF00 - ((0x10000 - d_g) * (GREEN_MASK - s_g) >>> 15));
3000
+ int b = (s_b < 0x80) ?
3001
+ s_b * (d_b + 1) >>> 7 :
3002
+ (0xFF00 - ((0x100 - d_b) * (BLUE_MASK - s_b) << 1)) >>> 8;
3003
+
3004
+ return min((dst >>> 24) + a, 0xFF) << 24 |
3005
+ ((dst & RB_MASK) * d_a + ((r | b) & RB_MASK) * s_a) >>> 8 & RB_MASK |
3006
+ ((dst & GN_MASK) * d_a + (g & GN_MASK) * s_a) >>> 8 & GN_MASK;
3007
+ }
3008
+
3009
+
3010
+ /**
3011
+ * Soft Light (Pegtop)
3012
+ * O = (1 - D) * MULTIPLY(D, S) + D * SCREEN(D, S)
3013
+ * O = (1 - D) * DS + D * (1 - (1 - D)(1 - S))
3014
+ * O = 2DS + DD - 2DDS
3015
+ */
3016
+ private static int blend_soft_light(int dst, int src) {
3017
+ int a = src >>> 24;
3018
+
3019
+ int s_a = a + (a >= 0x7F ? 1 : 0);
3020
+ int d_a = 0x100 - s_a;
3021
+
3022
+ int d_r = dst & RED_MASK;
3023
+ int d_g = dst & GREEN_MASK;
3024
+ int d_b = dst & BLUE_MASK;
3025
+
3026
+ int s_r1 = src & RED_MASK >> 16;
3027
+ int s_g1 = src & GREEN_MASK >> 8;
3028
+ int s_b1 = src & BLUE_MASK;
3029
+
3030
+ int d_r1 = (d_r >> 16) + (s_r1 < 7F ? 1 : 0);
3031
+ int d_g1 = (d_g >> 8) + (s_g1 < 7F ? 1 : 0);
3032
+ int d_b1 = d_b + (s_b1 < 7F ? 1 : 0);
3033
+
3034
+ int r = (s_r1 * d_r >> 7) + 0xFF * d_r1 * (d_r1 + 1) -
3035
+ ((s_r1 * d_r1 * d_r1) << 1) & RED_MASK;
3036
+ int g = (s_g1 * d_g << 1) + 0xFF * d_g1 * (d_g1 + 1) -
3037
+ ((s_g1 * d_g1 * d_g1) << 1) >>> 8 & GREEN_MASK;
3038
+ int b = (s_b1 * d_b << 9) + 0xFF * d_b1 * (d_b1 + 1) -
3039
+ ((s_b1 * d_b1 * d_b1) << 1) >>> 16;
3040
+
3041
+ return min((dst >>> 24) + a, 0xFF) << 24 |
3042
+ ((dst & RB_MASK) * d_a + (r | b) * s_a) >>> 8 & RB_MASK |
3043
+ ((dst & GN_MASK) * d_a + g * s_a) >>> 8 & GN_MASK;
3044
+ }
3045
+
3046
+
3047
+ /**
3048
+ * Dodge
3049
+ * O = D / (1 - S)
3050
+ */
3051
+ private static int blend_dodge(int dst, int src) {
3052
+ int a = src >>> 24;
3053
+
3054
+ int s_a = a + (a >= 0x7F ? 1 : 0);
3055
+ int d_a = 0x100 - s_a;
3056
+
3057
+ int r = (dst & RED_MASK) / (256 - ((src & RED_MASK) >> 16));
3058
+ int g = ((dst & GREEN_MASK) << 8) / (256 - ((src & GREEN_MASK) >> 8));
3059
+ int b = ((dst & BLUE_MASK) << 8) / (256 - (src & BLUE_MASK));
3060
+
3061
+ int rb =
3062
+ (r > 0xFF00 ? 0xFF0000 : ((r << 8) & RED_MASK)) |
3063
+ (b > 0x00FF ? 0x0000FF : b);
3064
+ int gn =
3065
+ (g > 0xFF00 ? 0x00FF00 : (g & GREEN_MASK));
3066
+
3067
+ return min((dst >>> 24) + a, 0xFF) << 24 |
3068
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
3069
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
3070
+ }
3071
+
3072
+
3073
+ /**
3074
+ * Burn
3075
+ * O = 1 - (1 - A) / B
3076
+ */
3077
+ private static int blend_burn(int dst, int src) {
3078
+ int a = src >>> 24;
3079
+
3080
+ int s_a = a + (a >= 0x7F ? 1 : 0);
3081
+ int d_a = 0x100 - s_a;
3082
+
3083
+ int r = ((0xFF0000 - (dst & RED_MASK))) / (1 + (src & RED_MASK >> 16));
3084
+ int g = ((0x00FF00 - (dst & GREEN_MASK)) << 8) / (1 + (src & GREEN_MASK >> 8));
3085
+ int b = ((0x0000FF - (dst & BLUE_MASK)) << 8) / (1 + (src & BLUE_MASK));
3086
+
3087
+ int rb = RB_MASK -
3088
+ (r > 0xFF00 ? 0xFF0000 : ((r << 8) & RED_MASK)) -
3089
+ (b > 0x00FF ? 0x0000FF : b);
3090
+ int gn = GN_MASK -
3091
+ (g > 0xFF00 ? 0x00FF00 : (g & GREEN_MASK));
3092
+
3093
+ return min((dst >>> 24) + a, 0xFF) << 24 |
3094
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
3095
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
3096
+ }
3097
+
3098
+
3099
+ //////////////////////////////////////////////////////////////
3100
+
3101
+ // FILE I/O
3102
+
3103
+
3104
+ static byte TIFF_HEADER[] = {
3105
+ 77, 77, 0, 42, 0, 0, 0, 8, 0, 9, 0, -2, 0, 4, 0, 0, 0, 1, 0, 0,
3106
+ 0, 0, 1, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 3, 0, 0, 0, 1,
3107
+ 0, 0, 0, 0, 1, 2, 0, 3, 0, 0, 0, 3, 0, 0, 0, 122, 1, 6, 0, 3, 0,
3108
+ 0, 0, 1, 0, 2, 0, 0, 1, 17, 0, 4, 0, 0, 0, 1, 0, 0, 3, 0, 1, 21,
3109
+ 0, 3, 0, 0, 0, 1, 0, 3, 0, 0, 1, 22, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0,
3110
+ 1, 23, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 8, 0, 8
3111
+ };
3112
+
3113
+
3114
+ static final String TIFF_ERROR =
3115
+ "Error: Processing can only read its own TIFF files.";
3116
+
3117
+ /**
3118
+ *
3119
+ * @param tiff
3120
+ * @return
3121
+ */
3122
+ static protected PImage loadTIFF(byte tiff[]) {
3123
+ if ((tiff[42] != tiff[102]) || // width/height in both places
3124
+ (tiff[43] != tiff[103])) {
3125
+ System.err.println(TIFF_ERROR);
3126
+ return null;
3127
+ }
3128
+
3129
+ int width =
3130
+ ((tiff[30] & 0xff) << 8) | (tiff[31] & 0xff);
3131
+ int height =
3132
+ ((tiff[42] & 0xff) << 8) | (tiff[43] & 0xff);
3133
+
3134
+ int count =
3135
+ ((tiff[114] & 0xff) << 24) |
3136
+ ((tiff[115] & 0xff) << 16) |
3137
+ ((tiff[116] & 0xff) << 8) |
3138
+ (tiff[117] & 0xff);
3139
+ if (count != width * height * 3) {
3140
+ System.err.println(TIFF_ERROR + " (" + width + ", " + height +")");
3141
+ return null;
3142
+ }
3143
+
3144
+ // check the rest of the header
3145
+ for (int i = 0; i < TIFF_HEADER.length; i++) {
3146
+ if ((i == 30) || (i == 31) || (i == 42) || (i == 43) ||
3147
+ (i == 102) || (i == 103) ||
3148
+ (i == 114) || (i == 115) || (i == 116) || (i == 117)) continue;
3149
+
3150
+ if (tiff[i] != TIFF_HEADER[i]) {
3151
+ System.err.println(TIFF_ERROR + " (" + i + ")");
3152
+ return null;
3153
+ }
3154
+ }
3155
+
3156
+ PImage outgoing = new PImage(width, height, RGB);
3157
+ int index = 768;
3158
+ count /= 3;
3159
+ for (int i = 0; i < count; i++) {
3160
+ outgoing.pixels[i] =
3161
+ 0xFF000000 |
3162
+ (tiff[index++] & 0xff) << 16 |
3163
+ (tiff[index++] & 0xff) << 8 |
3164
+ (tiff[index++] & 0xff);
3165
+ }
3166
+ return outgoing;
3167
+ }
3168
+
3169
+ /**
3170
+ *
3171
+ * @param output
3172
+ * @return
3173
+ */
3174
+ protected boolean saveTIFF(OutputStream output) {
3175
+ // shutting off the warning, people can figure this out themselves
3176
+ /*
3177
+ if (format != RGB) {
3178
+ System.err.println("Warning: only RGB information is saved with " +
3179
+ ".tif files. Use .tga or .png for ARGB images and others.");
3180
+ }
3181
+ */
3182
+ try {
3183
+ byte tiff[] = new byte[768];
3184
+ System.arraycopy(TIFF_HEADER, 0, tiff, 0, TIFF_HEADER.length);
3185
+
3186
+ tiff[30] = (byte) ((pixelWidth >> 8) & 0xff);
3187
+ tiff[31] = (byte) ((pixelWidth) & 0xff);
3188
+ tiff[42] = tiff[102] = (byte) ((pixelHeight >> 8) & 0xff);
3189
+ tiff[43] = tiff[103] = (byte) ((pixelHeight) & 0xff);
3190
+
3191
+ int count = pixelWidth*pixelHeight*3;
3192
+ tiff[114] = (byte) ((count >> 24) & 0xff);
3193
+ tiff[115] = (byte) ((count >> 16) & 0xff);
3194
+ tiff[116] = (byte) ((count >> 8) & 0xff);
3195
+ tiff[117] = (byte) ((count) & 0xff);
3196
+
3197
+ // spew the header to the disk
3198
+ output.write(tiff);
3199
+
3200
+ for (int i = 0; i < pixels.length; i++) {
3201
+ output.write((pixels[i] >> 16) & 0xff);
3202
+ output.write((pixels[i] >> 8) & 0xff);
3203
+ output.write(pixels[i] & 0xff);
3204
+ }
3205
+ output.flush();
3206
+ return true;
3207
+
3208
+ } catch (IOException e) {
3209
+ e.printStackTrace();
3210
+ }
3211
+ return false;
3212
+ }
3213
+
3214
+
3215
+ /**
3216
+ * Creates a Targa32 formatted byte sequence of specified
3217
+ * pixel buffer using RLE compression.
3218
+ * </p>
3219
+ * Also figured out how to avoid parsing the image upside-down
3220
+ * (there's a header flag to set the image origin to top-left)
3221
+ * </p>
3222
+ * Starting with revision 0092, the format setting is taken into account:
3223
+ * <UL>
3224
+ * <LI><TT>ALPHA</TT> images written as 8bit grayscale (uses lowest byte)
3225
+ * <LI><TT>RGB</TT> &rarr; 24 bits
3226
+ * <LI><TT>ARGB</TT> &rarr; 32 bits
3227
+ * </UL>
3228
+ * All versions are RLE compressed.
3229
+ * </p>
3230
+ * Contributed by toxi 8-10 May 2005, based on this RLE
3231
+ * <A HREF="http://www.wotsit.org/download.asp?f=tga">specification</A>
3232
+ * @param output
3233
+ * @return
3234
+ */
3235
+ protected boolean saveTGA(OutputStream output) {
3236
+ byte header[] = new byte[18];
3237
+
3238
+ if (format == ALPHA) { // save ALPHA images as 8bit grayscale
3239
+ header[2] = 0x0B;
3240
+ header[16] = 0x08;
3241
+ header[17] = 0x28;
3242
+
3243
+ } else if (format == RGB) {
3244
+ header[2] = 0x0A;
3245
+ header[16] = 24;
3246
+ header[17] = 0x20;
3247
+
3248
+ } else if (format == ARGB) {
3249
+ header[2] = 0x0A;
3250
+ header[16] = 32;
3251
+ header[17] = 0x28;
3252
+
3253
+ } else {
3254
+ throw new RuntimeException("Image format not recognized inside save()");
3255
+ }
3256
+ // set image dimensions lo-hi byte order
3257
+ header[12] = (byte) (pixelWidth & 0xff);
3258
+ header[13] = (byte) (pixelWidth >> 8);
3259
+ header[14] = (byte) (pixelHeight & 0xff);
3260
+ header[15] = (byte) (pixelHeight >> 8);
3261
+
3262
+ try {
3263
+ output.write(header);
3264
+
3265
+ int maxLen = pixelHeight * pixelWidth;
3266
+ int index = 0;
3267
+ int col; //, prevCol;
3268
+ int[] currChunk = new int[128];
3269
+
3270
+ // 8bit image exporter is in separate loop
3271
+ // to avoid excessive conditionals...
3272
+ if (format == ALPHA) {
3273
+ while (index < maxLen) {
3274
+ boolean isRLE = false;
3275
+ int rle = 1;
3276
+ currChunk[0] = col = pixels[index] & 0xff;
3277
+ while (index + rle < maxLen) {
3278
+ if (col != (pixels[index + rle]&0xff) || rle == 128) {
3279
+ isRLE = (rle > 1);
3280
+ break;
3281
+ }
3282
+ rle++;
3283
+ }
3284
+ if (isRLE) {
3285
+ output.write(0x80 | (rle - 1));
3286
+ output.write(col);
3287
+
3288
+ } else {
3289
+ rle = 1;
3290
+ while (index + rle < maxLen) {
3291
+ int cscan = pixels[index + rle] & 0xff;
3292
+ if ((col != cscan && rle < 128) || rle < 3) {
3293
+ currChunk[rle] = col = cscan;
3294
+ } else {
3295
+ if (col == cscan) rle -= 2;
3296
+ break;
3297
+ }
3298
+ rle++;
3299
+ }
3300
+ output.write(rle - 1);
3301
+ for (int i = 0; i < rle; i++) output.write(currChunk[i]);
3302
+ }
3303
+ index += rle;
3304
+ }
3305
+ } else { // export 24/32 bit TARGA
3306
+ while (index < maxLen) {
3307
+ boolean isRLE = false;
3308
+ currChunk[0] = col = pixels[index];
3309
+ int rle = 1;
3310
+ // try to find repeating bytes (min. len = 2 pixels)
3311
+ // maximum chunk size is 128 pixels
3312
+ while (index + rle < maxLen) {
3313
+ if (col != pixels[index + rle] || rle == 128) {
3314
+ isRLE = (rle > 1); // set flag for RLE chunk
3315
+ break;
3316
+ }
3317
+ rle++;
3318
+ }
3319
+ if (isRLE) {
3320
+ output.write(128 | (rle - 1));
3321
+ output.write(col & 0xff);
3322
+ output.write(col >> 8 & 0xff);
3323
+ output.write(col >> 16 & 0xff);
3324
+ if (format == ARGB) output.write(col >>> 24 & 0xff);
3325
+
3326
+ } else { // not RLE
3327
+ rle = 1;
3328
+ while (index + rle < maxLen) {
3329
+ if ((col != pixels[index + rle] && rle < 128) || rle < 3) {
3330
+ currChunk[rle] = col = pixels[index + rle];
3331
+ } else {
3332
+ // check if the exit condition was the start of
3333
+ // a repeating colour
3334
+ if (col == pixels[index + rle]) rle -= 2;
3335
+ break;
3336
+ }
3337
+ rle++;
3338
+ }
3339
+ // write uncompressed chunk
3340
+ output.write(rle - 1);
3341
+ if (format == ARGB) {
3342
+ for (int i = 0; i < rle; i++) {
3343
+ col = currChunk[i];
3344
+ output.write(col & 0xff);
3345
+ output.write(col >> 8 & 0xff);
3346
+ output.write(col >> 16 & 0xff);
3347
+ output.write(col >>> 24 & 0xff);
3348
+ }
3349
+ } else {
3350
+ for (int i = 0; i < rle; i++) {
3351
+ col = currChunk[i];
3352
+ output.write(col & 0xff);
3353
+ output.write(col >> 8 & 0xff);
3354
+ output.write(col >> 16 & 0xff);
3355
+ }
3356
+ }
3357
+ }
3358
+ index += rle;
3359
+ }
3360
+ }
3361
+ output.flush();
3362
+ return true;
3363
+
3364
+ } catch (IOException e) {
3365
+ e.printStackTrace();
3366
+ return false;
3367
+ }
3368
+ }
3369
+
3370
+
3371
+ /**
3372
+ * Use ImageIO functions from Java 1.4 and later to handle image save.
3373
+ * Various formats are supported, typically jpeg, png, bmp, and wbmp.
3374
+ * To get a list of the supported formats for writing, use: <BR>
3375
+ * <TT>println(javax.imageio.ImageIO.getReaderFormatNames())</TT>
3376
+ * @param path
3377
+ * @return
3378
+ * @throws java.io.IOException
3379
+ */
3380
+ protected boolean saveImageIO(String path) throws IOException {
3381
+ try {
3382
+ int outputFormat = (format == ARGB) ?
3383
+ BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB;
3384
+
3385
+ String extension =
3386
+ path.substring(path.lastIndexOf('.') + 1).toLowerCase();
3387
+
3388
+ // JPEG and BMP images that have an alpha channel set get pretty unhappy.
3389
+ // BMP just doesn't write, and JPEG writes it as a CMYK image.
3390
+ // http://code.google.com/p/processing/issues/detail?id=415
3391
+ if (extension.equals("bmp") || extension.equals("jpg") || extension.equals("jpeg")) {
3392
+ outputFormat = BufferedImage.TYPE_INT_RGB;
3393
+ }
3394
+
3395
+ BufferedImage bimage = new BufferedImage(pixelWidth, pixelHeight, outputFormat);
3396
+ bimage.setRGB(0, 0, pixelWidth, pixelHeight, pixels, 0, pixelWidth);
3397
+
3398
+ File file = new File(path);
3399
+
3400
+ ImageWriter writer = null;
3401
+ ImageWriteParam param = null;
3402
+ IIOMetadata metadata = null;
3403
+
3404
+ if (extension.equals("jpg") || extension.equals("jpeg")) {
3405
+ if ((writer = imageioWriter("jpeg")) != null) {
3406
+ // Set JPEG quality to 90% with baseline optimization. Setting this
3407
+ // to 1 was a huge jump (about triple the size), so this seems good.
3408
+ // Oddly, a smaller file size than Photoshop at 90%, but I suppose
3409
+ // it's a completely different algorithm.
3410
+ param = writer.getDefaultWriteParam();
3411
+ param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
3412
+ param.setCompressionQuality(0.9f);
3413
+ }
3414
+ }
3415
+
3416
+ if (extension.equals("png")) {
3417
+ if ((writer = imageioWriter("png")) != null) {
3418
+ param = writer.getDefaultWriteParam();
3419
+ if (false) {
3420
+ metadata = imageioDPI(writer, param, 100);
3421
+ }
3422
+ }
3423
+ }
3424
+
3425
+ if (writer != null) {
3426
+ BufferedOutputStream output =
3427
+ new BufferedOutputStream(PApplet.createOutput(file));
3428
+ writer.setOutput(ImageIO.createImageOutputStream(output));
3429
+ // writer.write(null, new IIOImage(bimage, null, null), param);
3430
+ writer.write(metadata, new IIOImage(bimage, null, metadata), param);
3431
+ writer.dispose();
3432
+
3433
+ output.flush();
3434
+ output.close();
3435
+ return true;
3436
+ }
3437
+ // If iter.hasNext() somehow fails up top, it falls through to here
3438
+ return javax.imageio.ImageIO.write(bimage, extension, file);
3439
+
3440
+ } catch (Exception e) {
3441
+ e.printStackTrace();
3442
+ throw new IOException("image save failed.");
3443
+ }
3444
+ }
3445
+
3446
+
3447
+ private ImageWriter imageioWriter(String extension) {
3448
+ Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName(extension);
3449
+ if (iter.hasNext()) {
3450
+ return iter.next();
3451
+ }
3452
+ return null;
3453
+ }
3454
+
3455
+
3456
+ private IIOMetadata imageioDPI(ImageWriter writer, ImageWriteParam param, double dpi) {
3457
+ // http://stackoverflow.com/questions/321736/how-to-set-dpi-information-in-an-image
3458
+ ImageTypeSpecifier typeSpecifier =
3459
+ ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
3460
+ IIOMetadata metadata =
3461
+ writer.getDefaultImageMetadata(typeSpecifier, param);
3462
+
3463
+ if (!metadata.isReadOnly() && metadata.isStandardMetadataFormatSupported()) {
3464
+ // for PNG, it's dots per millimeter
3465
+ double dotsPerMilli = dpi / 25.4;
3466
+
3467
+ IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
3468
+ horiz.setAttribute("value", Double.toString(dotsPerMilli));
3469
+
3470
+ IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
3471
+ vert.setAttribute("value", Double.toString(dotsPerMilli));
3472
+
3473
+ IIOMetadataNode dim = new IIOMetadataNode("Dimension");
3474
+ dim.appendChild(horiz);
3475
+ dim.appendChild(vert);
3476
+
3477
+ IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
3478
+ root.appendChild(dim);
3479
+
3480
+ try {
3481
+ metadata.mergeTree("javax_imageio_1.0", root);
3482
+ return metadata;
3483
+
3484
+ } catch (IIOInvalidTreeException e) {
3485
+ System.err.println("Could not set the DPI of the output image");
3486
+ e.printStackTrace();
3487
+ }
3488
+ }
3489
+ return null;
3490
+ }
3491
+
3492
+ /**
3493
+ *
3494
+ */
3495
+ protected String[] saveImageFormats;
3496
+
3497
+ /**
3498
+ * ( begin auto-generated from PImage_save.xml )
3499
+ *
3500
+ * Saves the image into a file. Append a file extension to the name of
3501
+ * the file, to indicate the file format to be used: either TIFF (.tif),
3502
+ * TARGA (.tga), JPEG (.jpg), or PNG (.png). If no extension is included
3503
+ * in the filename, the image will save in TIFF format and .tif will be
3504
+ * added to the name. These files are saved to the sketch's folder, which
3505
+ * may be opened by selecting "Show sketch folder" from the "Sketch" menu.
3506
+ * <br /><br />To save an image created within the code, rather
3507
+ * than through loading, it's necessary to make the image with the
3508
+ * <b>createImage()</b> function so it is aware of the location of the
3509
+ * program and can therefore save the file to the right place. See the
3510
+ * <b>createImage()</b> reference for more information.
3511
+ *
3512
+ * ( end auto-generated )
3513
+ * <h3>Advanced</h3>
3514
+ * Save this image to disk.
3515
+ * <p>
3516
+ * As of revision 0100, this function requires an absolute path,
3517
+ * in order to avoid confusion. To save inside the sketch folder,
3518
+ * use the function savePath() from PApplet, or use saveFrame() instead.
3519
+ * As of revision 0116, savePath() is not needed if this object has been
3520
+ * created (as recommended) via createImage() or createGraphics() or
3521
+ * one of its neighbors.
3522
+ * <p>
3523
+ * As of revision 0115, when using Java 1.4 and later, you can write
3524
+ * to several formats besides tga and tiff. If Java 1.4 is installed
3525
+ * and the extension used is supported (usually png, jpg, jpeg, bmp,
3526
+ * and tiff), then those methods will be used to write the image.
3527
+ * To get a list of the supported formats for writing, use: <BR>
3528
+ * <TT>println(javax.imageio.ImageIO.getReaderFormatNames())</TT>
3529
+ * <p>
3530
+ * To use the original built-in image writers, use .tga or .tif as the
3531
+ * extension, or don't include an extension. When no extension is used,
3532
+ * the extension .tif will be added to the file name.
3533
+ * <p>
3534
+ * The ImageIO API claims to support wbmp files, however they probably
3535
+ * require a black and white image. Basic testing produced a zero-length
3536
+ * file with no error.
3537
+ *
3538
+ * @return
3539
+ * @webref pimage:method
3540
+ * @brief Saves the image to a TIFF, TARGA, PNG, or JPEG file
3541
+ * @usage application
3542
+ * @param filename a sequence of letters and numbers
3543
+ */
3544
+ public boolean save(String filename) { // ignore
3545
+ boolean success = false;
3546
+
3547
+ if (parent != null) {
3548
+ // use savePath(), so that the intermediate directories are created
3549
+ filename = parent.savePath(filename);
3550
+
3551
+ } else {
3552
+ File file = new File(filename);
3553
+ if (file.isAbsolute()) {
3554
+ // make sure that the intermediate folders have been created
3555
+ PApplet.createPath(file);
3556
+ } else {
3557
+ String msg =
3558
+ "PImage.save() requires an absolute path. " +
3559
+ "Use createImage(), or pass savePath() to save().";
3560
+ PGraphics.showException(msg);
3561
+ }
3562
+ }
3563
+
3564
+ // Make sure the pixel data is ready to go
3565
+ loadPixels();
3566
+
3567
+ try {
3568
+ OutputStream os = null;
3569
+
3570
+ if (saveImageFormats == null) {
3571
+ saveImageFormats = javax.imageio.ImageIO.getWriterFormatNames();
3572
+ }
3573
+ if (saveImageFormats != null) {
3574
+ for (int i = 0; i < saveImageFormats.length; i++) {
3575
+ if (filename.endsWith("." + saveImageFormats[i])) {
3576
+ if (!saveImageIO(filename)) {
3577
+ System.err.println("Error while saving image.");
3578
+ return false;
3579
+ }
3580
+ return true;
3581
+ }
3582
+ }
3583
+ }
3584
+
3585
+ if (filename.toLowerCase().endsWith(".tga")) {
3586
+ os = new BufferedOutputStream(new FileOutputStream(filename), 32768);
3587
+ success = saveTGA(os); //, pixels, width, height, format);
3588
+
3589
+ } else {
3590
+ if (!filename.toLowerCase().endsWith(".tif") &&
3591
+ !filename.toLowerCase().endsWith(".tiff")) {
3592
+ // if no .tif extension, add it..
3593
+ filename += ".tif";
3594
+ }
3595
+ os = new BufferedOutputStream(new FileOutputStream(filename), 32768);
3596
+ success = saveTIFF(os); //, pixels, width, height);
3597
+ }
3598
+ os.flush();
3599
+ os.close();
3600
+
3601
+ } catch (IOException e) {
3602
+ System.err.println("Error while saving image.");
3603
+ e.printStackTrace();
3604
+ success = false;
3605
+ }
3606
+ return success;
3607
+ }
3608
+ }