propane 2.7.2-java → 2.8.0.pre-java

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