propane 3.4.0-java → 3.7.0.pre-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/extensions.xml +1 -2
- data/.mvn/wrapper/MavenWrapperDownloader.java +2 -2
- data/.mvn/wrapper/maven-wrapper.properties +2 -2
- data/.travis.yml +2 -2
- data/CHANGELOG.md +12 -0
- data/Gemfile +2 -0
- data/README.md +17 -8
- data/Rakefile +10 -11
- data/bin/propane +3 -1
- data/lib/propane.rb +6 -4
- data/lib/propane/app.rb +20 -10
- 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 +23 -24
- 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 +14 -6
- data/lib/propane/version.rb +2 -1
- data/library/boids/boids.rb +21 -11
- data/library/color_group/color_group.rb +28 -0
- data/library/control_panel/control_panel.rb +8 -5
- data/library/dxf/dxf.rb +6 -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 +7 -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 +4 -1
- data/pom.rb +37 -36
- data/pom.xml +7 -7
- data/propane.gemspec +16 -12
- data/src/main/java/monkstone/ColorUtil.java +13 -1
- data/src/main/java/monkstone/MathToolModule.java +253 -203
- 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 +2 -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 +7 -6
- 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 +3 -3
- data/src/main/java/monkstone/videoevent/CaptureEvent.java +27 -0
- data/src/main/java/monkstone/videoevent/{VideoInterface.java → MovieEvent.java} +11 -27
- data/src/main/java/monkstone/videoevent/package-info.java +1 -1
- data/src/main/java/processing/awt/PGraphicsJava2D.java +781 -285
- data/src/main/java/processing/awt/PImageAWT.java +377 -0
- data/src/main/java/processing/awt/PShapeJava2D.java +56 -52
- data/src/main/java/processing/awt/PSurfaceAWT.java +309 -209
- data/src/main/java/processing/awt/ShimAWT.java +581 -0
- data/src/main/java/processing/core/PApplet.java +4510 -4503
- data/src/main/java/processing/core/PConstants.java +477 -447
- data/src/main/java/processing/core/PFont.java +914 -880
- data/src/main/java/processing/core/PGraphics.java +193 -177
- data/src/main/java/processing/core/PImage.java +611 -309
- data/src/main/java/processing/core/PMatrix.java +172 -159
- data/src/main/java/processing/core/PMatrix2D.java +478 -415
- data/src/main/java/processing/core/PMatrix3D.java +762 -735
- data/src/main/java/processing/core/PShape.java +2888 -2652
- data/src/main/java/processing/core/PShapeOBJ.java +97 -92
- data/src/main/java/processing/core/PShapeSVG.java +1705 -1490
- data/src/main/java/processing/core/PStyle.java +40 -37
- data/src/main/java/processing/core/PSurface.java +139 -97
- data/src/main/java/processing/core/PSurfaceNone.java +296 -218
- data/src/main/java/processing/core/PVector.java +997 -965
- data/src/main/java/processing/core/ThinkDifferent.java +15 -13
- 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 +3690 -3510
- data/src/main/java/processing/data/TableRow.java +182 -183
- data/src/main/java/processing/data/XML.java +957 -883
- data/src/main/java/processing/dxf/RawDXF.java +404 -0
- 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 +88 -113
- data/src/main/java/processing/event/TouchEvent.java +10 -6
- data/src/main/java/processing/javafx/PGraphicsFX2D.java +20 -345
- data/src/main/java/processing/javafx/PSurfaceFX.java +149 -121
- data/src/main/java/processing/net/Client.java +744 -0
- data/src/main/java/processing/net/Server.java +388 -0
- data/src/main/java/processing/opengl/FontTexture.java +289 -270
- data/src/main/java/processing/opengl/FrameBuffer.java +386 -364
- data/src/main/java/processing/opengl/LinePath.java +547 -500
- data/src/main/java/processing/opengl/LineStroker.java +588 -581
- data/src/main/java/processing/opengl/PGL.java +3047 -2914
- 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 +12378 -12075
- data/src/main/java/processing/opengl/PJOGL.java +1753 -1670
- data/src/main/java/processing/opengl/PShader.java +1266 -1257
- data/src/main/java/processing/opengl/PShapeOpenGL.java +4678 -4580
- data/src/main/java/processing/opengl/PSurfaceJOGL.java +1114 -1027
- data/src/main/java/processing/opengl/Texture.java +1492 -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 +33 -62
- metadata +56 -48
- data/src/main/java/processing/core/util/image/ImageLoadFacade.java +0 -161
- data/src/main/java/processing/core/util/image/ImageSaveFacade.java +0 -169
- data/src/main/java/processing/core/util/image/constants/TifConstants.java +0 -45
- data/src/main/java/processing/core/util/image/load/AwtImageLoadStrategy.java +0 -80
- data/src/main/java/processing/core/util/image/load/Base64StringImageLoadStrategy.java +0 -73
- data/src/main/java/processing/core/util/image/load/FallbackImageLoadStrategy.java +0 -70
- data/src/main/java/processing/core/util/image/load/ImageIoImageLoadStrategy.java +0 -132
- data/src/main/java/processing/core/util/image/load/ImageLoadStrategy.java +0 -48
- data/src/main/java/processing/core/util/image/load/ImageLoadUtil.java +0 -45
- data/src/main/java/processing/core/util/image/load/TgaImageLoadStrategy.java +0 -255
- data/src/main/java/processing/core/util/image/load/TiffImageLoadStrategy.java +0 -98
- data/src/main/java/processing/core/util/image/save/ImageSaveStrategy.java +0 -49
- data/src/main/java/processing/core/util/image/save/ImageSaveUtil.java +0 -48
- data/src/main/java/processing/core/util/image/save/ImageWriterImageSaveStrategy.java +0 -179
- data/src/main/java/processing/core/util/image/save/SaveImageException.java +0 -41
- data/src/main/java/processing/core/util/image/save/TgaImageSaveStrategy.java +0 -198
- data/src/main/java/processing/core/util/image/save/TiffImageSaveStrategy.java +0 -91
- data/src/main/java/processing/core/util/image/save/TiffNakedFilenameImageSaveStrategy.java +0 -57
- data/src/main/java/processing/core/util/io/InputFactory.java +0 -285
- data/src/main/java/processing/core/util/io/PathUtil.java +0 -109
- 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
@@ -0,0 +1,388 @@
|
|
1
|
+
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
2
|
+
|
3
|
+
/*
|
4
|
+
Server - basic network server implementation
|
5
|
+
Part of the Processing project - http://processing.org
|
6
|
+
|
7
|
+
Copyright (c) 2004-2007 Ben Fry and Casey Reas
|
8
|
+
The previous version of this code was developed by Hernando Barragan
|
9
|
+
|
10
|
+
This library is free software; you can redistribute it and/or
|
11
|
+
modify it under the terms of the GNU Lesser General Public
|
12
|
+
License as published by the Free Software Foundation; either
|
13
|
+
version 2.1 of the License, or (at your option) any later version.
|
14
|
+
|
15
|
+
This library is distributed in the hope that it will be useful,
|
16
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
18
|
+
Lesser General Public License for more details.
|
19
|
+
|
20
|
+
You should have received a copy of the GNU Lesser General
|
21
|
+
Public License along with this library; if not, write to the
|
22
|
+
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
23
|
+
Boston, MA 02111-1307 USA
|
24
|
+
*/
|
25
|
+
|
26
|
+
package processing.net;
|
27
|
+
|
28
|
+
import processing.core.*;
|
29
|
+
|
30
|
+
import java.io.*;
|
31
|
+
import java.lang.reflect.*;
|
32
|
+
import java.net.*;
|
33
|
+
|
34
|
+
|
35
|
+
/**
|
36
|
+
* ( begin auto-generated from Server.xml )
|
37
|
+
*
|
38
|
+
* A server sends and receives data to and from its associated clients
|
39
|
+
* (other programs connected to it). When a server is started, it begins
|
40
|
+
* listening for connections on the port specified by the <b>port</b>
|
41
|
+
* parameter. Computers have many ports for transferring data and some are
|
42
|
+
* commonly used so be sure to not select one of these. For example, web
|
43
|
+
* servers usually use port 80 and POP mail uses port 110.
|
44
|
+
*
|
45
|
+
*
|
46
|
+
* @webref net
|
47
|
+
* @usage application
|
48
|
+
* @brief The server class is used to create server objects which send and receives data to and from its associated clients (other programs connected to it).
|
49
|
+
* @instanceName server any variable of type Server
|
50
|
+
*/
|
51
|
+
public class Server implements Runnable {
|
52
|
+
PApplet parent;
|
53
|
+
Method serverEventMethod;
|
54
|
+
|
55
|
+
volatile Thread thread;
|
56
|
+
ServerSocket server;
|
57
|
+
int port;
|
58
|
+
|
59
|
+
protected final Object clientsLock = new Object[0];
|
60
|
+
/** Number of clients currently connected. */
|
61
|
+
public int clientCount;
|
62
|
+
/** Array of client objects, useful length is determined by clientCount. */
|
63
|
+
public Client[] clients;
|
64
|
+
|
65
|
+
|
66
|
+
/**
|
67
|
+
* @param parent typically use "this"
|
68
|
+
* @param port port used to transfer data
|
69
|
+
*/
|
70
|
+
public Server(PApplet parent, int port) {
|
71
|
+
this(parent, port, null);
|
72
|
+
}
|
73
|
+
|
74
|
+
|
75
|
+
/**
|
76
|
+
* @param parent typically use "this"
|
77
|
+
* @param port port used to transfer data
|
78
|
+
* @param host when multiple NICs are in use, the ip (or name) to bind from
|
79
|
+
*/
|
80
|
+
public Server(PApplet parent, int port, String host) {
|
81
|
+
this.parent = parent;
|
82
|
+
this.port = port;
|
83
|
+
|
84
|
+
try {
|
85
|
+
if (host == null) {
|
86
|
+
server = new ServerSocket(this.port);
|
87
|
+
} else {
|
88
|
+
server = new ServerSocket(this.port, 10, InetAddress.getByName(host));
|
89
|
+
}
|
90
|
+
//clients = new Vector();
|
91
|
+
clients = new Client[10];
|
92
|
+
|
93
|
+
thread = new Thread(this);
|
94
|
+
thread.start();
|
95
|
+
|
96
|
+
parent.registerMethod("dispose", this);
|
97
|
+
|
98
|
+
// reflection to check whether host applet has a call for
|
99
|
+
// public void serverEvent(Server s, Client c);
|
100
|
+
// which is called when a new guy connects
|
101
|
+
try {
|
102
|
+
serverEventMethod =
|
103
|
+
parent.getClass().getMethod("serverEvent", Server.class, Client.class);
|
104
|
+
} catch (Exception e) {
|
105
|
+
// no such method, or an error.. which is fine, just ignore
|
106
|
+
}
|
107
|
+
|
108
|
+
} catch (IOException e) {
|
109
|
+
//e.printStackTrace();
|
110
|
+
thread = null;
|
111
|
+
throw new RuntimeException(e);
|
112
|
+
//errorMessage("<init>", e);
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
|
117
|
+
/**
|
118
|
+
* ( begin auto-generated from Server_disconnect.xml )
|
119
|
+
*
|
120
|
+
* Disconnect a particular client.
|
121
|
+
*
|
122
|
+
*
|
123
|
+
* @brief Disconnect a particular client.
|
124
|
+
* @webref server:server
|
125
|
+
* @param client the client to disconnect
|
126
|
+
*/
|
127
|
+
public void disconnect(Client client) {
|
128
|
+
client.stop();
|
129
|
+
synchronized (clientsLock) {
|
130
|
+
int index = clientIndex(client);
|
131
|
+
if (index != -1) {
|
132
|
+
removeIndex(index);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
|
138
|
+
protected void removeIndex(int index) {
|
139
|
+
synchronized (clientsLock) {
|
140
|
+
clientCount--;
|
141
|
+
// shift down the remaining clients
|
142
|
+
for (int i = index; i < clientCount; i++) {
|
143
|
+
clients[i] = clients[i + 1];
|
144
|
+
}
|
145
|
+
// mark last empty var for garbage collection
|
146
|
+
clients[clientCount] = null;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
|
151
|
+
protected void disconnectAll() {
|
152
|
+
synchronized (clientsLock) {
|
153
|
+
for (int i = 0; i < clientCount; i++) {
|
154
|
+
try {
|
155
|
+
clients[i].stop();
|
156
|
+
} catch (Exception e) {
|
157
|
+
// ignore
|
158
|
+
}
|
159
|
+
clients[i] = null;
|
160
|
+
}
|
161
|
+
clientCount = 0;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
|
166
|
+
protected void addClient(Client client) {
|
167
|
+
synchronized (clientsLock) {
|
168
|
+
if (clientCount == clients.length) {
|
169
|
+
clients = (Client[]) PApplet.expand(clients);
|
170
|
+
}
|
171
|
+
clients[clientCount++] = client;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
protected int clientIndex(Client client) {
|
177
|
+
synchronized (clientsLock) {
|
178
|
+
for (int i = 0; i < clientCount; i++) {
|
179
|
+
if (clients[i] == client) {
|
180
|
+
return i;
|
181
|
+
}
|
182
|
+
}
|
183
|
+
return -1;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
|
188
|
+
/**
|
189
|
+
* ( begin auto-generated from Server_active.xml )
|
190
|
+
*
|
191
|
+
* Returns true if this server is still active and hasn't run
|
192
|
+
* into any trouble.
|
193
|
+
*
|
194
|
+
*
|
195
|
+
* @webref server:server
|
196
|
+
* @brief Return true if this server is still active.
|
197
|
+
*/
|
198
|
+
public boolean active() {
|
199
|
+
return thread != null;
|
200
|
+
}
|
201
|
+
|
202
|
+
|
203
|
+
static public String ip() {
|
204
|
+
try {
|
205
|
+
return InetAddress.getLocalHost().getHostAddress();
|
206
|
+
} catch (UnknownHostException e) {
|
207
|
+
|
208
|
+
return null;
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
|
213
|
+
// the last index used for available. can't just cycle through
|
214
|
+
// the clients in order from 0 each time, because if client 0 won't
|
215
|
+
// shut up, then the rest of the clients will never be heard from.
|
216
|
+
int lastAvailable = -1;
|
217
|
+
|
218
|
+
/**
|
219
|
+
* ( begin auto-generated from Server_available.xml )
|
220
|
+
*
|
221
|
+
* Returns the next client in line with a new message.
|
222
|
+
*
|
223
|
+
*
|
224
|
+
* @brief Returns the next client in line with a new message.
|
225
|
+
* @webref server
|
226
|
+
* @usage application
|
227
|
+
*/
|
228
|
+
public Client available() {
|
229
|
+
synchronized (clientsLock) {
|
230
|
+
int index = lastAvailable + 1;
|
231
|
+
if (index >= clientCount) index = 0;
|
232
|
+
|
233
|
+
for (int i = 0; i < clientCount; i++) {
|
234
|
+
int which = (index + i) % clientCount;
|
235
|
+
Client client = clients[which];
|
236
|
+
//Check for valid client
|
237
|
+
if (!client.active()){
|
238
|
+
removeIndex(which); //Remove dead client
|
239
|
+
i--; //Don't skip the next client
|
240
|
+
//If the client has data make sure lastAvailable
|
241
|
+
//doesn't end up skipping the next client
|
242
|
+
which--;
|
243
|
+
//fall through to allow data from dead clients
|
244
|
+
//to be retreived.
|
245
|
+
}
|
246
|
+
if (client.available() > 0) {
|
247
|
+
lastAvailable = which;
|
248
|
+
return client;
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
return null;
|
253
|
+
}
|
254
|
+
|
255
|
+
|
256
|
+
/**
|
257
|
+
* ( begin auto-generated from Server_stop.xml )
|
258
|
+
*
|
259
|
+
* Disconnects all clients and stops the server.
|
260
|
+
*
|
261
|
+
*
|
262
|
+
* <h3>Advanced</h3>
|
263
|
+
* Use this to shut down the server if you finish using it while your applet
|
264
|
+
* is still running. Otherwise, it will be automatically be shut down by the
|
265
|
+
* host PApplet using dispose(), which is identical.
|
266
|
+
* @brief Disconnects all clients and stops the server.
|
267
|
+
* @webref server
|
268
|
+
* @usage application
|
269
|
+
*/
|
270
|
+
public void stop() {
|
271
|
+
dispose();
|
272
|
+
}
|
273
|
+
|
274
|
+
|
275
|
+
/**
|
276
|
+
* Disconnect all clients and stop the server: internal use only.
|
277
|
+
*/
|
278
|
+
public void dispose() {
|
279
|
+
thread = null;
|
280
|
+
|
281
|
+
if (clients != null) {
|
282
|
+
disconnectAll();
|
283
|
+
clientCount = 0;
|
284
|
+
clients = null;
|
285
|
+
}
|
286
|
+
|
287
|
+
try {
|
288
|
+
if (server != null) {
|
289
|
+
server.close();
|
290
|
+
server = null;
|
291
|
+
}
|
292
|
+
} catch (IOException e) {
|
293
|
+
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
|
298
|
+
@Override
|
299
|
+
public void run() {
|
300
|
+
while (Thread.currentThread() == thread) {
|
301
|
+
try {
|
302
|
+
Socket socket = server.accept();
|
303
|
+
Client client = new Client(parent, socket);
|
304
|
+
synchronized (clientsLock) {
|
305
|
+
addClient(client);
|
306
|
+
if (serverEventMethod != null) {
|
307
|
+
try {
|
308
|
+
serverEventMethod.invoke(parent, this, client);
|
309
|
+
} catch (Exception e) {
|
310
|
+
System.err.println("Disabling serverEvent() for port " + port);
|
311
|
+
Throwable cause = e;
|
312
|
+
// unwrap the exception if it came from the user code
|
313
|
+
if (e instanceof InvocationTargetException && e.getCause() != null) {
|
314
|
+
cause = e.getCause();
|
315
|
+
}
|
316
|
+
cause.printStackTrace();
|
317
|
+
serverEventMethod = null;
|
318
|
+
}
|
319
|
+
}
|
320
|
+
}
|
321
|
+
} catch (SocketException e) {
|
322
|
+
//thrown when server.close() is called and server is waiting on accept
|
323
|
+
System.err.println("Server SocketException: " + e.getMessage());
|
324
|
+
thread = null;
|
325
|
+
} catch (IOException e) {
|
326
|
+
//errorMessage("run", e);
|
327
|
+
|
328
|
+
thread = null;
|
329
|
+
}
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
|
334
|
+
/**
|
335
|
+
* ( begin auto-generated from Server_write.xml )
|
336
|
+
*
|
337
|
+
* Writes a value to all the connected clients. It sends bytes out from the
|
338
|
+
* Server object.
|
339
|
+
*
|
340
|
+
*
|
341
|
+
* @webref server
|
342
|
+
* @brief Writes data to all connected clients
|
343
|
+
* @param data data to write
|
344
|
+
*/
|
345
|
+
public void write(int data) { // will also cover char
|
346
|
+
synchronized (clientsLock) {
|
347
|
+
int index = 0;
|
348
|
+
while (index < clientCount) {
|
349
|
+
if (clients[index].active()) {
|
350
|
+
clients[index].write(data);
|
351
|
+
index++;
|
352
|
+
} else {
|
353
|
+
removeIndex(index);
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}
|
357
|
+
}
|
358
|
+
|
359
|
+
|
360
|
+
public void write(byte data[]) {
|
361
|
+
synchronized (clientsLock) {
|
362
|
+
int index = 0;
|
363
|
+
while (index < clientCount) {
|
364
|
+
if (clients[index].active()) {
|
365
|
+
clients[index].write(data);
|
366
|
+
index++;
|
367
|
+
} else {
|
368
|
+
removeIndex(index);
|
369
|
+
}
|
370
|
+
}
|
371
|
+
}
|
372
|
+
}
|
373
|
+
|
374
|
+
|
375
|
+
public void write(String data) {
|
376
|
+
synchronized (clientsLock) {
|
377
|
+
int index = 0;
|
378
|
+
while (index < clientCount) {
|
379
|
+
if (clients[index].active()) {
|
380
|
+
clients[index].write(data);
|
381
|
+
index++;
|
382
|
+
} else {
|
383
|
+
removeIndex(index);
|
384
|
+
}
|
385
|
+
}
|
386
|
+
}
|
387
|
+
}
|
388
|
+
}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
2
|
+
|
1
3
|
/*
|
2
4
|
Part of the Processing project - http://processing.org
|
3
5
|
|
@@ -18,7 +20,8 @@
|
|
18
20
|
Public License along with this library; if not, write to the
|
19
21
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
20
22
|
Boston, MA 02111-1307 USA
|
21
|
-
|
23
|
+
*/
|
24
|
+
|
22
25
|
package processing.opengl;
|
23
26
|
|
24
27
|
import processing.core.PApplet;
|
@@ -30,204 +33,216 @@ import processing.core.PImage;
|
|
30
33
|
import java.util.HashMap;
|
31
34
|
|
32
35
|
/**
|
33
|
-
* All the infrastructure needed for optimized font rendering
|
34
|
-
* Basically, this special class is needed because
|
35
|
-
* handled by a separate PImage for each
|
36
|
-
*
|
37
|
-
*
|
38
|
-
*
|
39
|
-
*
|
40
|
-
*
|
41
|
-
*
|
36
|
+
* All the infrastructure needed for optimized font rendering
|
37
|
+
* in OpenGL. Basically, this special class is needed because
|
38
|
+
* fonts in Processing are handled by a separate PImage for each
|
39
|
+
* glyph. For performance reasons, all these glyphs should be
|
40
|
+
* stored in a single OpenGL texture (otherwise, rendering a
|
41
|
+
* string of text would involve binding and un-binding several
|
42
|
+
* textures.
|
43
|
+
* PFontTexture manages the correspondence between individual
|
44
|
+
* glyphs and the large OpenGL texture containing them. Also,
|
45
|
+
* in the case that the font size is very large, one single
|
46
|
+
* OpenGL texture might not be enough to store all the glyphs,
|
47
|
+
* so PFontTexture also takes care of spreading a single font
|
42
48
|
* over several textures.
|
43
|
-
*
|
44
49
|
* @author Andres Colubri
|
45
50
|
*/
|
46
51
|
class FontTexture implements PConstants {
|
52
|
+
protected PGL pgl;
|
53
|
+
protected boolean is3D;
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
protected int lastTex;
|
59
|
-
protected TextureInfo[] glyphTexinfos;
|
60
|
-
protected HashMap<PFont.Glyph, TextureInfo> texinfoMap;
|
61
|
-
|
62
|
-
public FontTexture(PGraphicsOpenGL pg, PFont font, boolean is3D) {
|
63
|
-
pgl = pg.pgl;
|
64
|
-
this.is3D = is3D;
|
65
|
-
|
66
|
-
initTexture(pg, font);
|
67
|
-
}
|
55
|
+
protected int minSize;
|
56
|
+
protected int maxSize;
|
57
|
+
protected int offsetX;
|
58
|
+
protected int offsetY;
|
59
|
+
protected int lineHeight;
|
60
|
+
protected Texture[] textures = null;
|
61
|
+
protected PImage[] images = null;
|
62
|
+
protected int lastTex;
|
63
|
+
protected TextureInfo[] glyphTexinfos;
|
64
|
+
protected HashMap<PFont.Glyph, TextureInfo> texinfoMap;
|
68
65
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
}
|
66
|
+
public FontTexture(PGraphicsOpenGL pg, PFont font, boolean is3D) {
|
67
|
+
pgl = pg.pgl;
|
68
|
+
this.is3D = is3D;
|
73
69
|
|
74
|
-
|
75
|
-
|
76
|
-
textures[i].dispose();
|
77
|
-
}
|
78
|
-
}
|
70
|
+
initTexture(pg, font);
|
71
|
+
}
|
79
72
|
|
80
|
-
protected void initTexture(PGraphicsOpenGL pg, PFont font) {
|
81
|
-
lastTex = -1;
|
82
73
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
PApplet.max(PGL.MAX_FONT_TEX_SIZE, 2 * spow));
|
74
|
+
protected void allocate() {
|
75
|
+
// Nothing to do here: the font textures will allocate
|
76
|
+
// themselves.
|
77
|
+
}
|
88
78
|
|
89
|
-
if (maxSize < spow) {
|
90
|
-
PGraphics.showWarning("The font size is too large to be properly "
|
91
|
-
+ "displayed with OpenGL");
|
92
|
-
}
|
93
79
|
|
94
|
-
|
80
|
+
protected void dispose() {
|
81
|
+
for (Texture texture : textures) {
|
82
|
+
texture.dispose();
|
83
|
+
}
|
84
|
+
}
|
95
85
|
|
96
|
-
offsetX = 0;
|
97
|
-
offsetY = 0;
|
98
|
-
lineHeight = 0;
|
99
86
|
|
100
|
-
|
101
|
-
|
102
|
-
|
87
|
+
protected void initTexture(PGraphicsOpenGL pg, PFont font) {
|
88
|
+
lastTex = -1;
|
89
|
+
|
90
|
+
int spow = PGL.nextPowerOfTwo(font.getSize());
|
91
|
+
minSize = PApplet.min(PGraphicsOpenGL.maxTextureSize,
|
92
|
+
PApplet.max(PGL.MIN_FONT_TEX_SIZE, spow));
|
93
|
+
maxSize = PApplet.min(PGraphicsOpenGL.maxTextureSize,
|
94
|
+
PApplet.max(PGL.MAX_FONT_TEX_SIZE, 2 * spow));
|
95
|
+
|
96
|
+
if (maxSize < spow) {
|
97
|
+
PGraphics.showWarning("The font size is too large to be properly " +
|
98
|
+
"displayed with OpenGL");
|
103
99
|
}
|
104
100
|
|
105
|
-
|
106
|
-
int w, h;
|
107
|
-
boolean resize;
|
108
|
-
|
109
|
-
w = maxSize;
|
110
|
-
if (-1 < lastTex && textures[lastTex].glHeight < maxSize) {
|
111
|
-
// The height of the current texture is less than the maximum, this
|
112
|
-
// means we can replace it with a larger texture.
|
113
|
-
h = PApplet.min(2 * textures[lastTex].glHeight, maxSize);
|
114
|
-
resize = true;
|
115
|
-
} else {
|
116
|
-
h = minSize;
|
117
|
-
resize = false;
|
118
|
-
}
|
101
|
+
addTexture(pg);
|
119
102
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
// either when it is magnified or minified...
|
124
|
-
tex = new Texture(pg, w, h,
|
125
|
-
new Texture.Parameters(ARGB, Texture.BILINEAR, false));
|
126
|
-
} else {
|
127
|
-
// ...however, the effect of bilinear sampling is to add some blurriness
|
128
|
-
// to the text in its original size. In 2D, we assume that text will be
|
129
|
-
// shown at its original size, so linear sampling is chosen instead (which
|
130
|
-
// only affects minimized text).
|
131
|
-
tex = new Texture(pg, w, h,
|
132
|
-
new Texture.Parameters(ARGB, Texture.LINEAR, false));
|
133
|
-
}
|
103
|
+
offsetX = 0;
|
104
|
+
offsetY = 0;
|
105
|
+
lineHeight = 0;
|
134
106
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
images[0] = pg.wrapTexture(tex);
|
140
|
-
lastTex = 0;
|
141
|
-
} else if (resize) {
|
142
|
-
// Replacing old smaller texture with larger one.
|
143
|
-
// But first we must copy the contents of the older
|
144
|
-
// texture into the new one.
|
145
|
-
Texture tex0 = textures[lastTex];
|
146
|
-
tex.put(tex0);
|
147
|
-
textures[lastTex] = tex;
|
148
|
-
|
149
|
-
pg.setCache(images[lastTex], tex);
|
150
|
-
images[lastTex].width = tex.width;
|
151
|
-
images[lastTex].height = tex.height;
|
152
|
-
} else {
|
153
|
-
// Adding new texture to the list.
|
154
|
-
lastTex = textures.length;
|
155
|
-
Texture[] tempTex = new Texture[lastTex + 1];
|
156
|
-
PApplet.arrayCopy(textures, tempTex, textures.length);
|
157
|
-
tempTex[lastTex] = tex;
|
158
|
-
textures = tempTex;
|
159
|
-
|
160
|
-
PImage[] tempImg = new PImage[textures.length];
|
161
|
-
PApplet.arrayCopy(images, tempImg, images.length);
|
162
|
-
tempImg[lastTex] = pg.wrapTexture(tex);
|
163
|
-
images = tempImg;
|
164
|
-
}
|
107
|
+
texinfoMap = new HashMap<>();
|
108
|
+
glyphTexinfos = new TextureInfo[font.getGlyphCount()];
|
109
|
+
addAllGlyphsToTexture(pg, font);
|
110
|
+
}
|
165
111
|
|
166
|
-
// Make sure that the current texture is bound.
|
167
|
-
tex.bind();
|
168
112
|
|
169
|
-
|
170
|
-
|
113
|
+
public boolean addTexture(PGraphicsOpenGL pg) {
|
114
|
+
int w, h;
|
115
|
+
boolean resize;
|
171
116
|
|
172
|
-
|
117
|
+
w = maxSize;
|
118
|
+
if (-1 < lastTex && textures[lastTex].glHeight < maxSize) {
|
119
|
+
// The height of the current texture is less than the maximum, this
|
120
|
+
// means we can replace it with a larger texture.
|
121
|
+
h = PApplet.min(2 * textures[lastTex].glHeight, maxSize);
|
122
|
+
resize = true;
|
123
|
+
} else {
|
124
|
+
h = minSize;
|
125
|
+
resize = false;
|
173
126
|
}
|
174
127
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
128
|
+
Texture tex;
|
129
|
+
if (is3D) {
|
130
|
+
// Bilinear sampling ensures that the texture doesn't look pixelated
|
131
|
+
// either when it is magnified or minified...
|
132
|
+
tex = new Texture(pg, w, h,
|
133
|
+
new Texture.Parameters(ARGB, Texture.BILINEAR, false));
|
134
|
+
} else {
|
135
|
+
// ...however, the effect of bilinear sampling is to add some blurriness
|
136
|
+
// to the text in its original size. In 2D, we assume that text will be
|
137
|
+
// shown at its original size, so linear sampling is chosen instead (which
|
138
|
+
// only affects minimized text).
|
139
|
+
tex = new Texture(pg, w, h,
|
140
|
+
new Texture.Parameters(ARGB, Texture.LINEAR, false));
|
179
141
|
}
|
180
142
|
|
181
|
-
|
182
|
-
|
143
|
+
if (textures == null) {
|
144
|
+
textures = new Texture[1];
|
145
|
+
textures[0] = tex;
|
146
|
+
images = new PImage[1];
|
147
|
+
images[0] = pg.wrapTexture(tex);
|
148
|
+
lastTex = 0;
|
149
|
+
} else if (resize) {
|
150
|
+
// Replacing old smaller texture with larger one.
|
151
|
+
// But first we must copy the contents of the older
|
152
|
+
// texture into the new one.
|
153
|
+
Texture tex0 = textures[lastTex];
|
154
|
+
tex.put(tex0);
|
155
|
+
textures[lastTex] = tex;
|
156
|
+
|
157
|
+
pg.setCache(images[lastTex], tex);
|
158
|
+
images[lastTex].width = tex.width;
|
159
|
+
images[lastTex].height = tex.height;
|
160
|
+
} else {
|
161
|
+
// Adding new texture to the list.
|
162
|
+
lastTex = textures.length;
|
163
|
+
Texture[] tempTex = new Texture[lastTex + 1];
|
164
|
+
PApplet.arrayCopy(textures, tempTex, textures.length);
|
165
|
+
tempTex[lastTex] = tex;
|
166
|
+
textures = tempTex;
|
167
|
+
|
168
|
+
PImage[] tempImg = new PImage[textures.length];
|
169
|
+
PApplet.arrayCopy(images, tempImg, images.length);
|
170
|
+
tempImg[lastTex] = pg.wrapTexture(tex);
|
171
|
+
images = tempImg;
|
183
172
|
}
|
184
173
|
|
185
|
-
//
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
174
|
+
// Make sure that the current texture is bound.
|
175
|
+
tex.bind();
|
176
|
+
|
177
|
+
return resize;
|
178
|
+
}
|
179
|
+
|
180
|
+
|
181
|
+
public void begin() {
|
182
|
+
}
|
183
|
+
|
184
|
+
|
185
|
+
public void end() {
|
186
|
+
for (Texture texture : textures) {
|
187
|
+
pgl.disableTexturing(texture.glTarget);
|
191
188
|
}
|
189
|
+
}
|
192
190
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
191
|
+
|
192
|
+
public PImage getTexture(TextureInfo info) {
|
193
|
+
return images[info.texIndex];
|
194
|
+
}
|
195
|
+
|
196
|
+
|
197
|
+
// Add all the current glyphs to opengl texture.
|
198
|
+
public void addAllGlyphsToTexture(PGraphicsOpenGL pg, PFont font) {
|
199
|
+
// loop over current glyphs.
|
200
|
+
for (int i = 0; i < font.getGlyphCount(); i++) {
|
201
|
+
addToTexture(pg, i, font.getGlyph(i));
|
201
202
|
}
|
203
|
+
}
|
204
|
+
|
202
205
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
+
public void updateGlyphsTexCoords() {
|
207
|
+
// loop over current glyphs.
|
208
|
+
for (TextureInfo tinfo : glyphTexinfos) {
|
209
|
+
if (tinfo != null && tinfo.texIndex == lastTex) {
|
210
|
+
tinfo.updateUV();
|
211
|
+
}
|
206
212
|
}
|
213
|
+
}
|
207
214
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
+
|
216
|
+
public TextureInfo getTexInfo(PFont.Glyph glyph) {
|
217
|
+
TextureInfo info = texinfoMap.get(glyph);
|
218
|
+
return info;
|
219
|
+
}
|
220
|
+
|
221
|
+
|
222
|
+
public TextureInfo addToTexture(PGraphicsOpenGL pg, PFont.Glyph glyph) {
|
223
|
+
int n = glyphTexinfos.length;
|
224
|
+
if (n == 0) {
|
225
|
+
glyphTexinfos = new TextureInfo[1];
|
215
226
|
}
|
227
|
+
addToTexture(pg, n, glyph);
|
228
|
+
return glyphTexinfos[n];
|
229
|
+
}
|
216
230
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
if (outdated) {
|
225
|
-
for (int i = 0; i < textures.length; i++) {
|
226
|
-
textures[i].dispose();
|
227
|
-
}
|
228
|
-
}
|
229
|
-
return outdated;
|
231
|
+
|
232
|
+
public boolean contextIsOutdated() {
|
233
|
+
boolean outdated = false;
|
234
|
+
for (Texture texture : textures) {
|
235
|
+
if (texture.contextIsOutdated()) {
|
236
|
+
outdated = true;
|
237
|
+
}
|
230
238
|
}
|
239
|
+
if (outdated) {
|
240
|
+
for (Texture texture : textures) {
|
241
|
+
texture.dispose();
|
242
|
+
}
|
243
|
+
}
|
244
|
+
return outdated;
|
245
|
+
}
|
231
246
|
|
232
247
|
// public void draw() {
|
233
248
|
// Texture tex = textures[lastTex];
|
@@ -235,125 +250,129 @@ class FontTexture implements PConstants {
|
|
235
250
|
// tex.glWidth, tex.glHeight,
|
236
251
|
// 0, 0, tex.glWidth, tex.glHeight);
|
237
252
|
// }
|
238
|
-
// Adds this glyph to the opengl texture in PFont.
|
239
|
-
protected void addToTexture(PGraphicsOpenGL pg, int idx, PFont.Glyph glyph) {
|
240
|
-
// We add one pixel to avoid issues when sampling the font texture at
|
241
|
-
// fractional screen positions. I.e.: the pixel on the screen only contains
|
242
|
-
// half of the font rectangle, so it would sample half of the color from the
|
243
|
-
// glyph area in the texture, and the other half from the contiguous pixel.
|
244
|
-
// If the later contains a portion of the neighbor glyph and the former
|
245
|
-
// doesn't, this would result in a shaded pixel when the correct output is
|
246
|
-
// blank. This is a consequence of putting all the glyphs in a common
|
247
|
-
// texture with bilinear sampling.
|
248
|
-
int w = 1 + glyph.width + 1;
|
249
|
-
int h = 1 + glyph.height + 1;
|
250
|
-
|
251
|
-
// Converting the pixels array from the PImage into a valid RGBA array for
|
252
|
-
// OpenGL.
|
253
|
-
int[] rgba = new int[w * h];
|
254
|
-
int t = 0;
|
255
|
-
int p = 0;
|
256
|
-
if (PGL.BIG_ENDIAN) {
|
257
|
-
java.util.Arrays.fill(rgba, 0, w, 0xFFFFFF00); // Set the first row to blank pixels.
|
258
|
-
t = w;
|
259
|
-
for (int y = 0; y < glyph.height; y++) {
|
260
|
-
rgba[t++] = 0xFFFFFF00; // Set the leftmost pixel in this row as blank
|
261
|
-
for (int x = 0; x < glyph.width; x++) {
|
262
|
-
rgba[t++] = 0xFFFFFF00 | glyph.image.pixels[p++];
|
263
|
-
}
|
264
|
-
rgba[t++] = 0xFFFFFF00; // Set the rightmost pixel in this row as blank
|
265
|
-
}
|
266
|
-
java.util.Arrays.fill(rgba, (h - 1) * w, h * w, 0xFFFFFF00); // Set the last row to blank pixels.
|
267
|
-
} else {
|
268
|
-
java.util.Arrays.fill(rgba, 0, w, 0x00FFFFFF); // Set the first row to blank pixels.
|
269
|
-
t = w;
|
270
|
-
for (int y = 0; y < glyph.height; y++) {
|
271
|
-
rgba[t++] = 0x00FFFFFF; // Set the leftmost pixel in this row as blank
|
272
|
-
for (int x = 0; x < glyph.width; x++) {
|
273
|
-
rgba[t++] = (glyph.image.pixels[p++] << 24) | 0x00FFFFFF;
|
274
|
-
}
|
275
|
-
rgba[t++] = 0x00FFFFFF; // Set the rightmost pixel in this row as blank
|
276
|
-
}
|
277
|
-
java.util.Arrays.fill(rgba, (h - 1) * w, h * w, 0x00FFFFFF); // Set the last row to blank pixels.
|
278
|
-
}
|
279
253
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
254
|
+
|
255
|
+
// Adds this glyph to the opengl texture in PFont.
|
256
|
+
protected void addToTexture(PGraphicsOpenGL pg, int idx, PFont.Glyph glyph) {
|
257
|
+
// We add one pixel to avoid issues when sampling the font texture at
|
258
|
+
// fractional screen positions. I.e.: the pixel on the screen only contains
|
259
|
+
// half of the font rectangle, so it would sample half of the color from the
|
260
|
+
// glyph area in the texture, and the other half from the contiguous pixel.
|
261
|
+
// If the later contains a portion of the neighbor glyph and the former
|
262
|
+
// doesn't, this would result in a shaded pixel when the correct output is
|
263
|
+
// blank. This is a consequence of putting all the glyphs in a common
|
264
|
+
// texture with bilinear sampling.
|
265
|
+
int w = 1 + glyph.width + 1;
|
266
|
+
int h = 1 + glyph.height + 1;
|
267
|
+
|
268
|
+
// Converting the pixels array from the PImage into a valid RGBA array for
|
269
|
+
// OpenGL.
|
270
|
+
int[] rgba = new int[w * h];
|
271
|
+
int t = 0;
|
272
|
+
int p = 0;
|
273
|
+
if (PGL.BIG_ENDIAN) {
|
274
|
+
java.util.Arrays.fill(rgba, 0, w, 0xFFFFFF00); // Set the first row to blank pixels.
|
275
|
+
t = w;
|
276
|
+
for (int y = 0; y < glyph.height; y++) {
|
277
|
+
rgba[t++] = 0xFFFFFF00; // Set the leftmost pixel in this row as blank
|
278
|
+
for (int x = 0; x < glyph.width; x++) {
|
279
|
+
rgba[t++] = 0xFFFFFF00 | glyph.image.pixels[p++];
|
285
280
|
}
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
} else {
|
297
|
-
// A new texture has been created. Reseting texture coordinates
|
298
|
-
// and line.
|
299
|
-
offsetX = 0;
|
300
|
-
offsetY = 0;
|
301
|
-
lineHeight = 0;
|
302
|
-
}
|
281
|
+
rgba[t++] = 0xFFFFFF00; // Set the rightmost pixel in this row as blank
|
282
|
+
}
|
283
|
+
java.util.Arrays.fill(rgba, (h - 1) * w, h * w, 0xFFFFFF00); // Set the last row to blank pixels.
|
284
|
+
} else {
|
285
|
+
java.util.Arrays.fill(rgba, 0, w, 0x00FFFFFF); // Set the first row to blank pixels.
|
286
|
+
t = w;
|
287
|
+
for (int y = 0; y < glyph.height; y++) {
|
288
|
+
rgba[t++] = 0x00FFFFFF; // Set the leftmost pixel in this row as blank
|
289
|
+
for (int x = 0; x < glyph.width; x++) {
|
290
|
+
rgba[t++] = (glyph.image.pixels[p++] << 24) | 0x00FFFFFF;
|
303
291
|
}
|
292
|
+
rgba[t++] = 0x00FFFFFF; // Set the rightmost pixel in this row as blank
|
293
|
+
}
|
294
|
+
java.util.Arrays.fill(rgba, (h - 1) * w, h * w, 0x00FFFFFF); // Set the last row to blank pixels.
|
295
|
+
}
|
304
296
|
|
305
|
-
|
306
|
-
|
297
|
+
// Is there room for this glyph in the current line?
|
298
|
+
if (offsetX + w > textures[lastTex].glWidth) {
|
299
|
+
// No room, go to the next line:
|
300
|
+
offsetX = 0;
|
301
|
+
offsetY += lineHeight;
|
302
|
+
}
|
303
|
+
lineHeight = Math.max(lineHeight, h);
|
304
|
+
|
305
|
+
boolean resized = false;
|
306
|
+
if (offsetY + lineHeight > textures[lastTex].glHeight) {
|
307
|
+
// We run out of space in the current texture, so we add a new texture:
|
308
|
+
resized = addTexture(pg);
|
309
|
+
if (resized) {
|
310
|
+
// Because the current texture has been resized, we need to
|
311
|
+
// update the UV coordinates of all the glyphs associated to it:
|
312
|
+
updateGlyphsTexCoords();
|
313
|
+
} else {
|
314
|
+
// A new texture has been created. Reseting texture coordinates
|
315
|
+
// and line.
|
316
|
+
offsetX = 0;
|
317
|
+
offsetY = 0;
|
318
|
+
lineHeight = 0;
|
319
|
+
}
|
320
|
+
}
|
307
321
|
|
308
|
-
|
309
|
-
|
310
|
-
System.arraycopy(glyphTexinfos, 0, temp, 0, glyphTexinfos.length);
|
311
|
-
glyphTexinfos = temp;
|
312
|
-
}
|
322
|
+
TextureInfo tinfo = new TextureInfo(lastTex, offsetX, offsetY, w, h, rgba);
|
323
|
+
offsetX += w;
|
313
324
|
|
314
|
-
|
315
|
-
|
325
|
+
if (idx == glyphTexinfos.length) {
|
326
|
+
TextureInfo[] temp = new TextureInfo[glyphTexinfos.length + 1];
|
327
|
+
System.arraycopy(glyphTexinfos, 0, temp, 0, glyphTexinfos.length);
|
328
|
+
glyphTexinfos = temp;
|
316
329
|
}
|
317
330
|
|
318
|
-
|
331
|
+
glyphTexinfos[idx] = tinfo;
|
332
|
+
texinfoMap.put(glyph, tinfo);
|
333
|
+
}
|
334
|
+
|
319
335
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
336
|
+
class TextureInfo {
|
337
|
+
int texIndex;
|
338
|
+
int width;
|
339
|
+
int height;
|
340
|
+
int[] crop;
|
341
|
+
float u0, u1;
|
342
|
+
float v0, v1;
|
343
|
+
int[] pixels;
|
327
344
|
|
328
|
-
|
345
|
+
TextureInfo(int tidx, int cropX, int cropY, int cropW, int cropH,
|
329
346
|
int[] pix) {
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
347
|
+
texIndex = tidx;
|
348
|
+
crop = new int[4];
|
349
|
+
// The region of the texture corresponding to the glyph is surrounded by a
|
350
|
+
// 1-pixel wide border to avoid artifacts due to bilinear sampling. This
|
351
|
+
// is why the additions and subtractions to the crop values.
|
352
|
+
crop[0] = cropX + 1;
|
353
|
+
crop[1] = cropY + 1 + cropH - 2;
|
354
|
+
crop[2] = cropW - 2;
|
355
|
+
crop[3] = -cropH + 2;
|
356
|
+
pixels = pix;
|
357
|
+
updateUV();
|
358
|
+
updateTex();
|
359
|
+
}
|
343
360
|
|
344
|
-
void updateUV() {
|
345
|
-
width = textures[texIndex].glWidth;
|
346
|
-
height = textures[texIndex].glHeight;
|
347
361
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
v1 = v0 - (float) crop[3] / (float) height;
|
352
|
-
}
|
362
|
+
void updateUV() {
|
363
|
+
width = textures[texIndex].glWidth;
|
364
|
+
height = textures[texIndex].glHeight;
|
353
365
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
366
|
+
u0 = (float)crop[0] / (float)width;
|
367
|
+
u1 = u0 + (float)crop[2] / (float)width;
|
368
|
+
v0 = (float)(crop[1] + crop[3]) / (float)height;
|
369
|
+
v1 = v0 - (float)crop[3] / (float)height;
|
370
|
+
}
|
371
|
+
|
372
|
+
|
373
|
+
void updateTex() {
|
374
|
+
textures[texIndex].setNative(pixels, crop[0] - 1, crop[1] + crop[3] - 1,
|
375
|
+
crop[2] + 2, -crop[3] + 2);
|
358
376
|
}
|
377
|
+
}
|
359
378
|
}
|