gt:                     int[] pixels = getRGB24(in);
context: /*
   * @(#)TechSmithCodec.java 
   *
   * Copyright (c) 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 org.monte.media.BufferFlag.DISCARD;
  import static org.monte.media.BufferFlag.KEYFRAME;
  import static org.monte.media.BufferFlag.SAME_DATA;
  import static org.monte.media.FormatKeys.EncodingKey;
  import static org.monte.media.FormatKeys.FrameRateKey;
  import static org.monte.media.FormatKeys.KeyFrameIntervalKey;
  import static org.monte.media.FormatKeys.MIME_AVI;
  import static org.monte.media.FormatKeys.MIME_JAVA;
  import static org.monte.media.FormatKeys.MIME_QUICKTIME;
  import static org.monte.media.FormatKeys.MediaTypeKey;
  import static org.monte.media.FormatKeys.MimeTypeKey;
  import static org.monte.media.VideoFormatKeys.COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE;
  import static org.monte.media.VideoFormatKeys.CompressorNameKey;
  import static org.monte.media.VideoFormatKeys.DataClassKey;
  import static org.monte.media.VideoFormatKeys.DepthKey;
  import static org.monte.media.VideoFormatKeys.ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE;
  import static org.monte.media.VideoFormatKeys.ENCODING_BUFFERED_IMAGE;
  import static org.monte.media.VideoFormatKeys.FixedFrameRateKey;
  import static org.monte.media.VideoFormatKeys.HeightKey;
  import static org.monte.media.VideoFormatKeys.WidthKey;
  
  import java.awt.Point;
  import java.awt.Rectangle;
  import java.awt.image.BufferedImage;
  import java.awt.image.ColorModel;
  import java.awt.image.DataBuffer;
  import java.awt.image.DataBufferByte;
  import java.awt.image.DataBufferInt;
  import java.awt.image.DataBufferUShort;
  import java.awt.image.DirectColorModel;
  import java.awt.image.IndexColorModel;
  import java.awt.image.Raster;
  import java.awt.image.WritableRaster;
  import java.io.IOException;
  import java.util.Hashtable;
  
  import org.monte.media.AbstractVideoCodec;
  import org.monte.media.Buffer;
  import org.monte.media.BufferFlag;
  import org.monte.media.Format;
  import org.monte.media.FormatKeys.MediaType;
  import org.monte.media.io.SeekableByteArrayOutputStream;
  
  /**
   * {@code TechSmithCodec} (tscc) encodes a BufferedImage as a byte[] array. <p>
   * The TechSmith codec works with AVI and QuickTime. <p> This codec supports
   * encoding from a {@code BufferedImage} into the file format, and decoding from
   * the file format to a {@code BufferedImage}. <p> <p> This codec does not
   * encode the color palette of an image. This must be done separately. <p>
   * Supported input formats: <ul> {@code Format} with
   * {@code BufferedImage.class}, any width, any height, depth=8,16 or 24. </ul>
   * Supported output formats: <ul> {@code Format} with {@code byte[].class}, same
   * width and height as input format, depth=8,16 or 24. </ul> The codec supports
   * lossless delta- and key-frame encoding of images with 8, 16 or 24 bits per
   * pixel. <p> Compression of a frame is performed in two steps: In the first,
   * step a frame is compressed line by line from bottom to top. In the second
   * step the resulting data is compressed again using zlib compression. <p> Apart
   * from the second compression step and the support for 16- and 24-bit data,
   * this encoder is identical to the {@link RunLengthCodec}. <p> 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. <p> The following op-codes are supported: <ul> <li>{@code 0x00 0x00}
   * <br>Marks the end of a line.</li>
   *
   * <li>{@code  0x00 0x01} <br>Marks the end of the bitmap.</li>
   *
   * <li>{@code 0x00 0x02 x y} <br> Marks a delta (skip). {@code x} and {@code y}
   * indicate the horizontal and vertical offset from the current position.
   * {@code x} and {@code y} are unsigned 8-bit values.</li>
   *
   * <li>{@code 0x00 n pixel{n} 0x00?} <br> Marks a literal run. {@code n} gives
   * the number of 8-, 16- or 24-bit pixels that follow. {@code n} must be between
   * 3 and 255. If n is odd and 8-bit pixels are used, a pad byte with the value
   * 0x00 must be added. </li> <li>{@code n pixel} <br> Marks a repetition.
   * {@code n} gives the number of times the given pixel is repeated. {@code n}
   * must be between 1 and 255. </li> </ul> Example:
   * <pre>
   * 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
   * </pre>
   *
   * References:<br/> <a
   * href="http://wiki.multimedia.cx/index.php?title=TechSmith_Screen_Capture_Codec"
   * >http://wiki.multimedia.cx/index.php?title=TechSmith_Screen_Capture_Codec</a><br>
   *
   *
   * @author Werner Randelshofer
   * @version $Id: TechSmithCodec.java 299 2013-01-03 07:40:18Z werner $
   */
  public class TechSmithCodec extends AbstractVideoCodec {
  
      private TechSmithCodecCore state;
      private Object previousPixels;
      private int frameCounter;
      private Object oldPixels;
      private Object newPixels;
  
      public TechSmithCodec() {
          super(new Format[]{
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA,
                      EncodingKey, ENCODING_BUFFERED_IMAGE, FixedFrameRateKey, true), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 8), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 16), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 24), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 8), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 16), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 24), //
                  },
                  new Format[]{
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_JAVA,
                      EncodingKey, ENCODING_BUFFERED_IMAGE, FixedFrameRateKey, true), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 8), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 16), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_AVI,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 24), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 8), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 16), //
                      new Format(MediaTypeKey, MediaType.VIDEO, MimeTypeKey, MIME_QUICKTIME,
                      EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
                      CompressorNameKey, COMPRESSOR_NAME_AVI_TECHSMITH_SCREEN_CAPTURE,
                      DataClassKey, byte[].class,
                      FixedFrameRateKey, true, DepthKey, 24), //
                  });
          name = "TechSmith Screen Capture";
      }
  
      @Override
      public void reset() {
          state = null;
          frameCounter = 0;
      }
  
      @Override
      public int process(Buffer in, Buffer out) {
          if (state == null) {
              state = new TechSmithCodecCore();
          }
          if (in.isFlag(BufferFlag.DISCARD)) {
              out.setMetaTo(in);
              return CODEC_OK;
          }
          
          if (outputFormat.get(EncodingKey).equals(ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE)) {
              return encode(in, out);
          } else {
              return decode(in, out);
          }
      }
  
      public int decode(Buffer in, Buffer out) {
          out.setMetaTo(in);
          out.format = outputFormat;
          out.length = 1;
          out.offset = 0;
  
          if (state == null) {
              state = new TechSmithCodecCore();
          }
  
          int width = outputFormat.get(WidthKey);
          int height = outputFormat.get(HeightKey);
          int inputDepth = inputFormat.get(DepthKey);
          int outputDepth = outputFormat.get(DepthKey);
  
          boolean isKeyFrame;
          try {
              if (outputDepth == 8) {
                  if (!(newPixels instanceof byte[]) || ((byte[]) newPixels).length != width * height) {
                      newPixels = new byte[width * height];
                  }
                  isKeyFrame = state.decode8((byte[]) in.data, in.offset, in.length, (byte[]) newPixels, (byte[]) newPixels, width, height, false);
              } else {
                  if (!(newPixels instanceof int[]) || ((int[]) newPixels).length != width * height) {
                      newPixels = new int[width * height];
                  }
                  if (inputDepth == 8) {
                      isKeyFrame = state.decode8((byte[]) in.data, in.offset, in.length, (int[]) newPixels, (int[]) newPixels, width, height, false);
                  } else if (inputDepth == 16) {
                      isKeyFrame = state.decode16((byte[]) in.data, in.offset, in.length, (int[]) newPixels, (int[]) newPixels, width, height, false);
                  } else {
                      isKeyFrame = state.decode24((byte[]) in.data, in.offset, in.length, (int[]) newPixels, (int[]) newPixels, width, height, false);
                  }
              }
          } catch (IOException e) {
              e.printStackTrace();
              out.setFlag(DISCARD);
              return CODEC_FAILED;
          }
  
          MyBufferedImage img = null;
          if (out.data instanceof MyBufferedImage) {
              img = (MyBufferedImage) out.data;
          }
          switch (outputDepth) {
              case 8: {
                  int imgType = BufferedImage.TYPE_BYTE_INDEXED; // FIXME - Don't hardcode this value
                  if (img == null || img.getWidth() != width || img.getHeight() != height || img.getType() != imgType) {
                      int[] cmap = new int[256];
                      IndexColorModel icm = new IndexColorModel(8, 256, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
                      img = new MyBufferedImage(width, height, imgType, icm);
                  } else {
                      MyBufferedImage oldImg = img;
                      img = new MyBufferedImage(oldImg.getColorModel(), oldImg.getRaster(), oldImg.isAlphaPremultiplied(), null);
                  }
                  int[] cmap = new int[256];//state.getPalette();
                  for (int i = 0; i < 256; i++) {
                      cmap[i] = 255 << 24 | i | i << 8 | i << 16;
                  }
                  IndexColorModel icm = new IndexColorModel(8, 256, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
                  img.setColorModel(icm);
                  byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
                  System.arraycopy((byte[]) newPixels, 0, pixels, 0, width * height);
              }
              break;
              case 15: {
                  int imgType = BufferedImage.TYPE_USHORT_555_RGB;
                  if (img == null || img.getWidth() != width || img.getHeight() != height || img.getType() != imgType) {
                      DirectColorModel cm = new DirectColorModel(15, 0x1f << 10, 0x1f << 5, 0x1f << 0);
                      img = new MyBufferedImage(cm, Raster.createWritableRaster(cm.createCompatibleSampleModel(width, height), new Point(0, 0)), false, new Hashtable());
                  } else {
                      MyBufferedImage oldImg = img;
                      img = new MyBufferedImage(oldImg.getColorModel(), oldImg.getRaster(), oldImg.isAlphaPremultiplied(), null);
                  }
                  short[] pixels = ((DataBufferUShort) img.getRaster().getDataBuffer()).getData();
                  System.arraycopy((short[]) newPixels, 0, pixels, 0, width * height);
              }
              break;
              case 16: {
                  //int imgType = BufferedImage.TYPE_USHORT_565_RGB;
                  int imgType = BufferedImage.TYPE_INT_RGB;
                  if (img == null || img.getWidth() != width || img.getHeight() != height || img.getType() != imgType) {
                      //DirectColorModel cm = new DirectColorModel(24, 0x1f << 11, 0x3f << 5, 0x1f << 0);
                      DirectColorModel cm = new DirectColorModel(24, 0xff << 16, 0xff << 8, 0xff << 0);
                      img = new MyBufferedImage(cm, Raster.createWritableRaster(cm.createCompatibleSampleModel(width, height), new Point(0, 0)), false, new Hashtable());
                  } else {
                      MyBufferedImage oldImg = img;
                      img = new MyBufferedImage(oldImg.getColorModel(), oldImg.getRaster(), oldImg.isAlphaPremultiplied(), null);
                  }
                  /*
                   short[] pixels = ((DataBufferUShort) img.getRaster().getDataBuffer()).getData();
                   System.arraycopy((short[]) newPixels, 0, pixels, 0, width * height);
                   */
                  int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
                  System.arraycopy((int[]) newPixels, 0, pixels, 0, width * height);
              }
              break;
              case 24: {
                  int imgType = BufferedImage.TYPE_INT_RGB;
                  if (img == null || img.getWidth() != width || img.getHeight() != height || img.getType() != imgType) {
                      DirectColorModel cm = new DirectColorModel(24, 0xff << 16, 0xff << 8, 0xff << 0);
                      img = new MyBufferedImage(cm, Raster.createWritableRaster(cm.createCompatibleSampleModel(width, height), new Point(0, 0)), false, new Hashtable());
                  } else {
                      MyBufferedImage oldImg = img;
                      img = new MyBufferedImage(oldImg.getColorModel(), oldImg.getRaster(), oldImg.isAlphaPremultiplied(), null);
                  }
                  int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
                  System.arraycopy((int[]) newPixels, 0, pixels, 0, width * height);
              }
              break;
              default:
                  throw new UnsupportedOperationException("Unsupported depth:" + outputDepth);
          }
  
          out.setFlag(KEYFRAME, isKeyFrame);
  
          out.data = img;
          return CODEC_OK;
      }
  
      public int encode(Buffer in, Buffer out) {
          out.setMetaTo(in);
          out.format = outputFormat;
          if (in.isFlag(DISCARD)) {
              return CODEC_OK;
          }
  
          SeekableByteArrayOutputStream tmp;
          if (out.data instanceof byte[]) {
              tmp = new SeekableByteArrayOutputStream((byte[]) out.data);
          } else {
              tmp = new SeekableByteArrayOutputStream();
          }
  
          boolean isKeyframe = frameCounter == 0
                  || frameCounter % outputFormat.get(KeyFrameIntervalKey, outputFormat.get(FrameRateKey).intValue()) == 0;
          out.setFlag(KEYFRAME, isKeyframe);
          out.clearFlag(SAME_DATA);
          frameCounter++;
  
          // Handle sub-image
          Rectangle r;
          int scanlineStride;
          if (in.data instanceof BufferedImage) {
              BufferedImage image = (BufferedImage) in.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, outputFormat.get(WidthKey), outputFormat.get(HeightKey));
              scanlineStride = outputFormat.get(WidthKey);
              out.header = null;
          }
          int offset = r.x + r.y * scanlineStride;
  
          try {
              switch (outputFormat.get(DepthKey)) {
                  case 8: {
                      byte[] pixels = getIndexed8(in);
                      if (pixels == null) {
                          out.setFlag(DISCARD);
                          return CODEC_OK;
                      }
  
                      if (isKeyframe) {
                          state.encodeKey8(tmp, pixels, outputFormat.get(WidthKey), outputFormat.get(HeightKey), offset, scanlineStride);
                      } else {
                          if (in.isFlag(SAME_DATA)) {
                              state.encodeSameDelta8(tmp, pixels, (byte[]) previousPixels, outputFormat.get(WidthKey), outputFormat.get(HeightKey), offset, scanlineStride);
                          } else {
                              state.encodeDelta8(tmp, pixels, (byte[]) previousPixels, outputFormat.get(WidthKey), outputFormat.get(HeightKey), offset, scanlineStride);
                          }
                          out.clearFlag(KEYFRAME);
                      }
                      if (previousPixels == null) {
                          previousPixels = pixels.clone();
                      } else {
                          System.arraycopy(pixels, 0, (byte[]) previousPixels, 0, pixels.length);
                      }
                      break;
                  }
                  case 16: {
                      short[] pixels = getRGB15(in); // 16-bit TSCC is actually just 15-bit
                      if (pixels == null) {
                          out.setFlag(DISCARD);
                          return CODEC_OK;
                      }
  
                      if (isKeyframe) {
                          state.encodeKey16(tmp, pixels, outputFormat.get(WidthKey), outputFormat.get(HeightKey), offset, scanlineStride);
                      } else {
                          if (in.isFlag(SAME_DATA)) {
                              state.encodeSameDelta16(tmp, pixels, (short[]) previousPixels, outputFormat.get(WidthKey), outputFormat.get(HeightKey), offset, scanlineStride);
                          } else {
                              state.encodeDelta16(tmp, pixels, (short[]) previousPixels, outputFormat.get(WidthKey), outputFormat.get(HeightKey), offset, scanlineStride);
                          }
                      }
                      if (previousPixels == null) {
                          previousPixels = pixels.clone();
                      } else {
                          System.arraycopy(pixels, 0, (short[]) previousPixels, 0, pixels.length);
                      }
                      break;
                  }
                  case 24: {
  