Mercurial > jmeCapture
comparison src/com/aurellem/capture/MicrosoftRLEEncoder.java @ 3:a92de00f0414
migrating files
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 11:55:55 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:59509c585530 | 3:a92de00f0414 |
---|---|
1 /* | |
2 * @(#)AppleRLEEncoder.java 1.1.1 2011-01-17 | |
3 * | |
4 * Copyright © 2011 Werner Randelshofer, Immensee, Switzerland. | |
5 * All rights reserved. | |
6 * | |
7 * You may not use, copy or modify this file, except in compliance with the | |
8 * license agreement you entered into with Werner Randelshofer. | |
9 * For details see accompanying license terms. | |
10 */ | |
11 package com.aurellem.capture; | |
12 | |
13 import java.io.ByteArrayOutputStream; | |
14 import java.io.IOException; | |
15 import java.io.OutputStream; | |
16 import java.util.Arrays; | |
17 | |
18 /** | |
19 * Implements the run length encoding of the Microsoft RLE format. | |
20 * <p> | |
21 * Each line of a frame is compressed individually. A line consists of two-byte | |
22 * op-codes optionally followed by data. The end of the line is marked with | |
23 * the EOL op-code. | |
24 * <p> | |
25 * The following op-codes are supported: | |
26 * <ul> | |
27 * <li>{@code 0x00 0x00} | |
28 * <br>Marks the end of a line.</li> | |
29 * | |
30 * <li>{@code 0x00 0x01} | |
31 * <br>Marks the end of the bitmap.</li> | |
32 * | |
33 * <li>{@code 0x00 0x02 x y} | |
34 * <br> Marks a delta (skip). {@code x} and {@code y} | |
35 * indicate the horizontal and vertical offset from the current position. | |
36 * {@code x} and {@code y} are unsigned 8-bit values.</li> | |
37 * | |
38 * <li>{@code 0x00 n data{n} 0x00?} | |
39 * <br> Marks a literal run. {@code n} | |
40 * gives the number of data bytes that follow. {@code n} must be between 3 and | |
41 * 255. If n is odd, a pad byte with the value 0x00 must be added. | |
42 * </li> | |
43 * <li>{@code n data} | |
44 * <br> Marks a repetition. {@code n} | |
45 * gives the number of times the data byte is repeated. {@code n} must be | |
46 * between 1 and 255. | |
47 * </li> | |
48 * </ul> | |
49 * Example: | |
50 * <pre> | |
51 * Compressed data Expanded data | |
52 * | |
53 * 03 04 04 04 04 | |
54 * 05 06 06 06 06 06 06 | |
55 * 00 03 45 56 67 00 45 56 67 | |
56 * 02 78 78 78 | |
57 * 00 02 05 01 Move 5 right and 1 down | |
58 * 02 78 78 78 | |
59 * 00 00 End of line | |
60 * 09 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E | |
61 * 00 01 End of RLE bitmap | |
62 * </pre> | |
63 * | |
64 * References:<br/> | |
65 * <a href="http://wiki.multimedia.cx/index.php?title=Microsoft_RLE">http://wiki.multimedia.cx/index.php?title=Microsoft_RLE</a><br> | |
66 * | |
67 * @author Werner Randelshofer | |
68 * @version 1.1.1 2011-01-17 Removes unused imports. | |
69 * <br>1.1 2011-01-07 Improves performance. | |
70 * <br>1.0 2011-01-05 Created. | |
71 */ | |
72 public class MicrosoftRLEEncoder { | |
73 | |
74 private SeekableByteArrayOutputStream tempSeek=new SeekableByteArrayOutputStream(); | |
75 private DataChunkOutputStream temp=new DataChunkOutputStream(tempSeek); | |
76 | |
77 /** Encodes a 8-bit key frame. | |
78 * | |
79 * @param temp The output stream. Must be set to Big-Endian. | |
80 * @param data The image data. | |
81 * @param offset The offset to the first pixel in the data array. | |
82 * @param length The width of the image in data elements. | |
83 * @param step The number to add to offset to get to the next scanline. | |
84 */ | |
85 public void writeKey8(OutputStream out, byte[] data, int offset, int length, int step, int height) | |
86 throws IOException { | |
87 tempSeek.reset(); | |
88 int ymax = offset + height * step; | |
89 int upsideDown = ymax-step+offset; | |
90 | |
91 // Encode each scanline separately | |
92 for (int y = offset; y < ymax; y += step) { | |
93 int xy = upsideDown-y; | |
94 int xymax = xy + length; | |
95 | |
96 int literalCount = 0; | |
97 int repeatCount = 0; | |
98 for (; xy < xymax; ++xy) { | |
99 // determine repeat count | |
100 byte v = data[xy]; | |
101 for (repeatCount = 0; xy < xymax && repeatCount < 255; ++xy, ++repeatCount) { | |
102 if (data[xy] != v) { | |
103 break; | |
104 } | |
105 } | |
106 xy -= repeatCount; | |
107 if (repeatCount < 3) { | |
108 literalCount++; | |
109 if (literalCount == 254) { | |
110 temp.write(0);temp.write(literalCount); // Literal OP-code | |
111 temp.write(data, xy - literalCount + 1, literalCount); | |
112 literalCount = 0; | |
113 } | |
114 } else { | |
115 if (literalCount > 0) { | |
116 if (literalCount < 3) { | |
117 for (; literalCount > 0; --literalCount) { | |
118 temp.write(1); // Repeat OP-code | |
119 temp.write(data[xy - literalCount]); | |
120 } | |
121 } else { | |
122 temp.write(0);temp.write(literalCount); // Literal OP-code | |
123 temp.write(data, xy - literalCount, literalCount); | |
124 if (literalCount % 2 == 1) { | |
125 temp.write(0); // pad byte | |
126 } | |
127 literalCount = 0; | |
128 } | |
129 } | |
130 temp.write(repeatCount); // Repeat OP-code | |
131 temp.write(v); | |
132 xy += repeatCount - 1; | |
133 } | |
134 } | |
135 | |
136 // flush literal run | |
137 if (literalCount > 0) { | |
138 if (literalCount < 3) { | |
139 for (; literalCount > 0; --literalCount) { | |
140 temp.write(1); // Repeat OP-code | |
141 temp.write(data[xy - literalCount]); | |
142 } | |
143 } else { | |
144 temp.write(0);temp.write(literalCount); | |
145 temp.write(data, xy - literalCount, literalCount); | |
146 if (literalCount % 2 == 1) { | |
147 temp.write(0); // pad byte | |
148 } | |
149 } | |
150 literalCount = 0; | |
151 } | |
152 | |
153 temp.write(0);temp.write(0x0000);// End of line | |
154 } | |
155 temp.write(0);temp.write(0x0001);// End of bitmap | |
156 tempSeek.toOutputStream(out); | |
157 } | |
158 | |
159 /** Encodes a 8-bit delta frame. | |
160 * | |
161 * @param temp The output stream. Must be set to Big-Endian. | |
162 * @param data The image data. | |
163 * @param prev The image data of the previous frame. | |
164 * @param offset The offset to the first pixel in the data array. | |
165 * @param length The width of the image in data elements. | |
166 * @param step The number to add to offset to get to the next scanline. | |
167 */ | |
168 public void writeDelta8(OutputStream out, byte[] data, byte[] prev, int offset, int length, int step, int height) | |
169 throws IOException { | |
170 | |
171 tempSeek.reset(); | |
172 // Determine whether we can skip lines at the beginning | |
173 int ymin; | |
174 int ymax = offset + height * step; | |
175 int upsideDown = ymax-step+offset; | |
176 scanline: | |
177 for (ymin = offset; ymin < ymax; ymin += step) { | |
178 int xy = upsideDown-ymin; | |
179 int xymax = xy + length; | |
180 for (; xy < xymax; ++xy) { | |
181 if (data[xy] != prev[xy]) { | |
182 break scanline; | |
183 } | |
184 } | |
185 } | |
186 | |
187 if (ymin == ymax) { | |
188 // => Frame is identical to previous one | |
189 temp.write(0);temp.write(0x0001); // end of bitmap | |
190 return; | |
191 } | |
192 | |
193 if (ymin > offset) { | |
194 int verticalOffset = ymin / step; | |
195 while (verticalOffset > 255) { | |
196 temp.write(0);temp.write(0x0002); // Skip OP-code | |
197 temp.write(0); // horizontal offset | |
198 temp.write(255); // vertical offset | |
199 verticalOffset -= 255; | |
200 } | |
201 if (verticalOffset == 1) { | |
202 temp.write(0);temp.write(0x0000); // End of line OP-code | |
203 } else { | |
204 temp.write(0);temp.write(0x0002); // Skip OP-code | |
205 temp.write(0); // horizontal offset | |
206 temp.write(verticalOffset); // vertical offset | |
207 } | |
208 } | |
209 | |
210 | |
211 // Determine whether we can skip lines at the end | |
212 scanline: | |
213 for (; ymax > ymin; ymax -= step) { | |
214 int xy = upsideDown-ymax+step; | |
215 int xymax = xy + length; | |
216 for (; xy < xymax; ++xy) { | |
217 if (data[xy] != prev[xy]) { | |
218 break scanline; | |
219 } | |
220 } | |
221 } | |
222 //System.out.println("MicrosoftRLEEncoder ymin:" + ymin / step + " ymax" + ymax / step); | |
223 | |
224 | |
225 // Encode each scanline | |
226 int verticalOffset = 0; | |
227 for (int y = ymin; y < ymax; y += step) { | |
228 int xy = upsideDown-y; | |
229 int xymax = xy + length; | |
230 | |
231 // determine skip count | |
232 int skipCount = 0; | |
233 for (; xy < xymax; ++xy, ++skipCount) { | |
234 if (data[xy] != prev[xy]) { | |
235 break; | |
236 } | |
237 } | |
238 if (skipCount == length) { | |
239 // => the entire line can be skipped | |
240 ++verticalOffset; | |
241 if (verticalOffset == 255) { | |
242 temp.write(0);temp.write(0x0002); // Skip OP-code | |
243 temp.write(0); // horizontal offset | |
244 temp.write(255); // vertical offset | |
245 verticalOffset = 0; | |
246 } | |
247 continue; | |
248 } | |
249 | |
250 if (verticalOffset > 0 || skipCount > 0) { | |
251 if (verticalOffset == 1 && skipCount == 0) { | |
252 temp.write(0);temp.write(0x0000); // End of line OP-code | |
253 } else { | |
254 temp.write(0);temp.write(0x0002); // Skip OP-code | |
255 temp.write(Math.min(255, skipCount)); // horizontal offset | |
256 skipCount -= 255; | |
257 temp.write(verticalOffset); // vertical offset | |
258 } | |
259 verticalOffset = 0; | |
260 } | |
261 while (skipCount > 0) { | |
262 temp.write(0);temp.write(0x0002); // Skip OP-code | |
263 temp.write(Math.min(255, skipCount)); // horizontal offset | |
264 temp.write(0); // vertical offset | |
265 skipCount -= 255; | |
266 } | |
267 | |
268 int literalCount = 0; | |
269 int repeatCount = 0; | |
270 for (; xy < xymax; ++xy) { | |
271 // determine skip count | |
272 for (skipCount = 0; xy < xymax; ++xy, ++skipCount) { | |
273 if (data[xy] != prev[xy]) { | |
274 break; | |
275 } | |
276 } | |
277 xy -= skipCount; | |
278 | |
279 // determine repeat count | |
280 byte v = data[xy]; | |
281 for (repeatCount = 0; xy < xymax && repeatCount < 255; ++xy, ++repeatCount) { | |
282 if (data[xy] != v) { | |
283 break; | |
284 } | |
285 } | |
286 xy -= repeatCount; | |
287 | |
288 if (skipCount < 4 && xy + skipCount < xymax && repeatCount < 3) { | |
289 literalCount++; | |
290 if (literalCount == 254) { | |
291 temp.write(0);temp.write(literalCount); // Literal OP-code | |
292 temp.write(data, xy - literalCount + 1, literalCount); | |
293 literalCount = 0; | |
294 } | |
295 } else { | |
296 if (literalCount > 0) { | |
297 if (literalCount < 3) { | |
298 for (; literalCount > 0; --literalCount) { | |
299 temp.write(1); // Repeat OP-code | |
300 temp.write(data[xy - literalCount]); | |
301 } | |
302 } else { | |
303 temp.write(0);temp.write(literalCount); | |
304 temp.write(data, xy - literalCount, literalCount); | |
305 if (literalCount % 2 == 1) { | |
306 temp.write(0); // pad byte | |
307 } | |
308 } | |
309 literalCount = 0; | |
310 } | |
311 if (xy + skipCount == xymax) { | |
312 // => we can skip until the end of the line without | |
313 // having to write an op-code | |
314 xy += skipCount - 1; | |
315 } else if (skipCount >= repeatCount) { | |
316 while (skipCount > 255) { | |
317 temp.write(0);temp.write(0x0002); // Skip OP-code | |
318 temp.write(255); | |
319 temp.write(0); | |
320 xy += 255; | |
321 skipCount -= 255; | |
322 } | |
323 temp.write(0);temp.write(0x0002); // Skip OP-code | |
324 temp.write(skipCount); | |
325 temp.write(0); | |
326 xy += skipCount - 1; | |
327 } else { | |
328 temp.write(repeatCount); // Repeat OP-code | |
329 temp.write(v); | |
330 xy += repeatCount - 1; | |
331 } | |
332 } | |
333 } | |
334 | |
335 // flush literal run | |
336 if (literalCount > 0) { | |
337 if (literalCount < 3) { | |
338 for (; literalCount > 0; --literalCount) { | |
339 temp.write(1); // Repeat OP-code | |
340 temp.write(data[xy - literalCount]); | |
341 } | |
342 } else { | |
343 temp.write(0);temp.write(literalCount); | |
344 temp.write(data, xy - literalCount, literalCount); | |
345 if (literalCount % 2 == 1) { | |
346 temp.write(0); // pad byte | |
347 } | |
348 } | |
349 } | |
350 | |
351 temp.write(0);temp.write(0x0000); // End of line OP-code | |
352 } | |
353 | |
354 temp.write(0);temp.write(0x0001);// End of bitmap | |
355 tempSeek.toOutputStream(out); | |
356 } | |
357 | |
358 public static void main(String[] args) { | |
359 byte[] data = {// | |
360 8, 2, 3, 4, 4, 3,7,7,7, 8,// | |
361 8, 1, 1, 1, 1, 2,7,7,7, 8,// | |
362 8, 0, 2, 0, 0, 0,7,7,7, 8,// | |
363 8, 2, 2, 3, 4, 4,7,7,7, 8,// | |
364 8, 1, 4, 4, 4, 5,7,7,7, 8}; | |
365 | |
366 | |
367 byte[] prev = {// | |
368 8, 3, 3, 3, 3, 3,7,7,7, 8,// | |
369 8, 1, 1, 1, 1, 1,7,7,7, 8, // | |
370 8, 5, 5, 5, 5, 0,7,7,7, 8,// | |
371 8, 2, 2, 0, 0, 0,7,7,7, 8,// | |
372 8, 2, 0, 0, 0, 5,7,7,7, 8}; | |
373 ByteArrayOutputStream buf = new ByteArrayOutputStream(); | |
374 DataChunkOutputStream out = new DataChunkOutputStream(buf); | |
375 MicrosoftRLEEncoder enc = new MicrosoftRLEEncoder(); | |
376 | |
377 try { | |
378 enc.writeDelta8(out, data, prev, 1, 8, 10, 5); | |
379 //enc.writeKey8(out, data, 1, 8, 10,5); | |
380 out.close(); | |
381 | |
382 byte[] result = buf.toByteArray(); | |
383 System.out.println("size:" + result.length); | |
384 System.out.println(Arrays.toString(result)); | |
385 System.out.print("0x ["); | |
386 | |
387 for (int i = 0; i < result.length; i++) { | |
388 if (i != 0) { | |
389 System.out.print(','); | |
390 } | |
391 String hex = "00" + Integer.toHexString(result[i]); | |
392 System.out.print(hex.substring(hex.length() - 2)); | |
393 } | |
394 System.out.println(']'); | |
395 | |
396 } catch (IOException ex) { | |
397 ex.printStackTrace(); | |
398 } | |
399 } | |
400 } |