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