annotate src/com/aurellem/capture/MicrosoftRLEEncoder.java @ 4:edaa7e7806e4

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