BlinkenLibJava v.0.1.1 (2006-10-17)
Christian Heimke

Christian Heimke commited on 2011-07-15 08:55:06
Showing 6 changed files, with 1898 additions and 0 deletions.

... ...
@@ -0,0 +1,9 @@
1
+BlinkenLightsInteractiveMovieProgram
2
+Copyright (C) 2004-2006: Stefan Schuermans <1stein@schuermans.info>
3
+Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
4
+a blinkenarea.org project
5
+
6
+version 0.1.1 date 2006-10-17
7
+-----------------------------
8
+ - fixed bug in resizing frames if adding channels
9
+ - first public version
... ...
@@ -0,0 +1,34 @@
1
+# BlinkenLib
2
+# version 0.1.1 date 2006-10-17
3
+# Copyright (C) 2004-2006: Stefan Schuermans <1stein@schuermans.info>
4
+# Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+# a blinkenarea.org project
6
+
7
+JAVAC=javac
8
+JAR=jar
9
+KEYTOOL=keytool
10
+JARSIGNER=jarsigner
11
+KEYPASS=BlinkenLib
12
+JAVA=java
13
+P=org/blinkenarea/BlinkenLib
14
+
15
+CLASS_FILES=$(P)/BlinkenConstants.class $(P)/BlinkenFrame.class $(P)/BlinkenMovie.class
16
+
17
+.phony: all clean jar
18
+
19
+all: jar
20
+
21
+clean:
22
+	rm -f $(CLASS_FILES) BlinkenLib.jar
23
+
24
+jar: BlinkenLib.jar
25
+
26
+%.class: %.java
27
+	$(JAVAC) $<
28
+
29
+BlinkenLib.keystore:
30
+	$(KEYTOOL) -genkey -alias BlinkenLib -keystore BlinkenLib.keystore -keypass $(KEYPASS) -storepass $(KEYPASS)
31
+
32
+BlinkenLib.jar: BlinkenLib.keystore $(CLASS_FILES)
33
+	$(JAR) cf BlinkenLib.jar $(CLASS_FILES)
34
+	$(JARSIGNER) -keystore BlinkenLib.keystore -storepass $(KEYPASS) BlinkenLib.jar BlinkenLib
... ...
@@ -0,0 +1,30 @@
1
+#! /bin/sh
2
+
3
+NAME=BlinkenLibJava
4
+VERSION_FILE=Makefile
5
+
6
+VERSION=$(cat $VERSION_FILE | grep 'version [0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?' | sed 's/^.*version \([0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?\).*$/\1/')
7
+DATE=$(cat $VERSION_FILE | grep 'date [0-9]\+-[0-9]\+-[0-9]\+' | sed 's/^.*date \([0-9]\+-[0-9]\+-[0-9]\+\).*$/\1/')
8
+
9
+PACK="${NAME}-${VERSION}_${DATE}"
10
+
11
+FILES="ChangeLog Makefile org/blinkenarea/BlinkenLib/*.java mkpack.sh"
12
+
13
+rm -rf $PACK
14
+mkdir $PACK
15
+
16
+for FILE in $FILES
17
+do
18
+  mkdir -p $(dirname $PACK/$FILE)
19
+  cp $FILE $PACK/$FILE
20
+done
21
+
22
+ARCH=$PACK.tar.bz2
23
+if [ -f $ARCH ]
24
+then
25
+  ARCH=$ARCH.new
26
+fi
27
+
28
+tar jcf $ARCH $PACK
29
+rm -rf $PACK
30
+
... ...
@@ -0,0 +1,31 @@
1
+/* BlinkenLib
2
+ * version 0.1.1 date 2006-10-17
3
+ * Copyright (C) 2004-2006: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ */
7
+
8
+package org.blinkenarea.BlinkenLib;
9
+
10
+import java.awt.*;
11
+
12
+public class BlinkenConstants
13
+{
14
+
15
+  public static int BlinkenHeightMin = 1;
16
+  public static int BlinkenHeightMax = 1024;
17
+  public static int BlinkenWidthMin = 1;
18
+  public static int BlinkenWidthMax = 1024;
19
+  public static int BlinkenChannelsMin = 1;
20
+  public static int BlinkenChannelsMax = 16;
21
+  public static int BlinkenMaxvalMin = 1;
22
+  public static int BlinkenMaxvalMax = 255;
23
+  public static int BlinkenDurationMin = 1;
24
+  public static int BlinkenDurationMax = 65535;
25
+
26
+  public static int BlinkenProtoNone = 0x6E6F6F65; // 'n' 'o' 'n' 'e'
27
+  public static int BlinkenProtoBlp = 0x00424C50; // 0 'B' 'L' 'P'
28
+  public static int BlinkenProtoEblp = 0x45424C50; // 'E' 'B' 'L' 'P'
29
+  public static int BlinkenProtoMcuf = 0x4D435546; // 'M' 'C' 'U' 'F'
30
+
31
+} //public class BlinkenConstants
... ...
@@ -0,0 +1,547 @@
1
+/* BlinkenLib
2
+ * version 0.1.1 date 2006-10-17
3
+ * Copyright (C) 2004-2006: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ */
7
+
8
+package org.blinkenarea.BlinkenLib;
9
+
10
+import java.awt.*;
11
+import org.blinkenarea.BlinkenLib.*;
12
+
13
+public class BlinkenFrame
14
+{
15
+
16
+  private static byte [] BlinkenProtoBlpMagic = {(byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF};
17
+  private static byte [] BlinkenProtoEblpMagic = {(byte)0xFE, (byte)0xED, (byte)0xBE, (byte)0xEF};
18
+  private static byte [] BlinkenProtoMcufMagic = {(byte)0x23, (byte)0x54, (byte)0x26, (byte)0x66};
19
+
20
+  private int height;
21
+  private int width;
22
+  private int channels;
23
+  private int maxval;
24
+  private int duration;
25
+  private byte[][] data;
26
+
27
+  public BlinkenFrame( int height, int width, int channels, int maxval, int duration )
28
+  {
29
+    if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin;
30
+    if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax;
31
+    if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin;
32
+    if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax;
33
+    if( channels < BlinkenConstants.BlinkenChannelsMin ) channels = BlinkenConstants.BlinkenChannelsMin;
34
+    if( channels > BlinkenConstants.BlinkenChannelsMax ) channels = BlinkenConstants.BlinkenChannelsMax;
35
+    if( maxval < BlinkenConstants.BlinkenMaxvalMin ) maxval = BlinkenConstants.BlinkenMaxvalMin;
36
+    if( maxval > BlinkenConstants.BlinkenMaxvalMax ) maxval = BlinkenConstants.BlinkenMaxvalMax;
37
+    if( duration < BlinkenConstants.BlinkenDurationMin ) duration = BlinkenConstants.BlinkenDurationMin;
38
+    if( duration > BlinkenConstants.BlinkenDurationMax ) duration = BlinkenConstants.BlinkenDurationMax;
39
+
40
+    this.height = height;
41
+    this.width = width;
42
+    this.channels = channels;
43
+    this.maxval = maxval;
44
+    this.duration = duration;
45
+    data = new byte[height][width * channels];
46
+  }
47
+
48
+  public BlinkenFrame( BlinkenFrame frame )
49
+  {
50
+    int y, x, c, i;
51
+    height = frame.height;
52
+    width = frame.width;
53
+    channels = frame.channels;
54
+    maxval = frame.maxval;
55
+    duration = frame.duration;
56
+    data = new byte[height][width * channels];
57
+    for( y = 0; y < height; y++ )
58
+      for( x = 0, i = 0; x < width; x++ )
59
+        for( c = 0; c < channels; c++, i++ )
60
+          data[y][i] = frame.data[y][i];
61
+  }
62
+
63
+  public void clear( )
64
+  {
65
+    int x, y, c, i;
66
+    for( y = 0; y < height; y++ )
67
+      for( x = 0, i = 0; x < width; x++ )
68
+        for( c = 0; c < channels; c++, i++ )
69
+          data[y][i] = 0;
70
+  }
71
+
72
+  public int getHeight( )
73
+  {
74
+    return height;
75
+  }
76
+
77
+  public int getWidth( )
78
+  {
79
+    return width;
80
+  }
81
+
82
+  public int getChannels( )
83
+  {
84
+    return channels;
85
+  }
86
+
87
+  public int getMaxval( )
88
+  {
89
+    return maxval;
90
+  }
91
+
92
+  public int getDuration( )
93
+  {
94
+    return duration;
95
+  }
96
+
97
+  public void setDuration( int duration )
98
+  {
99
+    if( duration < BlinkenConstants.BlinkenDurationMin ) duration = BlinkenConstants.BlinkenDurationMin;
100
+    if( duration > BlinkenConstants.BlinkenDurationMax ) duration = BlinkenConstants.BlinkenDurationMax;
101
+    this.duration = duration;
102
+  }
103
+
104
+  public byte getPixel( int y, int x, int c )
105
+  {
106
+    if( y < 0 || y >= height ||
107
+        x < 0 || x >= width ||
108
+	c < 0 || c >= channels )
109
+      return 0;
110
+    return data[y][x * channels + c];
111
+  }
112
+
113
+  public void setPixel( int y, int x, int c, byte val )
114
+  {
115
+    if( y < 0 || y >= height ||
116
+        x < 0 || x >= width ||
117
+	c < 0 || c >= channels )
118
+      return;
119
+    if( val > maxval )
120
+      val = (byte)maxval;
121
+    data[y][x * channels + c] = val;
122
+  }
123
+
124
+  public Color getColor( int y, int x )
125
+  {
126
+    if( y < 0 || y >= height ||
127
+        x < 0 || x >= width ||
128
+        channels < 1 )
129
+      return new Color( 0, 0, 0 );
130
+    if( channels == 1 )
131
+      return new Color( (((int)data[y][x * 1 + 0] & 0xFF) * 255 + maxval / 2) / maxval,
132
+                        (((int)data[y][x * 1 + 0] & 0xFF) * 255 + maxval / 2) / maxval,
133
+                        (((int)data[y][x * 1 + 0] & 0xFF) * 255 + maxval / 2) / maxval );
134
+    if( channels == 2 )
135
+      return new Color( (((int)data[y][x * 2 + 0] & 0xFF) * 255 + maxval / 2) / maxval,
136
+                        (((int)data[y][x * 2 + 1] & 0xFF) * 255 + maxval / 2) / maxval, 0 );
137
+    int i = x * channels;
138
+    return new Color( (((int)data[y][i + 0] & 0xFF) * 255 + maxval / 2) / maxval,
139
+                      (((int)data[y][i + 1] & 0xFF) * 255 + maxval / 2) / maxval,
140
+                      (((int)data[y][i + 2] & 0xFF) * 255 + maxval / 2) / maxval );
141
+  }
142
+
143
+  public void setColor( int y, int x, Color color )
144
+  {
145
+    int alpha, alpha_, c;
146
+    if( y < 0 || y >= height ||
147
+        x < 0 || x >= width )
148
+      return;
149
+    int i = x * channels;
150
+    alpha = color.getAlpha( );
151
+    alpha_ = 255 - alpha;
152
+    if( channels >= 1 )
153
+      data[y][i + 0] = (byte)(( ((color.getRed( ) * maxval + 127) / 255) * alpha
154
+                              + ((int)data[y][i + 0] & 0xFF) * alpha_
155
+                              ) / 255);
156
+    if( channels >= 2 )
157
+      data[y][i + 1] = (byte)(( ((color.getGreen( ) * maxval + 127) / 255) * alpha
158
+                              + ((int)data[y][i + 1] & 0xFF) * alpha_
159
+                              ) / 255);
160
+    if( channels >= 3 )
161
+      data[y][i + 2] = (byte)(( ((color.getBlue( ) * maxval + 127) / 255) * alpha
162
+                              + ((int)data[y][i + 2] & 0xFF) * alpha_
163
+                              ) / 255);
164
+    for( c = 3; c < channels; c++ )
165
+      data[y][i + c] = (byte)(( 0
166
+                              + ((int)data[y][i + c] & 0xFF) * alpha_
167
+                              ) / 255);
168
+  }
169
+
170
+  public void resize( int height, int width, int channels, int maxval )
171
+  {
172
+    byte[][] new_data;
173
+    int y, x, c, i;
174
+    int emptyY, emptyX, skipY, skipX, rangeY, rangeX, val, div;
175
+
176
+    if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin;
177
+    if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax;
178
+    if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin;
179
+    if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax;
180
+    if( channels < BlinkenConstants.BlinkenChannelsMin ) channels = BlinkenConstants.BlinkenChannelsMin;
181
+    if( channels > BlinkenConstants.BlinkenChannelsMax ) channels = BlinkenConstants.BlinkenChannelsMax;
182
+    if( maxval < BlinkenConstants.BlinkenMaxvalMin ) maxval = BlinkenConstants.BlinkenMaxvalMin;
183
+    if( maxval > BlinkenConstants.BlinkenMaxvalMax ) maxval = BlinkenConstants.BlinkenMaxvalMax;
184
+
185
+    if( height == this.height &&
186
+        width == this.width &&
187
+        channels == this.channels &&
188
+        maxval == this.maxval )
189
+      return;
190
+
191
+    //allocate new data array
192
+    new_data = new byte[height][width * channels];
193
+    for( y = 0; y < height; y++ )
194
+      for( x = 0, i = 0; x < width; x++ )
195
+        for( c = 0; c < channels; c++, i++ )
196
+          new_data[y][i] = 0;
197
+
198
+    //get number of pixels to skip / to leave empty in X and Y direction
199
+    if( height > this.height )
200
+    {
201
+      emptyY = (height - this.height) / 2; 
202
+      skipY = 0;
203
+      rangeY = this.height;
204
+    }
205
+    else
206
+    {
207
+      emptyY = 0;
208
+      skipY = (this.height - height) / 2;
209
+      rangeY = height;
210
+    }
211
+    if( width > this.width )
212
+    {
213
+      emptyX = (width - this.width) / 2; 
214
+      skipX = 0;
215
+      rangeX = this.width;
216
+    }
217
+    else
218
+    {
219
+      emptyX = 0;
220
+      skipX = (this.width - width) / 2;
221
+      rangeX = width;
222
+    }
223
+
224
+    //resize frame with help of calculated parameters
225
+    for( y = 0; y < rangeY; y++ )
226
+    {
227
+      for( x = 0; x < rangeX; x++ )
228
+      {
229
+        int srcI = (skipX + x) * this.channels;
230
+        int destI = (emptyX + x) * channels;
231
+        if( channels >= this.channels ) //add channels: copy last channel into new channels
232
+	{
233
+          for( c = 0; c < this.channels; c++ )
234
+            new_data[emptyY + y][destI + c] = (byte)((((int)data[skipY + y][srcI + c] & 0xFF) * maxval + this.maxval / 2) / this.maxval);
235
+	  for( ; c < channels; c++ )
236
+	    new_data[emptyY + y][destI + c] = data[emptyY + y][srcI + this.channels - 1];
237
+	}
238
+	else //remove channels: merge leftover channels with last kept channel
239
+	{
240
+          val = 0;
241
+          for( c = 0; c < channels - 1; c++ )
242
+            new_data[emptyY + y][destI + c] = (byte)((((int)data[skipY + y][srcI + c] & 0xFF) * maxval + this.maxval / 2) / this.maxval);
243
+          for( c = channels - 1; c < this.channels; c++ )
244
+            val += (int)data[skipY + y][srcI + c] & 0xFF;
245
+          div = this.maxval * (this.channels - channels + 1);
246
+          new_data[emptyY + y][destI + channels - 1] = (byte)((val * maxval + div / 2) / div);
247
+	}
248
+      }
249
+    }
250
+
251
+    this.height = height;
252
+    this.width = width;
253
+    this.channels = channels;
254
+    this.maxval = maxval;
255
+    data = new_data;
256
+  }
257
+
258
+  public void scale( int height, int width )
259
+  {
260
+    byte[][] new_data;
261
+    double scaleHor, scaleVer, ox, oy, ox1, oy1, val;
262
+    int c, nx, nx_c, ny, x, x_c, y, oxi, oxi_c, oyi, ox1i, ox1i_c, oy1i;
263
+
264
+    if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin;
265
+    if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax;
266
+    if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin;
267
+    if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax;
268
+
269
+    if( height == this.height &&
270
+        width == this.width )
271
+      return;
272
+
273
+    scaleHor = (double)width / (double)this.width;
274
+    scaleVer = (double)height / (double)this.height;
275
+
276
+    //allocate new data array
277
+    new_data = new byte[height][width * channels];
278
+
279
+    //scale every channel
280
+    for( c = 0; c < channels; c++ )
281
+    {
282
+      for( ny = 0; ny < height; ny++ )
283
+      {
284
+        for( nx = 0, nx_c = c; nx < width; nx++, nx_c += channels )
285
+        {
286
+          oy = (double)ny / scaleVer; //sub-pixel exact range in old picture
287
+          ox = (double)nx / scaleHor;
288
+          oy1 = (double)(ny + 1) / scaleVer - 0.000001;
289
+          ox1 = (double)(nx + 1) / scaleHor - 0.000001;
290
+          if( oy < 0 || ox < 0 || oy1 >= this.height || ox1 >= this.width) //out of old picture
291
+            new_data[ny][nx_c] = 0;
292
+          else
293
+          {
294
+            oyi = (int)oy;
295
+            oxi = (int)ox;
296
+            oxi_c = oxi * channels + c;
297
+            oy1i = (int)oy1;
298
+            ox1i = (int)ox1;
299
+            ox1i_c = ox1i * channels + c;
300
+            if( oyi == oy1i )
301
+            {
302
+              if( oxi == ox1i) //one source pixel
303
+              {
304
+                val = (double)((int)data[oyi][oxi_c] & 0xFF);
305
+              }
306
+              else //one line of source pixels
307
+              {
308
+                val = (double)((int)data[oyi][oxi_c] & 0xFF) * (1 - ox + oxi)
309
+                    + (double)((int)data[oyi][ox1i_c] & 0xFF) * (ox1 - ox1i);
310
+                for( x_c = oxi_c + channels; x_c < ox1i_c; x_c += channels )
311
+                  val += (double)((int)data[oyi][x_c] & 0xFF);
312
+                val /= ox1 - ox;
313
+              }
314
+            }
315
+            else //one column of source pixels
316
+            {
317
+              if( oxi == ox1i )
318
+              {
319
+                val = (double)((int)data[oyi][oxi_c] & 0xFF) * (1 - oy + oyi)
320
+                    + (double)((int)data[oy1i][oxi_c] & 0xFF) * (oy1 - oy1i);
321
+                for( y = oyi + 1; y < oy1i; y++ )
322
+                  val += (double)((int)data[y][oxi_c] & 0xFF);
323
+                val /= oy1 - oy;
324
+              }
325
+              else //rectangle of source pixels
326
+              {
327
+                val = (double)((int)data[oyi][oxi_c] & 0xFF) * (1 - oy + oyi) * (1 - ox + oxi)
328
+                    + (double)((int)data[oyi][ox1i_c] & 0xFF) * (1 - oy + oyi) * (ox1 - ox1i)
329
+                    + (double)((int)data[oy1i][oxi_c] & 0xFF) * (oy1 - oy1i) * (1 - ox + oxi)
330
+                    + (double)((int)data[oy1i][ox1i_c] & 0xFF) * (oy1 - oy1i) * (ox1 - ox1i);
331
+                for( y = oyi + 1; y < oy1i; y++ )
332
+                {
333
+                  val += (double)((int)data[y][oxi_c] & 0xFF) * (1 - ox + oxi)
334
+                       + (double)((int)data[y][ox1i_c] & 0xFF) * (ox1 - ox1i);
335
+                }
336
+                for( x_c = oxi_c + channels; x_c < ox1i_c; x_c += channels )
337
+                {
338
+                  val += (double)((int)data[oyi][x_c] & 0xFF) * (1 - oy + oyi)
339
+                       + (double)((int)data[oy1i][x_c] & 0xFF) * (oy1 - oy1i);
340
+                }
341
+                for( y = oyi + 1; y < oy1i; y++ )
342
+                  for( x_c = oxi_c + channels; x_c < ox1i_c; x_c += channels )
343
+                    val += (double)((int)data[y][x_c] & 0xFF);
344
+                val /= (oy1 - oy) * (ox1 - ox);
345
+              }
346
+            }
347
+            new_data[ny][nx_c] = (byte)(int)(val + 0.5);
348
+          }
349
+        } //for( nx ...
350
+      } //for( ny ...
351
+    } //for( c ...
352
+
353
+    this.height = height;
354
+    this.width = width;
355
+    data = new_data;
356
+  }
357
+
358
+  public String toString( )
359
+  {
360
+    String str = "";
361
+    int h, w, c, i, val;
362
+    for( h = 0; h < height; h++ )
363
+    {
364
+      for( w = 0, i = 0; w < width; w++ )
365
+      {
366
+        val = 0;
367
+        for( val = 0, c = 0; c < channels; c++, i++ )
368
+          val += ((int)data[h][i] & 0xFF);
369
+        val = val * 7 / maxval / channels;
370
+        str = str + " -+*%#&@".substring( val, val + 1); 
371
+      }
372
+      str = str + "\n";
373
+    }
374
+    return str + duration + " ms\n";
375
+  }
376
+
377
+  public byte [] toNetwork( int/*BlinkenProto*/ proto )
378
+  {
379
+    byte [] netData;
380
+
381
+    if( proto == BlinkenConstants.BlinkenProtoNone ) {
382
+      return new byte [0];
383
+    }
384
+
385
+    if( proto == BlinkenConstants.BlinkenProtoBlp ) {
386
+      netData = new byte[12 + height * width]; //allocate buffer for frame
387
+      netData[0] = BlinkenProtoBlpMagic[0]; //magic
388
+      netData[1] = BlinkenProtoBlpMagic[1];
389
+      netData[2] = BlinkenProtoBlpMagic[2];
390
+      netData[3] = BlinkenProtoBlpMagic[3];
391
+      netData[4] = 0; //frameNo
392
+      netData[5] = 0;
393
+      netData[6] = 0;
394
+      netData[7] = 0;
395
+      netData[8] = (byte)(width >> 8 & 0xFF); //width
396
+      netData[9] = (byte)(width & 0xFF);
397
+      netData[10] = (byte)(height >> 8 & 0xFF); //height
398
+      netData[11] = (byte)(height & 0xFF);
399
+      for( int i = 12, y = 0; y < height; y++ )
400
+      {
401
+        for( int x = 0, j = 0; x < width; x++, i++ )
402
+        {
403
+          int val = 0;
404
+          for( int c = 0; c < channels; c++, j++ )
405
+            val += (int)data[y][j] & 0xFF;
406
+          netData[i] = (byte)(val >= channels * maxval / 2 ? 0x01 : 0x00);
407
+        }
408
+      }
409
+      return netData;
410
+    }
411
+
412
+    if( proto == BlinkenConstants.BlinkenProtoEblp ) {
413
+      netData = new byte[12 + height * width]; //allocate buffer for frame
414
+      netData[0] = BlinkenProtoEblpMagic[0]; //magic
415
+      netData[1] = BlinkenProtoEblpMagic[1];
416
+      netData[2] = BlinkenProtoEblpMagic[2];
417
+      netData[3] = BlinkenProtoEblpMagic[3];
418
+      netData[4] = 0; //frameNo
419
+      netData[5] = 0;
420
+      netData[6] = 0;
421
+      netData[7] = 0;
422
+      netData[8] = (byte)(width >> 8 & 0xFF); //width
423
+      netData[9] = (byte)(width & 0xFF);
424
+      netData[10] = (byte)(height >> 8 & 0xFF); //height
425
+      netData[11] = (byte)(height & 0xFF);
426
+      for( int i = 12, y = 0; y < height; y++ )
427
+      {
428
+        for( int x = 0, j = 0; x < width; x++, i++ )
429
+        {
430
+          int val = 0;
431
+          for( int c = 0; c < channels; c++, j++ )
432
+            val += (int)data[y][j] & 0xFF;
433
+          val /= channels;
434
+          netData[i] = (byte)(maxval == 255 ? val
435
+                                            : (val * 255 + maxval / 2) / maxval);
436
+        }
437
+      }
438
+      return netData;
439
+    }
440
+
441
+    if( proto == BlinkenConstants.BlinkenProtoMcuf ) {
442
+      netData = new byte[12 + height * width * channels]; //allocate buffer for frame
443
+      netData[0] = BlinkenProtoMcufMagic[0]; //magic
444
+      netData[1] = BlinkenProtoMcufMagic[1];
445
+      netData[2] = BlinkenProtoMcufMagic[2];
446
+      netData[3] = BlinkenProtoMcufMagic[3];
447
+      netData[4] = (byte)(height >> 8 & 0xFF); //height
448
+      netData[5] = (byte)(height & 0xFF);
449
+      netData[6] = (byte)(width >> 8 & 0xFF); //width
450
+      netData[7] = (byte)(width & 0xFF);
451
+      netData[8] = (byte)(channels >> 8 & 0xFF); //channels
452
+      netData[9] = (byte)(channels & 0xFF);
453
+      netData[10] = (byte)(maxval >> 8 & 0xFF); //maxval
454
+      netData[11] = (byte)(maxval & 0xFF);
455
+      for( int i = 12, y = 0; y < height; y++ )
456
+        for( int x = 0, j = 0; x < width; x++ )
457
+          for( int c = 0; c < channels; c++, i++, j++ )
458
+            netData[i] = data[y][j];
459
+      return netData;
460
+    }
461
+
462
+    return new byte [0];
463
+  }
464
+
465
+  public int/*BlinkenProto*/ fromNetwork( byte [] netData )
466
+  {
467
+    if( netData.length >= 12 && netData[0] == BlinkenProtoBlpMagic[0] &&
468
+                                netData[1] == BlinkenProtoBlpMagic[1] &&
469
+                                netData[2] == BlinkenProtoBlpMagic[2] &&
470
+                                netData[3] == BlinkenProtoBlpMagic[3] )
471
+    {
472
+      int netWidth = ((int)netData[8] & 0xFF) << 8 | ((int)netData[9] & 0xFF); //get header data
473
+      int netHeight = ((int)netData[10] & 0xFF) << 8 | ((int)netData[11] & 0xFF);
474
+      if( netData.length < 12 + netHeight * netWidth ) //check length of data
475
+        return BlinkenConstants.BlinkenProtoNone;
476
+      if( netHeight < BlinkenConstants.BlinkenHeightMin || netHeight > BlinkenConstants.BlinkenHeightMax || //check header data
477
+          netWidth < BlinkenConstants.BlinkenWidthMin || netWidth > BlinkenConstants.BlinkenWidthMax )
478
+        return BlinkenConstants.BlinkenProtoNone;
479
+      height = netHeight; //set frame to header data
480
+      width = netWidth;
481
+      channels = 1;
482
+      maxval = 1;
483
+      duration = BlinkenConstants.BlinkenDurationMin;
484
+      data = new byte[height][width * channels];
485
+      for( int i = 12, y = 0; y < height; y++ ) //put data into frame
486
+        for( int x = 0; x < width; x++, i++ )
487
+          data[y][x] = (byte)(netData[i] != 0 ? 0x01 : 0x00);
488
+      return BlinkenConstants.BlinkenProtoBlp;
489
+    }
490
+
491
+    if( netData.length >= 12 && netData[0] == BlinkenProtoEblpMagic[0] &&
492
+                                netData[1] == BlinkenProtoEblpMagic[1] &&
493
+                                netData[2] == BlinkenProtoEblpMagic[2] &&
494
+                                netData[3] == BlinkenProtoEblpMagic[3] )
495
+    {
496
+      int netWidth = ((int)netData[8] & 0xFF) << 8 | ((int)netData[9] & 0xFF); //get header data
497
+      int netHeight = ((int)netData[10] & 0xFF) << 8 | ((int)netData[11] & 0xFF);
498
+      if( netData.length < 12 + netHeight * netWidth ) //check length of data
499
+        return BlinkenConstants.BlinkenProtoNone;
500
+      if( netHeight < BlinkenConstants.BlinkenHeightMin || netHeight > BlinkenConstants.BlinkenHeightMax || //check header data
501
+          netWidth < BlinkenConstants.BlinkenWidthMin || netWidth > BlinkenConstants.BlinkenWidthMax )
502
+        return BlinkenConstants.BlinkenProtoNone;
503
+      height = netHeight; //set frame to header data
504
+      width = netWidth;
505
+      channels = 1;
506
+      maxval = 255;
507
+      duration = BlinkenConstants.BlinkenDurationMin;
508
+      data = new byte[height][width * channels];
509
+      for( int i = 12, y = 0; y < height; y++ ) //put data into frame
510
+        for( int x = 0; x < width; x++, i++ )
511
+          data[y][x] = netData[i];
512
+      return BlinkenConstants.BlinkenProtoEblp;
513
+    }
514
+
515
+    if( netData.length >= 12 && netData[0] == BlinkenProtoMcufMagic[0] &&
516
+                                netData[1] == BlinkenProtoMcufMagic[1] &&
517
+                                netData[2] == BlinkenProtoMcufMagic[2] &&
518
+                                netData[3] == BlinkenProtoMcufMagic[3] )
519
+    {
520
+      int netHeight = ((int)netData[4] & 0xFF) << 8 | ((int)netData[5] & 0xFF); //get header data
521
+      int netWidth = ((int)netData[6] & 0xFF) << 8 | ((int)netData[7] & 0xFF);
522
+      int netChannels = ((int)netData[8] & 0xFF) << 8 | ((int)netData[9] & 0xFF);
523
+      int netMaxval = ((int)netData[10] & 0xFF) << 8 | ((int)netData[11] & 0xFF);
524
+      if( netData.length < 12 + netHeight * netWidth * netChannels ) //check length of data
525
+        return BlinkenConstants.BlinkenProtoNone;
526
+      if( netHeight < BlinkenConstants.BlinkenHeightMin || netHeight > BlinkenConstants.BlinkenHeightMax || //check header data
527
+          netWidth < BlinkenConstants.BlinkenWidthMin || netWidth > BlinkenConstants.BlinkenWidthMax ||
528
+          netChannels < BlinkenConstants.BlinkenChannelsMin || netChannels > BlinkenConstants.BlinkenChannelsMax ||
529
+          netMaxval < BlinkenConstants.BlinkenMaxvalMin || netMaxval > BlinkenConstants.BlinkenMaxvalMax )
530
+        return BlinkenConstants.BlinkenProtoNone;
531
+      height = netHeight; //set frame to header data
532
+      width = netWidth;
533
+      channels = netChannels;
534
+      maxval = netMaxval;
535
+      duration = BlinkenConstants.BlinkenDurationMin;
536
+      data = new byte[height][width * channels];
537
+      for( int i = 12, y = 0; y < height; y++ ) //put data into frame
538
+        for( int x = 0, j = 0; x < width; x++ )
539
+          for( int c = 0; c < channels; c++, i++, j++ )
540
+            data[y][j] = netData[i];
541
+      return BlinkenConstants.BlinkenProtoMcuf;
542
+    }
543
+
544
+    return BlinkenConstants.BlinkenProtoNone;
545
+  }
546
+
547
+} //public class BlinkenFrame
... ...
@@ -0,0 +1,1247 @@
1
+/* BlinkenLib
2
+ * version 0.1.1 date 2006-10-17
3
+ * Copyright (C) 2004-2006: Stefan Schuermans <1stein@schuermans.info>
4
+ * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
5
+ * a blinkenarea.org project
6
+ */
7
+
8
+package org.blinkenarea.BlinkenLib;
9
+
10
+import java.util.*;
11
+import java.util.regex.*;
12
+import java.io.*;
13
+import org.blinkenarea.BlinkenLib.*;
14
+
15
+public class BlinkenMovie
16
+{
17
+
18
+  private int height;
19
+  private int width;
20
+  private int channels;
21
+  private int maxval;
22
+  private int infoCnt;
23
+  private String[][] infos;
24
+  private int frameCnt;
25
+  private BlinkenFrame[] frames;
26
+
27
+  public BlinkenMovie( int height, int width, int channels, int maxval )
28
+  {
29
+    if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin;
30
+    if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax;
31
+    if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin;
32
+    if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax;
33
+    if( channels < BlinkenConstants.BlinkenChannelsMin ) channels = BlinkenConstants.BlinkenChannelsMin;
34
+    if( channels > BlinkenConstants.BlinkenChannelsMax ) channels = BlinkenConstants.BlinkenChannelsMax;
35
+    if( maxval < BlinkenConstants.BlinkenMaxvalMin ) maxval = BlinkenConstants.BlinkenMaxvalMin;
36
+    if( maxval > BlinkenConstants.BlinkenMaxvalMax ) maxval = BlinkenConstants.BlinkenMaxvalMax;
37
+
38
+    this.height = height;
39
+    this.width = width;
40
+    this.channels = channels;
41
+    this.maxval = maxval;
42
+    infoCnt = 0;
43
+    infos = new String[0][2];
44
+    frameCnt = 0;
45
+    frames = new BlinkenFrame[0];
46
+  }
47
+
48
+  public BlinkenMovie( BlinkenMovie movie )
49
+  {
50
+    int i;
51
+
52
+    height = movie.height;
53
+    width = movie.width;
54
+    channels = movie.channels;
55
+    maxval = movie.maxval;
56
+    infoCnt = 0;
57
+    infos = new String[0][2];
58
+    frameCnt = 0;
59
+    frames = new BlinkenFrame[0];
60
+
61
+    for( i = 0; i < movie.infoCnt; i++ )
62
+      appendInfo( new String( movie.infos[i][0] ), new String( movie.infos[i][1] ) );
63
+
64
+    for( i = 0; i < movie.frameCnt; i++ )
65
+      appendFrame( new BlinkenFrame( movie.frames[i] ) );
66
+  }
67
+
68
+  public int getHeight( )
69
+  {
70
+    return height;
71
+  }
72
+
73
+  public int getWidth( )
74
+  {
75
+    return width;
76
+  }
77
+
78
+  public int getChannels( )
79
+  {
80
+    return channels;
81
+  }
82
+
83
+  public int getMaxval( )
84
+  {
85
+    return maxval;
86
+  }
87
+
88
+  public int getDuration( )
89
+  {
90
+    int duration, i;
91
+    duration = 0;
92
+    for( i = 0; i < frameCnt; i++ )
93
+      duration += frames[i].getDuration( );
94
+    return duration;
95
+  }
96
+
97
+  public int getInfoCnt( )
98
+  {
99
+    return infoCnt;
100
+  }
101
+
102
+  public String getInfoType( int infoNo )
103
+  {
104
+    if( infoCnt < 1 ) return "";
105
+    if( infoNo < 0 ) infoNo = 0;
106
+    if( infoNo >= infoCnt ) infoNo = infoCnt - 1;
107
+    return infos[infoNo][0];
108
+  }
109
+
110
+  public String getInfoData( int infoNo )
111
+  {
112
+    if( infoCnt < 1 ) return "";
113
+    if( infoNo < 0 ) infoNo = 0;
114
+    if( infoNo >= infoCnt ) infoNo = infoCnt - 1;
115
+    return infos[infoNo][1];
116
+  }
117
+
118
+  public void setInfo( int infoNo, String infoType, String infoData )
119
+  {
120
+    if( infoNo < 0 || infoNo >= infoCnt )
121
+      return;
122
+    infos[infoNo][0] = infoType;
123
+    infos[infoNo][1] = infoData;
124
+  }
125
+
126
+  public void insertInfo( int infoNo, String infoType, String infoData )
127
+  {
128
+    String[][] new_infos;
129
+    int i;
130
+
131
+    if( infoNo < 0 || infoNo > infoCnt )
132
+      return;
133
+
134
+    new_infos = new String[infoCnt+1][2];
135
+
136
+    for( i = 0; i < infoNo; i++ )
137
+    {
138
+      new_infos[i][0] = infos[i][0];
139
+      new_infos[i][1] = infos[i][1];
140
+    }
141
+
142
+    new_infos[infoNo][0] = infoType;
143
+    new_infos[infoNo][1] = infoData;
144
+
145
+    for( i = infoNo; i < infoCnt; i++ )
146
+    {
147
+      new_infos[i+1][0] = infos[i][0];
148
+      new_infos[i+1][1] = infos[i][1];
149
+    }
150
+
151
+    infos = new_infos;
152
+    infoCnt++;
153
+  }
154
+  
155
+  public void appendInfo( String infoType, String infoData )
156
+  {
157
+    insertInfo( infoCnt, infoType, infoData );
158
+  }
159
+
160
+  public void deleteInfo( int infoNo )
161
+  {
162
+    String[][] new_infos;
163
+    int i;
164
+
165
+    if( infoNo < 0 || infoNo >= infoCnt )
166
+      return;
167
+
168
+    new_infos = new String[infoCnt-1][2];
169
+
170
+    for( i = 0; i < infoNo; i++ )
171
+    {
172
+      new_infos[i][0] = infos[i][0];
173
+      new_infos[i][1] = infos[i][1];
174
+    }
175
+
176
+    for( i = infoNo; i < infoCnt-1; i++ )
177
+    {
178
+      new_infos[i][0] = infos[i+1][0];
179
+      new_infos[i][1] = infos[i+1][1];
180
+    }
181
+
182
+    infos = new_infos;
183
+    infoCnt--;
184
+  }
185
+  
186
+  public void deleteInfos( )
187
+  {
188
+    infos = new String[0][2];
189
+    infoCnt = 0;
190
+  }
191
+
192
+  public int getFrameCnt( )
193
+  {
194
+    return frameCnt;
195
+  }
196
+
197
+  public BlinkenFrame getFrame( int frameNo )
198
+  {
199
+    BlinkenFrame frame;
200
+    if( frameCnt < 1 )
201
+    {
202
+      frame = new BlinkenFrame( height, width, channels, maxval, 0 );
203
+      frame.clear( );
204
+      return frame;
205
+    }
206
+    if( frameNo < 0 ) frameNo = 0;
207
+    if( frameNo >= frameCnt ) frameNo = frameCnt - 1;
208
+    return frames[frameNo];
209
+  }
210
+
211
+  public void setFrame( int frameNo, BlinkenFrame frame )
212
+  {
213
+    if( frameNo < 0 || frameNo >= frameCnt )
214
+      return;
215
+    frame.resize( height, width, channels, maxval );
216
+    frames[frameNo] = frame;
217
+  }
218
+
219
+  public void insertFrame( int frameNo, BlinkenFrame frame )
220
+  {
221
+    BlinkenFrame[] new_frames;
222
+    int i;
223
+
224
+    if( frameNo < 0 || frameNo > frameCnt )
225
+      return;
226
+
227
+    new_frames = new BlinkenFrame[frameCnt+1];
228
+
229
+    for( i = 0; i < frameNo; i++ )
230
+      new_frames[i] = frames[i];
231
+
232
+    frame.resize( height, width, channels, maxval );
233
+    new_frames[frameNo] = frame;
234
+
235
+    for( i = frameNo; i < frameCnt; i++ )
236
+      new_frames[i+1] = frames[i];
237
+
238
+    frames = new_frames;
239
+    frameCnt++;
240
+  }
241
+  
242
+  public void appendFrame( BlinkenFrame frame )
243
+  {
244
+    insertFrame( frameCnt, frame );
245
+  }
246
+
247
+  public void deleteFrame( int frameNo )
248
+  {
249
+    BlinkenFrame[] new_frames;
250
+    int i;
251
+
252
+    if( frameNo < 0 || frameNo >= frameCnt )
253
+      return;
254
+
255
+    new_frames = new BlinkenFrame[frameCnt-1];
256
+
257
+    for( i = 0; i < frameNo; i++ )
258
+      new_frames[i] = frames[i];
259
+
260
+    for( i = frameNo; i < frameCnt-1; i++ )
261
+      new_frames[i] = frames[i+1];
262
+
263
+    frames = new_frames;
264
+    frameCnt--;
265
+  }
266
+  
267
+  public void deleteFrames( )
268
+  {
269
+    frames = new BlinkenFrame[0];
270
+    frameCnt = 0;
271
+  }
272
+
273
+  public void resize( int height, int width, int channels, int maxval )
274
+  {
275
+    int i;
276
+
277
+    if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin;
278
+    if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax;
279
+    if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin;
280
+    if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax;
281
+    if( channels < BlinkenConstants.BlinkenChannelsMin ) channels = BlinkenConstants.BlinkenChannelsMin;
282
+    if( channels > BlinkenConstants.BlinkenChannelsMax ) channels = BlinkenConstants.BlinkenChannelsMax;
283
+    if( maxval < BlinkenConstants.BlinkenMaxvalMin ) maxval = BlinkenConstants.BlinkenMaxvalMin;
284
+    if( maxval > BlinkenConstants.BlinkenMaxvalMax ) maxval = BlinkenConstants.BlinkenMaxvalMax;
285
+
286
+    this.height = height;
287
+    this.width = width;
288
+    this.channels = channels;
289
+    this.maxval = maxval;
290
+
291
+    for( i = 0; i < frameCnt; i++ )
292
+      frames[i].resize( height, width, channels, maxval );
293
+  }
294
+
295
+  public void scale( int height, int width )
296
+  {
297
+    int i;
298
+
299
+    if( height < BlinkenConstants.BlinkenHeightMin ) height = BlinkenConstants.BlinkenHeightMin;
300
+    if( height > BlinkenConstants.BlinkenHeightMax ) height = BlinkenConstants.BlinkenHeightMax;
301
+    if( width < BlinkenConstants.BlinkenWidthMin ) width = BlinkenConstants.BlinkenWidthMin;
302
+    if( width > BlinkenConstants.BlinkenWidthMax ) width = BlinkenConstants.BlinkenWidthMax;
303
+
304
+    this.height = height;
305
+    this.width = width;
306
+
307
+    for( i = 0; i < frameCnt; i++ )
308
+      frames[i].scale( height, width );
309
+  }
310
+
311
+  public String toString( )
312
+  {
313
+    String str = "BlinkenMovie " + width + "x" + height + "-" + channels + "/" + (maxval + 1) + "\n";
314
+    int i;
315
+    for( i = 0; i < infoCnt; i++ )
316
+      str += infos[i][0] + " = " + infos[i][1] + "\n";
317
+    for( i = 0; i < frameCnt; i++ )
318
+      str += "frame " + i + "\n" + frames[i].toString( );
319
+    return str;
320
+  }
321
+
322
+  public boolean loadBlm( BufferedReader reader )
323
+  {
324
+    Pattern header, infoLine, startOfFrame, dataLine;
325
+    String line, pixel;
326
+    Matcher matcher;
327
+    int new_width, new_height, new_duration, y, x, val;
328
+    BlinkenFrame frame;
329
+
330
+    //initialize needed regexp patterns
331
+    header = Pattern.compile( "^ *# BlinkenLights Movie ([0-9]+)x([0-9]+)" );
332
+    infoLine = Pattern.compile( "^ *# ?([A-Za-z0-9]+)(?: *= *|: *)(.*)" );
333
+    startOfFrame = Pattern.compile( "^ *@([0-9]+)" );
334
+    dataLine = Pattern.compile( " *([01])" );
335
+
336
+    //delete all frames
337
+    deleteInfos( );
338
+    deleteFrames( );
339
+    resize( 0, 0, 0, 0 );
340
+
341
+    //try to read from buffered reader
342
+    try
343
+    {
344
+      //read magic and size
345
+      if( (line = reader.readLine( )) != null )
346
+      {
347
+        if( (matcher = header.matcher( line )).find( ) )
348
+        {
349
+          new_width = Integer.parseInt( matcher.group( 1 ) );
350
+          new_height = Integer.parseInt( matcher.group( 2 ) );
351
+          resize( new_height, new_width, 1, 1 );
352
+
353
+          //create unused dummy frame for beginning
354
+          frame = new BlinkenFrame( height, width, 1, 1, 0 );
355
+          y = 0;
356
+
357
+          //read frames
358
+          while( (line = reader.readLine( )) != null )
359
+          {
360
+
361
+            //info line
362
+            if( (matcher = infoLine.matcher( line )).find( ) )
363
+            {
364
+              appendInfo( matcher.group( 1 ), matcher.group( 2 ) );
365
+            }
366
+
367
+            //start of frame
368
+            else if( (matcher = startOfFrame.matcher( line )).find( ) )
369
+            {
370
+              new_duration = Integer.parseInt( matcher.group( 1 ) );
371
+              //create new frame and append it to movie
372
+              frame = new BlinkenFrame( height, width, 1, 1, new_duration );
373
+              frame.clear( );
374
+              appendFrame( frame );
375
+              y = 0;
376
+            }
377
+
378
+            //data line
379
+            else if( (matcher = dataLine.matcher( line )).find( ) )
380
+            {
381
+              x = 0;
382
+              while( true )
383
+              {
384
+                pixel = matcher.group( 1 );
385
+                val = Integer.parseInt( pixel );
386
+                frame.setPixel( y, x, 0, (byte)val ); //set pixel
387
+                if( ! matcher.find( ) ) //get next pixel
388
+                  break;
389
+                x++; //next pixel
390
+              }
391
+              y++; //next row
392
+            }
393
+
394
+          } //while( (line = ...
395
+        } //if( matcher = header..matcher( ...
396
+      } //if( (line = ...
397
+
398
+      //success
399
+      return true;
400
+    }
401
+    catch( IOException e ) { }
402
+
403
+    //some error
404
+    return false;
405
+  }
406
+
407
+  public boolean loadBlm( String filename )
408
+  {
409
+    try
410
+    {
411
+      BufferedReader file = new BufferedReader( new FileReader( filename ) );
412
+      boolean ret = loadBlm( file );
413
+      file.close( );
414
+      return ret;
415
+    }
416
+    catch( IOException e )
417
+    {
418
+      return false;
419
+    }
420
+  }
421
+
422
+  public boolean loadBmm( BufferedReader reader )
423
+  {
424
+    Pattern header, infoLine, startOfFrame, dataLine;
425
+    String line, pixel;
426
+    Matcher matcher;
427
+    int new_width, new_height, new_duration, y, x, val;
428
+    BlinkenFrame frame;
429
+
430
+    //initialize needed regexp patterns
431
+    header = Pattern.compile( "^ *# BlinkenMini Movie ([0-9]+)x([0-9]+)" );
432
+    infoLine = Pattern.compile( "^ *# ?([A-Za-z0-9]+)(?: *= *|: *)(.*)" );
433
+    startOfFrame = Pattern.compile( "^ *@([0-9]+)" );
434
+    dataLine = Pattern.compile( " *(0x[0-9A-Fa-f]+|[0-9]+)" );
435
+
436
+    //delete all frames
437
+    deleteInfos( );
438
+    deleteFrames( );
439
+    resize( 0, 0, 0, 0 );
440
+
441
+    //try to read from buffered reader
442
+    try
443
+    {
444
+      //read magic and size
445
+      if( (line = reader.readLine( )) != null )
446
+      {
447
+        if( (matcher = header.matcher( line )).find( ) )
448
+        {
449
+          new_width = Integer.parseInt( matcher.group( 1 ) );
450
+          new_height = Integer.parseInt( matcher.group( 2 ) );
451
+          resize( new_height, new_width, 1, 255 );
452
+
453
+          //create unused dummy frame for beginning
454
+          frame = new BlinkenFrame( height, width, 1, 255, 0 );
455
+          y = 0;
456
+
457
+          //read frames
458
+          while( (line = reader.readLine( )) != null )
459
+          {
460
+
461
+            //info line
462
+            if( (matcher = infoLine.matcher( line )).find( ) )
463
+            {
464
+              appendInfo( matcher.group( 1 ), matcher.group( 2 ) );
465
+            }
466
+
467
+            //start of frame
468
+            else if( (matcher = startOfFrame.matcher( line )).find( ) )
469
+            {
470
+              new_duration = Integer.parseInt( matcher.group( 1 ) );
471
+              //create new frame and append it to movie
472
+              frame = new BlinkenFrame( height, width, 1, 255, new_duration );
473
+              frame.clear( );
474
+              appendFrame( frame );
475
+              y = 0;
476
+            }
477
+
478
+            //data line
479
+            else if( (matcher = dataLine.matcher( line )).find( ) )
480
+            {
481
+              x = 0;
482
+              while( true )
483
+              {
484
+                pixel = matcher.group( 1 );
485
+                if( pixel.length( ) >= 2 && pixel.substring( 0, 2 ).equals( "0x" ) )
486
+                  val = Integer.parseInt( pixel.substring( 2 ), 0x10 );
487
+                else
488
+                  val = Integer.parseInt( pixel );
489
+                frame.setPixel( y, x, 0, (byte)val ); //set pixel
490
+                if( ! matcher.find( ) ) //get next pixel
491
+                  break;
492
+                x++; //next pixel
493
+              }
494
+              y++; //next row
495
+            }
496
+
497
+          } //while( (line = ...
498
+        } //if( matcher = header..matcher( ...
499
+      } //if( (line = ...
500
+
501
+      //success
502
+      return true;
503
+    }
504
+    catch( IOException e ) { }
505
+
506
+    //some error
507
+    return false;
508
+  }
509
+
510
+  public boolean loadBmm( String filename )
511
+  {
512
+    try
513
+    {
514
+      BufferedReader file = new BufferedReader( new FileReader( filename ) );
515
+      boolean ret = loadBmm( file );
516
+      file.close( );
517
+      return ret;
518
+    }
519
+    catch( IOException e )
520
+    {
521
+      return false;
522
+    }
523
+  }
524
+
525
+  public boolean loadBml( BufferedReader reader )
526
+  {
527
+    Pattern blmTag, blmHeight, blmWidth, blmChannels, blmBits;
528
+    Pattern infoTitle, infoDescription, infoGeneric, infoCreator, infoAuthor, infoEmail, infoUrl;
529
+    Pattern frameTag, frameDuration, rowTag, tag;
530
+    String line, data, row;
531
+    boolean blmTagFound;
532
+    Matcher matcher, submatcher;
533
+    int new_height, new_width, new_channels, bits, new_maxval, chrs, duration, y, x, c, len, i, val;
534
+    BlinkenFrame frame;
535
+
536
+    //initialize needed regexp patterns
537
+    blmTag = Pattern.compile( "^[^<]*<blm([^>]*)>" );
538
+    blmHeight = Pattern.compile( "height=\"?([0-9]*)\"?" );
539
+    blmWidth = Pattern.compile( "width=\"?([0-9]*)\"?" );
540
+    blmChannels = Pattern.compile( "channels=\"?([0-9]*)\"?" );
541
+    blmBits = Pattern.compile( "bits=\"?([0-9]*)\"?" );
542
+    infoTitle = Pattern.compile( "^[^<]*<title>([^<]*)</title>" );
543
+    infoDescription = Pattern.compile( "[^<]*<description>([^<]*)</description>" );
544
+    infoGeneric = Pattern.compile( "^([A-Za-z0-9]+)(?: *= *|: *)(.*)" );
545
+    infoCreator = Pattern.compile( "^[^<]*<creator>([^<]*)</creator>" );
546
+    infoAuthor = Pattern.compile( "^[^<]*<author>([^<]*)</author>" );
547
+    infoEmail = Pattern.compile( "^[^<]*<email>([^<]*)</email>" );
548
+    infoUrl = Pattern.compile( "^[^<]*<url>([^<]*)</url>" );
549
+    frameTag = Pattern.compile( "^[^<]*<frame([^>]*)>" );
550
+    frameDuration = Pattern.compile( "duration=\"?([0-9]*)\"?" );
551
+    rowTag = Pattern.compile( "^[^<]*<row>([0-9A-Fa-f]*)</row>" );
552
+    tag = Pattern.compile( "^[^<]*<[^>]*>" );
553
+
554
+    //delete all frames
555
+    deleteInfos( );
556
+    deleteFrames( );
557
+    resize( 0, 0, 0, 0 );
558
+
559
+    //try to read from buffered reader
560
+    try
561
+    {
562
+      //create unused dummy frame for beginning
563
+      frame = new BlinkenFrame( 0, 0, 0, 0, 0 );
564
+      y = 0;
565
+      chrs = 1;
566
+
567
+      //read
568
+      data = "";
569
+      blmTagFound = false;
570
+      while( (line = reader.readLine( )) != null )
571
+      {
572
+        data += " " + line; //add new line to data
573
+
574
+        //match tags
575
+        while( true )
576
+        {
577
+
578
+          //no blm tag yet
579
+          if( ! blmTagFound )
580
+          {
581
+
582
+            //blm tag
583
+            if( (matcher = blmTag.matcher( data )).find( ) )
584
+            {
585
+              //remove matched part
586
+              data = data.substring( matcher.end( ) );
587
+              //get attributes
588
+              new_width = 0;
589
+              new_height = 0;
590
+              new_channels = 1;
591
+              bits = 4;
592
+              new_maxval = 15;
593
+              if( (submatcher = blmHeight.matcher( matcher.group( 1 ) )).find( ) ) //height
594
+                new_height = Integer.parseInt( submatcher.group( 1 ) );
595
+              if( (submatcher = blmWidth.matcher( matcher.group( 1 ) )).find( ) ) //width
596
+                new_width = Integer.parseInt( submatcher.group( 1 ) );
597
+              if( (submatcher = blmChannels.matcher( matcher.group( 1 ) )).find( ) ) //channels
598
+                new_channels = Integer.parseInt( submatcher.group( 1 ) );
599
+              if( (submatcher = blmBits.matcher( matcher.group( 1 ) )).find( ) ) //bits
600
+                new_maxval = (1 << (bits = Integer.parseInt( submatcher.group( 1 ) ))) - 1;
601
+              //remember that blm tag was found
602
+              blmTagFound = true;
603
+              //set movie size
604
+              resize( new_height, new_width, new_channels, new_maxval );
605
+              //get number of characters per channel
606
+              chrs = (bits + 3) >> 2;
607
+            }
608
+
609
+            //unknown tag
610
+            else if( (matcher = tag.matcher( data )).find( ) )
611
+              //remove matched part
612
+              data = data.substring( matcher.end( ) );
613
+
614
+            //nothing matches
615
+            else
616
+              //end loop
617
+              break;
618
+
619
+          } //if( ! blmTagFound )
620
+
621
+          //blm tag was already found
622
+          else
623
+          {
624
+
625
+            //title tag
626
+            if( (matcher = infoTitle.matcher( data )).find( ) )
627
+            {
628
+              //remove matched part
629
+              data = data.substring( matcher.end( ) );
630
+              //add info to movie
631
+              appendInfo( "title", matcher.group( 1 ) );
632
+            }
633
+
634
+            //description tag
635
+            else if( (matcher = infoDescription.matcher( data )).find( ) )
636
+            {
637
+              //remove matched part
638
+              data = data.substring( matcher.end( ) );
639
+              //check if generic info
640
+              if( (submatcher = infoGeneric.matcher( matcher.group( 1 ) )).find( ) )
641
+                //add info to movie
642
+                appendInfo( submatcher.group( 1 ), submatcher.group( 2 ) );
643
+              else
644
+                //add info to movie
645
+                appendInfo( "description", matcher.group( 1 ) );
646
+            }
647
+
648
+            //creator tag
649
+            else if( (matcher = infoCreator.matcher( data )).find( ) )
650
+            {
651
+              //remove matched part
652
+              data = data.substring( matcher.end( ) );
653
+              //add info to movie
654
+              appendInfo( "creator", matcher.group( 1 ) );
655
+            }
656
+
657
+            //author tag
658
+            else if( (matcher = infoAuthor.matcher( data )).find( ) )
659
+            {
660
+              //remove matched part
661
+              data = data.substring( matcher.end( ) );
662
+              //add info to movie
663
+              appendInfo( "author", matcher.group( 1 ) );
664
+            }
665
+
666
+            //email tag
667
+            else if( (matcher = infoEmail.matcher( data )).find( ) )
668
+            {
669
+              //remove matched part
670
+              data = data.substring( matcher.end( ) );
671
+              //add info to movie
672
+              appendInfo( "email", matcher.group( 1 ) );
673
+            }
674
+
675
+            //url tag
676
+            else if( (matcher = infoUrl.matcher( data )).find( ) )
677
+            {
678
+              //remove matched part
679
+              data = data.substring( matcher.end( ) );
680
+              //add info to movie
681
+              appendInfo( "url", matcher.group( 1 ) );
682
+            }
683
+
684
+            //frame tag
685
+            else if( (matcher = frameTag.matcher( data )).find( ) )
686
+            {
687
+              //remove matched part
688
+              data = data.substring( matcher.end( ) );
689
+              //get attributes
690
+              duration = 0;
691
+              if( (submatcher = frameDuration.matcher( matcher.group( 1 ) )).find( ) ) //duration
692
+                duration = Integer.parseInt( submatcher.group( 1 ) );
693
+              //create new frame and append it to movie
694
+              frame = new BlinkenFrame( height, width, channels, maxval, duration );
695
+              frame.clear( );
696
+              appendFrame( frame );
697
+              y = 0;
698
+            }
699
+
700
+            //row tag
701
+            else if( (matcher = rowTag.matcher( data )).find( ) )
702
+            {
703
+              //remove matched part
704
+              data = data.substring( matcher.end( ) );
705
+              //parse row
706
+              row = matcher.group( 1 );
707
+              len = row.length( );
708
+              i = 0;
709
+              for( x = 0; x < this.width && i + chrs <= len; x++ )
710
+              {
711
+                for( c = 0; c < this.channels && i + chrs <= len; c++, i += chrs )
712
+                {
713
+                  val = Integer.parseInt( row.substring( i, i + chrs ), 0x10 );
714
+                  frame.setPixel( y, x, c, (byte)val ); //set pixel
715
+                }
716
+              }
717
+              y++; //next row
718
+            }
719
+
720
+            //unknown tag
721
+            else if( (matcher = tag.matcher( data )).find( ) )
722
+              //remove matched part
723
+              data = data.substring( matcher.end( ) );
724
+
725
+            //nothing matches
726
+            else
727
+              //end loop
728
+              break;
729
+
730
+          } //if( ! blmTagFound ) ... else
731
+
732
+        } //while( true )
733
+
734
+      } //while( (line = ...
735
+
736
+      //success
737
+      return true;
738
+    }
739
+    catch( IOException e ) { }
740
+
741
+    //some error
742
+    return false;
743
+  }
744
+
745
+  public boolean loadBml( String filename )
746
+  {
747
+    try
748
+    {
749
+      BufferedReader file = new BufferedReader( new FileReader( filename ) );
750
+      boolean ret = loadBml( file );
751
+      file.close( );
752
+      return ret;
753
+    }
754
+    catch( IOException e )
755
+    {
756
+      return false;
757
+    }
758
+  }
759
+
760
+  public boolean loadBbm( String filename )
761
+  {
762
+    RandomAccessFile file;
763
+    byte[] header, subHeader, infoHeader, frameStartMarker, frameData;
764
+    int headerMagic, headerHeight, headerWidth, headerChannels, headerMaxval;
765
+    int headerFrameCnt, headerDuration, headerFramePtr;
766
+    int subHeaderMagic, subHeaderSize;
767
+    int frameStartMarkerMagic;
768
+    StringTokenizer tokenizer;
769
+    String infoType, infoData;
770
+    int fileLength, frameLength;
771
+    int duration, i, y, x, c;
772
+    BlinkenFrame frame;
773
+
774
+    //delete all frames
775
+    deleteInfos( );
776
+    deleteFrames( );
777
+    resize( 0, 0, 0, 0 );
778
+
779
+    //try to read file
780
+    try
781
+    {
782
+      //open file
783
+      file = new RandomAccessFile( filename, "r" );
784
+
785
+      //read header
786
+      header = new byte[24];
787
+      file.readFully( header );
788
+      headerMagic = ((int)header[0] & 0xFF) << 24 | ((int)header[1] & 0xFF) << 16 | ((int)header[2] & 0xFF) << 8 | ((int)header[3] & 0xFF);
789
+      headerHeight = ((int)header[4] & 0xFF) << 8 | ((int)header[5] & 0xFF);
790
+      headerWidth = ((int)header[6] & 0xFF) << 8 | ((int)header[7] & 0xFF);
791
+      headerChannels = ((int)header[8] & 0xFF) << 8 | ((int)header[9] & 0xFF);
792
+      headerMaxval = ((int)header[10] & 0xFF) << 8 | ((int)header[11] & 0xFF);
793
+      headerFrameCnt = ((int)header[12] & 0xFF) << 24 | ((int)header[13] & 0xFF) << 16 | ((int)header[14] & 0xFF) << 8 | ((int)header[15] & 0xFF);
794
+      headerDuration = ((int)header[16] & 0xFF) << 24 | ((int)header[17] & 0xFF) << 16 | ((int)header[18] & 0xFF) << 8 | ((int)header[19] & 0xFF);
795
+      headerFramePtr = ((int)header[20] & 0xFF) << 24 | ((int)header[21] & 0xFF) << 16 | ((int)header[22] & 0xFF) << 8 | ((int)header[23] & 0xFF);
796
+      //check magic
797
+      if( headerMagic == 0x23542666 )
798
+      {
799
+
800
+        //adapt movie format
801
+        resize( headerHeight, headerWidth, headerChannels, headerMaxval );
802
+
803
+        //read subheaders
804
+        subHeader = new byte[6];
805
+        while( file.getFilePointer( ) + 6 <= headerFramePtr )
806
+        {
807
+          file.readFully( subHeader );
808
+          subHeaderMagic = ((int)subHeader[0] & 0xFF) << 24 | ((int)subHeader[1] & 0xFF) << 16 | ((int)subHeader[2] & 0xFF) << 8 | ((int)subHeader[3] & 0xFF);
809
+          subHeaderSize = ((int)subHeader[4] & 0xFF) << 8 | ((int)subHeader[5] & 0xFF);
810
+
811
+          //header fits into gap to frame start
812
+          if( subHeaderSize >= 6 && file.getFilePointer( ) + subHeaderSize - 6 <= headerFramePtr )
813
+          {
814
+            //info header
815
+            if( subHeaderMagic == 0x696E666F ) //'i' 'n' 'f' 'o'
816
+            {
817
+              //read rest of info header
818
+              infoHeader = new byte[subHeaderSize - 6];
819
+              file.readFully( infoHeader );
820
+              //parse information
821
+              tokenizer = new StringTokenizer( new String( infoHeader ), "\000" );
822
+              if( tokenizer.countTokens( ) >= 2 )
823
+              {
824
+                infoType = tokenizer.nextToken();
825
+                infoData = tokenizer.nextToken();
826
+                appendInfo( infoType, infoData );
827
+              }
828
+            }
829
+
830
+            //unknown subHeader
831
+            else
832
+              //skip
833
+              file.skipBytes( subHeaderSize - 6 );
834
+
835
+          } //if( file.getFilePointer( ) ...
836
+        } //while( file.getFilePointer( ) ...
837
+
838
+        //seek to start of frames
839
+        file.seek( headerFramePtr );
840
+
841
+        //read frame start marker
842
+        frameStartMarker = new byte[4];
843
+        file.readFully( frameStartMarker );
844
+        frameStartMarkerMagic = ((int)frameStartMarker[0] & 0xFF) << 24 | ((int)frameStartMarker[1] & 0xFF) << 16 | ((int)frameStartMarker[2] & 0xFF) << 8 | ((int)frameStartMarker[3] & 0xFF);
845
+        if( frameStartMarkerMagic == 0x66726D73 ) //'f' 'r' 'm' 's'
846
+        {
847
+
848
+          //read frames
849
+          fileLength = (int)file.length( );
850
+          frameLength = 2 + headerHeight * headerWidth * headerChannels;
851
+          frameData = new byte[frameLength];
852
+          while( file.getFilePointer( ) + frameLength <= fileLength )
853
+          {
854
+            //read frame
855
+            file.readFully( frameData );
856
+            duration = ((int)frameData[0] & 0xFF) << 8 | ((int)frameData[1] & 0xFF);
857
+            //build frame and append it to movie
858
+            frame = new BlinkenFrame( height, width, channels, maxval, duration );
859
+            i = 2;
860
+            for( y = 0; y < headerHeight; y++ )
861
+              for( x = 0; x < headerWidth; x++ )
862
+                for( c = 0; c < headerChannels; c++, i++ )
863
+                  frame.setPixel( y, x, c, frameData[i] );
864
+            appendFrame( frame );
865
+
866
+          } //while( file.getFilePointer ...
867
+
868
+        } //if( frameStartMarkerMagic ...
869
+
870
+      } //if( headerMagic ...
871
+
872
+      //close file
873
+      file.close( );
874
+
875
+      //success
876
+      return true;
877
+    }
878
+    catch( IOException e ) { }
879
+
880
+    //some error
881
+    return false;
882
+  }
883
+
884
+  public boolean load( String filename )
885
+  {
886
+    if( filename.endsWith( ".blm" ) )
887
+      return loadBlm( filename );
888
+    if( filename.endsWith( ".bmm" ) )
889
+      return loadBmm( filename );
890
+    if( filename.endsWith( ".bml" ) )
891
+      return loadBml( filename );
892
+    if( filename.endsWith( ".bbm" ) )
893
+      return loadBbm( filename );
894
+    deleteFrames( );
895
+    return false;
896
+  }
897
+
898
+  public boolean saveBlm( BufferedWriter writer )
899
+  {
900
+    BlinkenMovie movie;
901
+    int cnt, i, y, x;
902
+    String line;
903
+
904
+    //convert movie to suitable format
905
+    movie = new BlinkenMovie( this );
906
+    movie.resize( movie.height, movie.width, 1, 1 );
907
+
908
+    try
909
+    {
910
+      //write header line
911
+      writer.write( "# BlinkenLights Movie " + movie.width + "x" + movie.height + "\n" );
912
+
913
+      //write information lines
914
+      cnt = movie.getInfoCnt( );
915
+      for( i = 0; i < cnt; i++ )
916
+        writer.write( "# " + movie.getInfoType( i ) + " = " + movie.getInfoData( i ) + "\n" );
917
+
918
+      //write frames
919
+      cnt = movie.getFrameCnt( );
920
+      for( i = 0; i < cnt; i++ )
921
+      {
922
+        writer.write( "\n@" + movie.frames[i].getDuration( ) + "\n" );
923
+        for( y = 0; y < movie.height; y++ )
924
+        {
925
+          line = "";
926
+          for( x = 0; x < movie.width; x++ )
927
+          {
928
+            if( movie.frames[i].getPixel( y, x, 0 ) != 0 )
929
+              line = line + "1";
930
+            else
931
+              line = line + "0";
932
+          }
933
+          writer.write( line + "\n" );
934
+        }
935
+      }
936
+
937
+      //success
938
+      return true;
939
+    }
940
+    catch( IOException e ) { }
941
+
942
+    //some error
943
+    return false;
944
+  }
945
+
946
+  public boolean saveBlm( String filename )
947
+  {
948
+    try
949
+    {
950
+      BufferedWriter file = new BufferedWriter( new FileWriter( filename ) );
951
+      boolean ret = saveBlm( file );
952
+      file.close( );
953
+      return ret;
954
+    }
955
+    catch( IOException e )
956
+    {
957
+      return false;
958
+    }
959
+  }
960
+
961
+  public boolean saveBmm( BufferedWriter writer )
962
+  {
963
+    BlinkenMovie movie;
964
+    int cnt, i, y, x, val;
965
+    String line;
966
+
967
+    //convert movie to suitable format
968
+    movie = new BlinkenMovie( this );
969
+    movie.resize( movie.height, movie.width, 1, 255 );
970
+
971
+    try
972
+    {
973
+      //write header line
974
+      writer.write( "# BlinkenMini Movie " + movie.width + "x" + movie.height + "\n" );
975
+
976
+      //write information lines
977
+      cnt = movie.getInfoCnt( );
978
+      for( i = 0; i < cnt; i++ )
979
+        writer.write( "# " + movie.getInfoType( i ) + " = " + movie.getInfoData( i ) + "\n" );
980
+
981
+      //write frames
982
+      cnt = movie.getFrameCnt( );
983
+      for( i = 0; i < cnt; i++ )
984
+      {
985
+        writer.write( "\n@" + movie.frames[i].getDuration( ) + "\n" );
986
+        for( y = 0; y < movie.height; y++ )
987
+        {
988
+          line = "";
989
+          for( x = 0; x < movie.width; x++ )
990
+          {
991
+            val = (int)movie.frames[i].getPixel( y, x, 0 ) & 0xFF;
992
+            if( val < 0x10 )
993
+              line = line + " 0x0" + Integer.toHexString( val ).toUpperCase( );
994
+            else
995
+              line = line + " 0x" + Integer.toHexString( val ).toUpperCase( );
996
+          }
997
+          writer.write( line.substring( 1 ) + "\n" );
998
+        }
999
+      }
1000
+
1001
+      //success
1002
+      return true;
1003
+    }
1004
+    catch( IOException e ) { }
1005
+
1006
+    //some error
1007
+    return false;
1008
+  }
1009
+
1010
+  public boolean saveBmm( String filename )
1011
+  {
1012
+    try
1013
+    {
1014
+      BufferedWriter file = new BufferedWriter( new FileWriter( filename ) );
1015
+      boolean ret = saveBmm( file );
1016
+      file.close( );
1017
+      return ret;
1018
+    }
1019
+    catch( IOException e )
1020
+    {
1021
+      return false;
1022
+    }
1023
+  }
1024
+
1025
+  public boolean saveBml( BufferedWriter writer )
1026
+  {
1027
+    BlinkenMovie movie;
1028
+    int bits, cnt, i, y, x, c, val;
1029
+    String infoType, infoData, line;
1030
+
1031
+    //convert movie to suitable format
1032
+    movie = new BlinkenMovie( this );
1033
+    val = movie.maxval; //get number of bits
1034
+    for( bits = 0; val != 0; val >>= 1, bits++ );
1035
+    movie.resize( movie.height, movie.width, movie.channels, (1 << bits) - 1 );
1036
+
1037
+    try
1038
+    {
1039
+      //write header line
1040
+      writer.write( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
1041
+
1042
+      //write blm start tag
1043
+      writer.write( "<blm width=\"" + movie.width + "\" height=\"" + movie.height
1044
+                  + "\" bits=\"" + bits + "\" channels=\"" + movie.channels + "\">\n" );
1045
+
1046
+      //write information lines
1047
+      writer.write( "\t<header>\n" );
1048
+      cnt = movie.getInfoCnt( );
1049
+      for( i = 0; i < cnt; i++ )
1050
+      {
1051
+        infoType = movie.getInfoType( i );
1052
+        infoData = movie.getInfoData( i );
1053
+        if( infoType.equals( "title" ) )
1054
+          writer.write( "\t\t<title>" + infoData + "</title>\n" );
1055
+        else if( infoType.equals( "description" ) )
1056
+          writer.write( "\t\t<description>" + infoData + "</description>\n" );
1057
+        else if( infoType.equals( "creator" ) )
1058
+          writer.write( "\t\t<creator>" + infoData + "</creator>\n" );
1059
+        else if( infoType.equals( "author" ) )
1060
+          writer.write( "\t\t<author>" + infoData + "</author>\n" );
1061
+        else if( infoType.equals( "email" ) )
1062
+          writer.write( "\t\t<email>" + infoData + "</email>\n" );
1063
+        else if( infoType.equals( "url" ) )
1064
+          writer.write( "\t\t<url>" + infoData + "</url>\n" );
1065
+        else
1066
+          writer.write( "\t\t<description>" + infoType + ": " + infoData + "</description>\n" );
1067
+      }
1068
+      writer.write( "\t</header>\n" );
1069
+
1070
+      //write frames
1071
+      cnt = movie.getFrameCnt( );
1072
+      for( i = 0; i < cnt; i++ )
1073
+      {
1074
+        writer.write( "\n\t<frame duration=\"" + movie.frames[i].getDuration( ) + "\">\n" );
1075
+        for( y = 0; y < movie.height; y++ )
1076
+        {
1077
+          line = "";
1078
+          for( x = 0; x < movie.width; x++ )
1079
+          {
1080
+            for( c = 0; c < movie.channels; c++ )
1081
+            {
1082
+              val = (int)movie.frames[i].getPixel( y, x, c ) & 0xFF;
1083
+              if( bits > 4 && val < 0x10 )
1084
+                line = line + "0" + Integer.toHexString( val ).toUpperCase( );
1085
+              else
1086
+                line = line + Integer.toHexString( val ).toUpperCase( );
1087
+            }
1088
+          }
1089
+          writer.write( "\t\t<row>" + line + "</row>\n" );
1090
+        }
1091
+        writer.write( "\t</frame>\n" );
1092
+      }
1093
+
1094
+      //write blm end tag
1095
+      writer.write( "</blm>\n" );
1096
+
1097
+      //success
1098
+      return true;
1099
+    }
1100
+    catch( IOException e ) { }
1101
+
1102
+    //some error
1103
+    return false;
1104
+  }
1105
+
1106
+  public boolean saveBml( String filename )
1107
+  {
1108
+    try
1109
+    {
1110
+      BufferedWriter file = new BufferedWriter( new FileWriter( filename ) );
1111
+      boolean ret = saveBml( file );
1112
+      file.close( );
1113
+      return ret;
1114
+    }
1115
+    catch( IOException e )
1116
+    {
1117
+      return false;
1118
+    }
1119
+  }
1120
+
1121
+  public boolean saveBbm( String filename )
1122
+  {
1123
+    RandomAccessFile file;
1124
+    byte[] header, infoHeader, framePointer, frameStartMarker, frameData;
1125
+    int cnt, duration, i, j, len, y, x, c, val;
1126
+    String infoType, infoData, line;
1127
+
1128
+    try
1129
+    {
1130
+      //open file
1131
+      file = new RandomAccessFile( filename, "rw" );
1132
+
1133
+      //write header
1134
+      header = new byte[24];
1135
+      header[0] = 0x23; //magic
1136
+      header[1] = 0x54;
1137
+      header[2] = 0x26;
1138
+      header[3] = 0x66;
1139
+      header[4] = (byte)(this.height >> 8);
1140
+      header[5] = (byte)this.height;
1141
+      header[6] = (byte)(this.width >> 8);
1142
+      header[7] = (byte)this.width;
1143
+      header[8] = (byte)(this.channels >> 8);
1144
+      header[9] = (byte)this.channels;
1145
+      header[10] = (byte)(this.maxval >> 8);
1146
+      header[11] = (byte)this.maxval;
1147
+      cnt = this.getFrameCnt( );
1148
+      header[12] = (byte)(cnt >> 24);
1149
+      header[13] = (byte)(cnt >> 16);
1150
+      header[14] = (byte)(cnt >> 8);
1151
+      header[15] = (byte)cnt;
1152
+      duration = 0;
1153
+      for( i = 0; i < cnt; i++ )
1154
+        duration += this.frames[i].getDuration( ); 
1155
+      header[16] = (byte)(duration >> 24);
1156
+      header[17] = (byte)(duration >> 16);
1157
+      header[18] = (byte)(duration >> 8);
1158
+      header[19] = (byte)duration;
1159
+      header[20] = 0; //frame pointer is written later
1160
+      header[21] = 0;
1161
+      header[22] = 0;
1162
+      header[23] = 0;
1163
+      file.write( header );
1164
+
1165
+      //write information
1166
+      cnt = this.getInfoCnt( );
1167
+      for( i = 0; i < cnt; i++ )
1168
+      {
1169
+        infoType = this.getInfoType( i );
1170
+        if( infoType.length( ) > 32760 )
1171
+          infoType = infoType.substring( 0, 32760 );
1172
+        infoData = this.getInfoData( i );
1173
+        if( infoData.length( ) > 32760 )
1174
+          infoData = infoData.substring( 0, 32760 );
1175
+        len = 8 + infoType.length( ) + infoData.length( );
1176
+        infoHeader = new byte[6];
1177
+        infoHeader[0] = 0x69; //'i'
1178
+        infoHeader[1] = 0x6E; //'n'
1179
+        infoHeader[2] = 0x66; //'f'
1180
+        infoHeader[3] = 0x6F; //'o'
1181
+        infoHeader[4] = (byte)(len >> 8);
1182
+        infoHeader[5] = (byte)len;
1183
+        file.write( infoHeader );
1184
+        file.write( (infoType + "\000" + infoData + "\000").getBytes( ) );
1185
+      }
1186
+
1187
+      //write frame pointer
1188
+      framePointer = new byte[4];
1189
+      val = (int)file.getFilePointer( );
1190
+      framePointer[0] = (byte)(val >> 24);
1191
+      framePointer[1] = (byte)(val >> 16);
1192
+      framePointer[2] = (byte)(val >> 8);
1193
+      framePointer[3] = (byte)val;
1194
+      file.seek( 20 );
1195
+      file.write( framePointer );
1196
+      file.seek( val );
1197
+
1198
+      //write frame start marker
1199
+      frameStartMarker = new byte[4];
1200
+      frameStartMarker[0] = 0x66; //'f'
1201
+      frameStartMarker[1] = 0x72; //'r'
1202
+      frameStartMarker[2] = 0x6D; //'m'
1203
+      frameStartMarker[3] = 0x73; //'s'
1204
+      file.write( frameStartMarker );
1205
+
1206
+      //write frames
1207
+      len = 2 + this.height * this.width * this.channels;
1208
+      frameData = new byte[len];
1209
+      cnt = this.getFrameCnt( );
1210
+      for( i = 0; i < cnt; i++ )
1211
+      {
1212
+        val = this.frames[i].getDuration( );
1213
+        frameData[0] = (byte)(val >> 8);
1214
+        frameData[1] = (byte)val;
1215
+        for( j = 2, y = 0; y < this.height; y++ )
1216
+          for( x = 0; x < this.width; x++ )
1217
+            for( c = 0; c < this.channels; c++, j++ )
1218
+              frameData[j] = this.frames[i].getPixel( y, x, c );
1219
+        file.write( frameData );
1220
+      }
1221
+
1222
+      //close file
1223
+      file.close( );
1224
+
1225
+      //success
1226
+      return true;
1227
+    }
1228
+    catch( IOException e ) { }
1229
+
1230
+    //some error
1231
+    return false;
1232
+  }
1233
+
1234
+  public boolean save( String filename )
1235
+  {
1236
+    if( filename.endsWith( ".blm" ) )
1237
+      return saveBlm( filename );
1238
+    if( filename.endsWith( ".bmm" ) )
1239
+      return saveBmm( filename );
1240
+    if( filename.endsWith( ".bml" ) )
1241
+      return saveBml( filename );
1242
+    if( filename.endsWith( ".bbm" ) )
1243
+      return saveBbm( filename );
1244
+    return false;
1245
+  }
1246
+
1247
+} //public class BlinkenMovie
0 1248