Mercurial > jmeCapture
view src/com/aurellem/capture/MicrosoftRLEEncoder.java @ 6:0634e72bad3e
don't need StdAudio since I'm now using builtin javax to write wav files
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 12:07:56 -0700 |
parents | a92de00f0414 |
children |
line wrap: on
line source
1 /*2 * @(#)AppleRLEEncoder.java 1.1.1 2011-01-173 *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 the8 * license agreement you entered into with Werner Randelshofer.9 * For details see accompanying license terms.10 */11 package com.aurellem.capture;13 import java.io.ByteArrayOutputStream;14 import java.io.IOException;15 import java.io.OutputStream;16 import java.util.Arrays;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-byte22 * op-codes optionally followed by data. The end of the line is marked with23 * 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 and41 * 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 be46 * between 1 and 255.47 * </li>48 * </ul>49 * Example:50 * <pre>51 * Compressed data Expanded data52 *53 * 03 04 04 04 0454 * 05 06 06 06 06 06 0655 * 00 03 45 56 67 00 45 56 6756 * 02 78 78 7857 * 00 02 05 01 Move 5 right and 1 down58 * 02 78 78 7859 * 00 00 End of line60 * 09 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E61 * 00 01 End of RLE bitmap62 * </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 Randelshofer68 * @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 {74 private SeekableByteArrayOutputStream tempSeek=new SeekableByteArrayOutputStream();75 private DataChunkOutputStream temp=new DataChunkOutputStream(tempSeek);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;91 // Encode each scanline separately92 for (int y = offset; y < ymax; y += step) {93 int xy = upsideDown-y;94 int xymax = xy + length;96 int literalCount = 0;97 int repeatCount = 0;98 for (; xy < xymax; ++xy) {99 // determine repeat count100 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-code111 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-code119 temp.write(data[xy - literalCount]);120 }121 } else {122 temp.write(0);temp.write(literalCount); // Literal OP-code123 temp.write(data, xy - literalCount, literalCount);124 if (literalCount % 2 == 1) {125 temp.write(0); // pad byte126 }127 literalCount = 0;128 }129 }130 temp.write(repeatCount); // Repeat OP-code131 temp.write(v);132 xy += repeatCount - 1;133 }134 }136 // flush literal run137 if (literalCount > 0) {138 if (literalCount < 3) {139 for (; literalCount > 0; --literalCount) {140 temp.write(1); // Repeat OP-code141 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 byte148 }149 }150 literalCount = 0;151 }153 temp.write(0);temp.write(0x0000);// End of line154 }155 temp.write(0);temp.write(0x0001);// End of bitmap156 tempSeek.toOutputStream(out);157 }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 {171 tempSeek.reset();172 // Determine whether we can skip lines at the beginning173 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 }187 if (ymin == ymax) {188 // => Frame is identical to previous one189 temp.write(0);temp.write(0x0001); // end of bitmap190 return;191 }193 if (ymin > offset) {194 int verticalOffset = ymin / step;195 while (verticalOffset > 255) {196 temp.write(0);temp.write(0x0002); // Skip OP-code197 temp.write(0); // horizontal offset198 temp.write(255); // vertical offset199 verticalOffset -= 255;200 }201 if (verticalOffset == 1) {202 temp.write(0);temp.write(0x0000); // End of line OP-code203 } else {204 temp.write(0);temp.write(0x0002); // Skip OP-code205 temp.write(0); // horizontal offset206 temp.write(verticalOffset); // vertical offset207 }208 }211 // Determine whether we can skip lines at the end212 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);225 // Encode each scanline226 int verticalOffset = 0;227 for (int y = ymin; y < ymax; y += step) {228 int xy = upsideDown-y;229 int xymax = xy + length;231 // determine skip count232 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 skipped240 ++verticalOffset;241 if (verticalOffset == 255) {242 temp.write(0);temp.write(0x0002); // Skip OP-code243 temp.write(0); // horizontal offset244 temp.write(255); // vertical offset245 verticalOffset = 0;246 }247 continue;248 }250 if (verticalOffset > 0 || skipCount > 0) {251 if (verticalOffset == 1 && skipCount == 0) {252 temp.write(0);temp.write(0x0000); // End of line OP-code253 } else {254 temp.write(0);temp.write(0x0002); // Skip OP-code255 temp.write(Math.min(255, skipCount)); // horizontal offset256 skipCount -= 255;257 temp.write(verticalOffset); // vertical offset258 }259 verticalOffset = 0;260 }261 while (skipCount > 0) {262 temp.write(0);temp.write(0x0002); // Skip OP-code263 temp.write(Math.min(255, skipCount)); // horizontal offset264 temp.write(0); // vertical offset265 skipCount -= 255;266 }268 int literalCount = 0;269 int repeatCount = 0;270 for (; xy < xymax; ++xy) {271 // determine skip count272 for (skipCount = 0; xy < xymax; ++xy, ++skipCount) {273 if (data[xy] != prev[xy]) {274 break;275 }276 }277 xy -= skipCount;279 // determine repeat count280 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;288 if (skipCount < 4 && xy + skipCount < xymax && repeatCount < 3) {289 literalCount++;290 if (literalCount == 254) {291 temp.write(0);temp.write(literalCount); // Literal OP-code292 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-code300 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 byte307 }308 }309 literalCount = 0;310 }311 if (xy + skipCount == xymax) {312 // => we can skip until the end of the line without313 // having to write an op-code314 xy += skipCount - 1;315 } else if (skipCount >= repeatCount) {316 while (skipCount > 255) {317 temp.write(0);temp.write(0x0002); // Skip OP-code318 temp.write(255);319 temp.write(0);320 xy += 255;321 skipCount -= 255;322 }323 temp.write(0);temp.write(0x0002); // Skip OP-code324 temp.write(skipCount);325 temp.write(0);326 xy += skipCount - 1;327 } else {328 temp.write(repeatCount); // Repeat OP-code329 temp.write(v);330 xy += repeatCount - 1;331 }332 }333 }335 // flush literal run336 if (literalCount > 0) {337 if (literalCount < 3) {338 for (; literalCount > 0; --literalCount) {339 temp.write(1); // Repeat OP-code340 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 byte347 }348 }349 }351 temp.write(0);temp.write(0x0000); // End of line OP-code352 }354 temp.write(0);temp.write(0x0001);// End of bitmap355 tempSeek.toOutputStream(out);356 }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};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();377 try {378 enc.writeDelta8(out, data, prev, 1, 8, 10, 5);379 //enc.writeKey8(out, data, 1, 8, 10,5);380 out.close();382 byte[] result = buf.toByteArray();383 System.out.println("size:" + result.length);384 System.out.println(Arrays.toString(result));385 System.out.print("0x [");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(']');396 } catch (IOException ex) {397 ex.printStackTrace();398 }399 }400 }