Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * @(#)RunLengthCodec.java  
   *
   * Copyright © 2011 Werner Randelshofer, Goldau, Switzerland.
   * All rights reserved.
   *
   * You may not use, copy or modify this file, except in compliance with the
   * license agreement you entered into with Werner Randelshofer.
   * For details see accompanying license terms.
  */
 package org.monte.media.avi;
 
 import static java.lang.Math.*;
 import static org.monte.media.VideoFormatKeys.*;
 import static org.monte.media.BufferFlag.*;

RunLengthCodec encodes a BufferedImage as a byte[] array.

This codec only works with the AVI file format. Other formats, such as QuickTime, use a different encoding for run-length compressed video.

This codec currently only supports encoding from a BufferedImage into the file format. Decoding support may be added in the future.

Supported input formats:

    Format with BufferedImage.class, any width, any height, depth=8.
Supported output formats:
    Format with byte[].class, same width and height as input format, depth=8.
The codec supports lossless delta- and key-frame encoding of images with 8 bits per pixel.

The codec does not encode the color palette of an image. This must be done separately.

A frame is compressed line by line from bottom to top.

Each line of a frame is compressed individually. A line consists of two-byte op-codes optionally followed by data. The end of the line is marked with the EOL op-code.

The following op-codes are supported:

  • 0x00 0x00
    Marks the end of a line.
  • 0x00 0x01
    Marks the end of the bitmap.
  • 0x00 0x02 x y
    Marks a delta (skip). x and y indicate the horizontal and vertical offset from the current position. x and y are unsigned 8-bit values.
  • 0x00 n data{n 0x00?}
    Marks a literal run. n gives the number of data bytes that follow. n must be between 3 and 255. If n is odd, a pad byte with the value 0x00 must be added.
  • n data
    Marks a repetition. n gives the number of times the data byte is repeated. n must be between 1 and 255.
Example:
 Compressed data         Expanded data

 03 04                   04 04 04
 05 06                   06 06 06 06 06
 00 03 45 56 67 00       45 56 67
 02 78                   78 78
 00 02 05 01             Move 5 right and 1 down
 02 78                   78 78
 00 00                   End of line
 09 1E                   1E 1E 1E 1E 1E 1E 1E 1E 1E
 00 01                   End of RLE bitmap
 
References:
http://wiki.multimedia.cx/index.php?title=Microsoft_RLE

Author(s):
Werner Randelshofer
Version:
$Id: RunLengthCodec.java 299 2013-01-03 07:40:18Z werner $
public class RunLengthCodec extends AbstractVideoCodec {
    private byte[] previousPixels;
    private int frameCounter;
    public RunLengthCodec() {
        super(new Format[]{
                    new Format(.
                            true), //
                },
                new Format[]{
                    new Format(.,
                    byte[].class,
                            true,8), //
                });
    }
    @Override
    public void reset() {
         = 0;
    }
    @Override
    public int process(Buffer inBuffer out) {
        if (==nullreturn ;
            return encode(inout);
        } else {
            return decode(inout);
        }
    }
    private int encode(Buffer inBuffer out) {
        out.setMetaTo(in);
        out.format=;
        if (in.isFlag()) {
            return ;
        }
        
        ByteArrayImageOutputStream tmp;
        if (out.data instanceof byte[]) {
            tmp = new ByteArrayImageOutputStream((byte[]) out.data);
        } else {
            tmp = new ByteArrayImageOutputStream();
        }
        // Handle sub-image
        Rectangle r;
        int scanlineStride;
        if (in.data instanceof BufferedImage) {
            BufferedImage image = (BufferedImagein.data;
            WritableRaster raster = image.getRaster();
            scanlineStride = raster.getSampleModel().getWidth();
            r = raster.getBounds();
            r.x -= raster.getSampleModelTranslateX();
            r.y -= raster.getSampleModelTranslateY();
            out.header = image.getColorModel();
        } else {
            r = new Rectangle(0, 0, .get(), .get());
            scanlineStride = .get();
            out.header = null;
        }
        int offset = r.x + r.y * scanlineStride;
        boolean isKeyframe = == 0
                ||  % .get(,.get().intValue()) == 0;
        ++;
        try {
            byte[] pixels = getIndexed8(in);
            if (pixels == null) {
                return ;
            }
            if (isKeyframe) {
                writeKey8(tmppixelsr.widthr.heightoffsetscanlineStride);
                out.setFlag();
            } else {
                writeDelta8(tmppixelsr.widthr.heightoffsetscanlineStride);
                out.clearFlag();
            }
            out.data = tmp.getBuffer();
            out.offset = 0;
            out.length = (inttmp.getStreamPosition();
            //
            if ( == null) {
                 = pixels.clone();
            } else {
                System.arraycopy(pixels, 0, , 0, pixels.length);
            }
            return ;
        } catch (IOException ex) {
            ex.printStackTrace();
            out.setFlag();
            return ;
        }
    }
    private int decode(Buffer inBuffer out) {
        return ;
    }

    
Encodes an 8-bit key frame.

Parameters:
out The output stream.
data The image data.
offset The offset to the first pixel in the data array.
width The width of the image in data elements.
scanlineStride The number to append to offset to get to the next scanline.
    public void writeKey8(OutputStream outbyte[] dataint widthint heightint offsetint scanlineStridethrows IOException {
        ByteArrayImageOutputStream buf = new ByteArrayImageOutputStream(data.length);
        writeKey8(bufdatawidthheightoffsetscanlineStride);
        buf.toOutputStream(out);
    }

    
Encodes an 8-bit key frame.

Parameters:
out The output stream.
data The image data.
offset The offset to the first pixel in the data array.
width The width of the image in data elements.
scanlineStride The number to append to offset to get to the next scanline.
    public void writeKey8(ImageOutputStream outbyte[] dataint widthint heightint offsetint scanlineStride)
            throws IOException {
        int ymax = offset + height * scanlineStride;
        int upsideDown = ymax - scanlineStride + offset;
        // Encode each scanline separately
        for (int y = offsety < ymaxy += scanlineStride) {
            int xy = upsideDown - y;
            int xymax = xy + width;
            int literalCount = 0;
            int repeatCount = 0;
            for (; xy < xymax; ++xy) {
                // determine repeat count
                byte v = data[xy];
                for (repeatCount = 0; xy < xymax && repeatCount < 255; ++xy, ++repeatCount) {
                    if (data[xy] != v) {
                        break;
                    }
                }
                xy -= repeatCount;
                if (repeatCount < 3) {
                    literalCount++;
                    if (literalCount == 254) {
                        out.write(0);
                        out.write(literalCount); // Literal OP-code
                        out.write(dataxy - literalCount + 1, literalCount);
                        literalCount = 0;
                    }
                } else {
                    if (literalCount > 0) {
                        if (literalCount < 3) {
                            for (; literalCount > 0; --literalCount) {
                                out.write(1); // Repeat OP-code
                                out.write(data[xy - literalCount]);
                            }
                        } else {
                            out.write(0);
                            out.write(literalCount); // Literal OP-code
                            out.write(dataxy - literalCountliteralCount);
                            if (literalCount % 2 == 1) {
                                out.write(0); // pad byte
                            }
                            literalCount = 0;
                        }
                    }
                    out.write(repeatCount); // Repeat OP-code
                    out.write(v);
                    xy += repeatCount - 1;
                }
            }
            // flush literal run
            if (literalCount > 0) {
                if (literalCount < 3) {
                    for (; literalCount > 0; --literalCount) {
                        out.write(1); // Repeat OP-code
                        out.write(data[xy - literalCount]);
                    }
                } else {
                    out.write(0);
                    out.write(literalCount);
                    out.write(dataxy - literalCountliteralCount);
                    if (literalCount % 2 == 1) {
                        out.write(0); // pad byte
                    }
                }
                literalCount = 0;
            }
            out.write(0);
            out.write(0x0000);// End of line
        }
        out.write(0);
        out.write(0x0001);// End of bitmap
    }

    
Encodes an 8-bit key frame.

Parameters:
out The output stream.
data The image data.
offset The offset to the first pixel in the data array.
width The width of the image in data elements.
scanlineStride The number to append to offset to get to the next scanline.
    public void writeDelta8(OutputStream outbyte[] databyte[] prevint widthint heightint offsetint scanlineStridethrows IOException {
        ByteArrayImageOutputStream buf = new ByteArrayImageOutputStream(data.length);
        writeDelta8(bufdataprevwidthheightoffsetscanlineStride);
        buf.toOutputStream(out);
    }

    
Encodes an 8-bit delta frame.

Parameters:
out The output stream.
data The image data.
prev The image data of the previous frame.
offset The offset to the first pixel in the data array.
width The width of the image in data elements.
scanlineStride The number to append to offset to get to the next scanline.
    public void writeDelta8(ImageOutputStream outbyte[] databyte[] prevint widthint heightint offsetint scanlineStride)
            throws IOException {
        int ymax = offset + height * scanlineStride;
        int upsideDown = ymax - scanlineStride + offset;
        // Encode each scanline
        int verticalOffset = 0;
        for (int y = offsety < ymaxy += scanlineStride) {
            int xy = upsideDown - y;
            int xymax = xy + width;
            // determine skip count
            int skipCount = 0;
            for (; xy < xymax; ++xy, ++skipCount) {
                if (data[xy] != prev[xy]) {
                    break;
                }
            }
            if (skipCount == width) {
                // => the entire line can be skipped
                ++verticalOffset;
                continue;
            }
            while (verticalOffset > 0 || skipCount > 0) {
                if (verticalOffset == 1 && skipCount == 0) {
                    out.write(0x00);
                    out.write(0x00); // End of line OP-code
                    verticalOffset = 0;
                } else {
                    out.write(0x00);
                    out.write(0x02); // Skip OP-code
                    out.write(min(255, skipCount)); // horizontal offset
                    out.write(min(255, verticalOffset)); // vertical offset
                    skipCount -= min(255, skipCount);
                    verticalOffset -= min(255, verticalOffset);
                }
            }
            int literalCount = 0;
            int repeatCount = 0;
            for (; xy < xymax; ++xy) {
                // determine skip count
                for (skipCount = 0; xy < xymax; ++xy, ++skipCount) {
                    if (data[xy] != prev[xy]) {
                        break;
                    }
                }
                xy -= skipCount;
                // determine repeat count
                byte v = data[xy];
                for (repeatCount = 0; xy < xymax && repeatCount < 255; ++xy, ++repeatCount) {
                    if (data[xy] != v) {
                        break;
                    }
                }
                xy -= repeatCount;
                if (skipCount < 4 && xy + skipCount < xymax && repeatCount < 3) {
                    literalCount++;
                } else {
                    while (literalCount > 0) {
                        if (literalCount < 3) {
                            out.write(1); // Repeat OP-code
                            out.write(data[xy - literalCount]);
                            literalCount--;
                        } else {
                            int literalRun = min(254, literalCount);
                            out.write(0);
                            out.write(literalRun); // Literal OP-code
                            out.write(dataxy - literalCountliteralRun);
                            if (literalRun % 2 == 1) {
                                out.write(0); // pad byte
                            }
                            literalCount -= literalRun;
                        }
                    }
                    if (xy + skipCount == xymax) {
                        // => we can skip until the end of the line without
                        //    having to write an op-code
                        xy += skipCount - 1;
                    } else if (skipCount >= repeatCount) {
                        while (skipCount > 0) {
                            out.write(0);
                            out.write(0x0002); // Skip OP-code
                            out.write(min(255, skipCount));
                            out.write(0);
                            xy += min(255, skipCount);
                            skipCount -= min(255, skipCount);
                        }
                        xy -= 1;
                    } else {
                        out.write(repeatCount); // Repeat OP-code
                        out.write(v);
                        xy += repeatCount - 1;
                    }
                }
            }
            // flush literal run
            while (literalCount > 0) {
                if (literalCount < 3) {
                    out.write(1); // Repeat OP-code
                    out.write(data[xy - literalCount]);
                    literalCount--;
                } else {
                    int literalRun = min(254, literalCount);
                    out.write(0);
                    out.write(literalRun); // Literal OP-code
                    out.write(dataxy - literalCountliteralRun);
                    if (literalRun % 2 == 1) {
                        out.write(0); // pad byte
                    }
                    literalCount -= literalRun;
                }
            }
            out.write(0);
            out.write(0x0000); // End of line OP-code
        }
        out.write(0);
        out.write(0x0001);// End of bitmap
    }
New to GrepCode? Check out our FAQ X