package com.javaxyq.util; import java.awt.Image; import java.awt.image.ColorModel; import java.awt.image.DirectColorModel; import java.awt.image.ImageProducer; import java.awt.image.MemoryImageSource; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import com.javaxyq.core.Toolkit; import com.javaxyq.io.RandomAcessInputStream; import com.javaxyq.widget.TCPFrame; /** * tcp/tca���������� * * @author ����ΰ * @date create 2009-12-08 */ public class TCPImageDecoder { static final int TYPE_ALPHA = 0x00;// ǰ2λ static final int TYPE_ALPHA_PIXEL = 0x20;// ǰ3λ 0010 0000 static final int TYPE_ALPHA_REPEAT = 0x00;// ǰ3λ static final int TYPE_FLAG = 0xC0;// 2����ǰ2λ 1100 0000 static final int TYPE_PIXELS = 0x40;// ����ǰ2λ 0100 0000 static final int TYPE_REPEAT = 0x80;// 1000 0000 static final int TYPE_SKIP = 0xC0; // 1100 0000 /** �ļ�ͷ��� */ static final String WAS_FILE_TAG = "SP"; static final int TCP_HEADER_SIZE = 12; // Reference Pixel(���ҵ�) private int refPixelX; private int refPixelY; /** ������������ */ private int animCount; /** ������֡�� */ private int frameCount; /** ԭʼ��ɫ�� */ private short[] originPalette; /** ��ǰ��ɫ�� */ private short[] palette; /** ������ */ private int width; /** ����߶� */ private int height; private RandomAcessInputStream in; private int[] frameOffsets; private String filename; private List<MemoryImageSource>animSources = new ArrayList<MemoryImageSource>(); private AnimationThread animator = new AnimationThread(animSources); private int[] animpixels; private int[] totalPixels; public TCPImageDecoder() { palette = new short[256]; originPalette = new short[256]; animator.start(); } /** * @param string * @throws Exception */ public TCPImageDecoder(String filename) throws Exception { palette = new short[256]; originPalette = new short[256]; load(filename); animator.start(); } public int getAnimCount() { return animCount; } public int getFrameCount() { return frameCount; } public int getHeight() { return height; } public short[] getOriginPalette() { return originPalette; } public short[] getPalette() { return palette; } public int getRefPixelX() { return refPixelX; } public int getRefPixelY() { return refPixelY; } public int getWidth() { return width; } public void load(File file) throws IllegalStateException, FileNotFoundException, IOException { load(new FileInputStream(file)); } public void load(InputStream is) throws IllegalStateException, IOException { in = prepareInputStream(is); // tcp ��Ϣ short headerSize = in.readUnsignedShort(); animCount = in.readUnsignedShort(); frameCount = in.readUnsignedShort(); width = in.readUnsignedShort(); height = in.readUnsignedShort(); refPixelX = in.readUnsignedShort(); refPixelY = in.readUnsignedShort(); // ��ȡ֡��ʱ��Ϣ int len = headerSize - TCP_HEADER_SIZE; if (len < 0) { throw new IllegalStateException("֡��ʱ��Ϣ����: " + len); } int[] delays = new int[len]; for (int i = 0; i < len; i++) { delays[i] = in.read(); } // ��ȡ��ɫ�� in.seek(headerSize + 4); for (int i = 0; i < 256; i++) { originPalette[i] = in.readUnsignedShort(); } // ���Ƶ�ɫ�� System.arraycopy(originPalette, 0, palette, 0, 256); // ֡ƫ���б� frameOffsets = new int[animCount * frameCount]; in.seek(headerSize + 4 + 512); for (int i = 0; i < animCount; i++) { for (int n = 0; n < frameCount; n++) { frameOffsets[i * frameCount + n] = in.readInt() + headerSize + 4; } } } public void load(String filename) throws Exception { this.filename = filename; // InputStream fileIn = getClass().getResourceAsStream(filename); // File file = new File(filename); // InputStream fileIn = new FileInputStream(file); load(Toolkit.getInputStream(filename)); } /** * ��ȡij֡���� * * @param animIndex * ���������� * @param frameIndex * ֡������ * @return */ public TCPFrame readFrame(int animIndex, int frameIndex) { int offset = frameOffsets[animIndex * frameCount + frameIndex]; if (offset == 0) return null;// blank frame try { in.seek(offset); int frameX = in.readInt(); int frameY = in.readInt(); int frameWidth = in.readInt(); int frameHeight = in.readInt(); // ColorModel cm = new DirectColorModel(32, 0xff0000, 0xff00, 0xff, // 0xff000000); ColorModel cm = new DirectColorModel(21, 0xf800, 0x07E0, 0x001F, 0x1f0000); int[] pixels = getPixels(offset, frameWidth, frameHeight); ImageProducer producer = new MemoryImageSource(frameWidth, frameHeight, cm, pixels, 0, frameWidth); Image image = java.awt.Toolkit.getDefaultToolkit().createImage(producer); return new TCPFrame(image, frameX, frameY, frameWidth, frameHeight); } catch (IOException e) { e.printStackTrace(); } return null; } public Image readAnimation(int index) { index %= animCount; int startFrame = index * frameCount; int endFrame = startFrame + frameCount; int frameSize = width * height; int[] pixels = new int[frameSize * frameCount]; for (int i = startFrame; i < endFrame; i++) { try { int offset = frameOffsets[i]; in.seek(offset); int frameX = in.readInt(); int frameY = in.readInt(); int frameWidth = in.readInt(); int frameHeight = in.readInt(); getPixels(offset, pixels, (i - startFrame) * frameSize, width, height); } catch (IOException e) { e.printStackTrace(); } } animpixels = new int[frameSize]; totalPixels = pixels; ColorModel cm = new DirectColorModel(21, 0xf800, 0x07E0, 0x001F, 0x1f0000); System.arraycopy(pixels, 0, animpixels, 0, frameSize); MemoryImageSource producer = new MemoryImageSource(width, height, cm, animpixels, 0, width); producer.setAnimated(true); Image image = java.awt.Toolkit.getDefaultToolkit().createImage(producer); registerAnimation(producer); return image; } private int[] getPixels(int frameOffset, int width, int height) throws IOException { int[] pixels = new int[width * height]; return getPixels(frameOffset, pixels, 0, width, height); } /** * ��ȡ����ij֡ͼƬ���������� * * @param pixels * * @param frameIndex * @return * @throws IOException */ private int[] getPixels(int frameOffset, int[] pixels, int offset, int width, int height) throws IOException { in.seek(frameOffset); int offx = in.readInt(); int offy = in.readInt(); int w = in.readInt(); int h = in.readInt(); // ����������ƫ�� int[] lineOffsets = new int[h]; for (int l = 0; l < h; l++) { lineOffsets[l] = in.readInt(); } int b, x, c; int index; int count; int x0 = refPixelX -offx; int y0 = refPixelY - offy; for (int y = 0; y < h; y++) { x = 0; in.seek(lineOffsets[y] + frameOffset); while (x < w) { b = in.read(); switch ((b & TYPE_FLAG)) { case TYPE_ALPHA: if ((b & TYPE_ALPHA_PIXEL) > 0) { index = in.read(); c = palette[index]; pixels[offset + (y0 + y) * width + x0 + x++] = c + ((b & 0x1F) << 16); } else if (b != 0) {// ??? count = b & 0x1F;// count b = in.read();// alpha index = in.read(); c = palette[index]; for (int i = 0; i < count; i++) { pixels[offset + (y0 + y) * width + x0 + x++] = c + ((b & 0x1F) << 16); } } else {// block end if (x > w) { System.err.println("block end error: [" + y + "][" + x + "/" + w + "]"); continue; } else if (x == 0) { // System.err.println("x==0"); } else { x = w; } } break; case TYPE_PIXELS: count = b & 0x3F; for (int i = 0; i < count; i++) { index = in.read(); pixels[offset + (y0 + y) * width + x0 + x++] = palette[index] + (0x1F << 16); } break; case TYPE_REPEAT: count = b & 0x3F; index = in.read(); c = palette[index]; for (int i = 0; i < count; i++) { pixels[offset + (y0 + y) * width + x0 + x++] = c + (0x1F << 16); } break; case TYPE_SKIP: count = b & 0x3F; x += count; break; } } if (x > w) System.err.println("block end error: [" + y + "][" + x + "/" + w + "]"); } return pixels; } private RandomAcessInputStream prepareInputStream(InputStream in) throws IOException, IllegalStateException { byte[] buf; RandomAcessInputStream randomIn; buf = new byte[2]; in.mark(10); in.read(buf, 0, 2); String flag = new String(buf, 0, 2); if (!WAS_FILE_TAG.equals(flag)) { throw new IllegalStateException("�ļ�ͷ��־����:" + print(buf)); } if (in instanceof RandomAcessInputStream) { in.reset(); randomIn = (RandomAcessInputStream) in; } else { byte[] buf2 = new byte[in.available() + buf.length]; System.arraycopy(buf, 0, buf2, 0, buf.length); int a = 0, count = buf.length; while (in.available() > 0) { a = in.read(buf2, count, in.available()); count += a; } // construct a new seekable stream randomIn = new RandomAcessInputStream(buf2); } // skip header randomIn.seek(2); return randomIn; } private String print(byte[] buf) { String output = "["; for (byte b : buf) { output += b; output += ","; } output += "]"; return output; } public void resetPalette() { System.arraycopy(originPalette, 0, palette, 0, 256); } private void registerAnimation(MemoryImageSource is) { animSources.add(is); } private class AnimationThread extends Thread{ private List<MemoryImageSource> animSources; public AnimationThread(List<MemoryImageSource>animSources) { this.animSources = animSources; setDaemon(true); setName("TCPImageAnimation"); } public void run() { int frameIndex = 0; int frameSize = width*height; while(true) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(animSources.size()>0) { frameIndex ++; frameIndex %= frameCount; System.arraycopy(totalPixels, frameIndex*frameSize, animpixels, 0, animpixels.length); for (int i = 0; i < animSources.size(); i++) { MemoryImageSource imgprod = animSources.get(i); imgprod.newPixels(); } } } } } }