propane 3.9.0-java → 3.10.0-java

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