//TODO ���̼߳���,paint���ȴ�
package com.javaxyq.tools;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.io.File;
import java.util.LinkedList;
import java.util.Vector;
import javax.swing.JComponent;
import com.javaxyq.util.MapDecoder;
/**
* ��ʾ��Ϸ��ͼ�Ŀؼ�
*
* @author Administrator
*
*/
public class JMap extends JComponent {
private static final long serialVersionUID = 1L;
private MapDecoder decoder;
private Rectangle maxVisibleRect = new Rectangle();
private LinkedList<MapImage> loadedImages = new LinkedList<MapImage>();
private MapImage[][] visibleImages;
public JMap(File file) {
loadMap(file);
}
public JMap() {
}
@Override
public void paint(Graphics g) {
paintComponent(g);
}
protected void paintComponent(Graphics g) {
if (decoder == null)
return;
Rectangle clipRect = getVisibleRect();// g.getClipBounds();
if (!maxVisibleRect.contains(clipRect)) {
loadRectMapData(clipRect);
}
drawMap(g);
}
/**
* ��g�ϻ�����ǰ�ɼ������Ӧ�ĵ�ͼͼ��
*
* @param g
*/
protected void drawMap(Graphics g) {
g = g.create();
MapImage img0 = visibleImages[0][0];
int xOffset = img0.h * 320;
int yOffset = img0.v * 240;
g.translate(xOffset, yOffset);
for (int h = 0; h < visibleImages.length; h++) {
for (int v = 0; v < visibleImages[0].length; v++) {
g.drawImage(visibleImages[h][v].image, h * 320, v * 240, this);
}
}
g.dispose();
}
protected void drawCell(Graphics g) {
}
/**
* ����ָ������
*
* @param g
* @param clipRect
*/
public void drawRect(Graphics g, Rectangle clipRect) {
if (decoder == null)
return;
if (!maxVisibleRect.contains(clipRect)) {
loadRectMapData(clipRect);
}
g = g.create();
MapImage img0 = visibleImages[0][0];
int xOffset = img0.h * 320 - clipRect.x;
int yOffset = img0.v * 240 - clipRect.y;
g.translate(xOffset, yOffset);
for (int h = 0; h < visibleImages.length; h++) {
for (int v = 0; v < visibleImages[0].length; v++) {
g.drawImage(visibleImages[h][v].image, h * 320, v * 240, this);
}
}
g.dispose();
}
/**
* ����clipRect�õ��ĵ�ͼ��
*
* @param clipRect
*/
private void loadRectMapData(Rectangle clipRect) {
MapArea mapArea = getMapArea(clipRect);
MapImage mapimage;
int rows = mapArea.right - mapArea.left + 1;
int cols = mapArea.bottom - mapArea.top + 1;
visibleImages = new MapImage[rows][cols];
// long begTime, endTime;
// begTime = System.currentTimeMillis();
Vector<ImageLoadThread> threads = new Vector<ImageLoadThread>();
for (int h = mapArea.left; h <= mapArea.right; h++) {
for (int v = mapArea.top; v <= mapArea.bottom; v++) {
mapimage = removeMapImage(h, v);
if (mapimage == null) {
byte[] imageData = decoder.getJpegData(h, v);
Image image = Toolkit.getDefaultToolkit().createImage(imageData);
mapimage = new MapImage(image, h, v);
ImageLoadThread thread = getLoadThread();
thread.setLoadingImage(image);
threads.add(thread);
}
loadedImages.add(0, mapimage);
if (loadedImages.size() > 16) {
loadedImages.removeLast();
}
visibleImages[h - mapArea.left][v - mapArea.top] = mapimage;
}
}
for (ImageLoadThread thread : threads) {
synchronized (thread) {
while (!thread.isFinished())
try {
thread.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
releaseLoadThread(thread);
}
// endTime = System.currentTimeMillis();
// System.out.println("load image :" + (endTime - begTime));
maxVisibleRect.x = mapArea.left * 320;
maxVisibleRect.y = mapArea.top * 240;
maxVisibleRect.width = (mapArea.right - mapArea.left + 1) * 320;
maxVisibleRect.height = (mapArea.bottom - mapArea.top + 1) * 240;
}
private void releaseImage(MapImage img) {
for (int w = 0; w < visibleImages.length; w++) {
for (int h = 0; h < visibleImages[w].length; h++) {
if(visibleImages[w][h] == img) {
visibleImages[w][h] = null;
break;
}
}
}
System.gc();
}
Vector<ImageLoadThread> threadBuffer = new Vector<ImageLoadThread>(10);
private void releaseLoadThread(ImageLoadThread thread) {
threadBuffer.add(thread);
}
private ImageLoadThread getLoadThread() {
if (threadBuffer.size() > 0) { return threadBuffer.remove(threadBuffer.size() - 1); }
ImageLoadThread thread = new ImageLoadThread();
thread.start();
return thread;
}
/**
* ���clipRect��Ӧ��ͼ���ݿ�����
*
* @param clipRect
* @return
*/
private MapArea getMapArea(Rectangle clipRect) {
MapArea mapArea = new MapArea();
mapArea.left = (int) Math.ceil(clipRect.x / 320.0) - 1;
mapArea.top = (int) Math.ceil(clipRect.y / 240.0) - 1;
mapArea.right = (int) Math.ceil((clipRect.x + clipRect.width) / 320.0) - 1;
mapArea.bottom = (int) Math.ceil((clipRect.y + clipRect.height) / 240.0) - 1;
if (mapArea.left < 0)
mapArea.left = 0;
if (mapArea.top < 0)
mapArea.top = 0;
// System.out.println(mapArea);
return mapArea;
}
/**
* �ӻ�������ȡij��ͼ���
*
* @param h
* @param v
* @return
*/
private MapImage removeMapImage(int h, int v) {
int length = loadedImages.size();
MapImage mapImage = null;
int index = -1;
for (int i = 0; i < length; i++) {
mapImage = loadedImages.get(i);
if (mapImage.h == h && mapImage.v == v) {
index = i;
break;
}
}
return index == -1 ? null : loadedImages.remove(index);
}
/**
* ����������Ϸ��ͼ���ݿ�����
*
* @author Langlauf
* @date
*/
class MapArea {
public int left;
public int right;
public int top;
public int bottom;
@Override
public String toString() {
return "[" + left + "," + top + "," + right + "," + bottom + "]";
}
}
/**
* ����������ͼ��һƬ����������
*
* @author Langlauf
* @date
*/
class MapImage {
public Image image;
// public String mapname;
public int h;
public int v;
public MapImage(Image image, int h, int v) {
this.image = image;
this.h = h;
this.v = v;
}
}
/**
* ����һ���ļ�
*
* @param file
* @return
*/
public boolean loadMap(File file) {
if (file == null)
return false;
try {
decoder = new MapDecoder(file);
setSize(decoder.getWidth(), decoder.getHeight());
setPreferredSize(new Dimension(decoder.getWidth(), decoder.getHeight()));
loadedImages = new LinkedList<MapImage>();
maxVisibleRect.height = 0;
maxVisibleRect.width = 0;
} catch (Exception e) {
System.err.println("��ͼ�ļ�����:" + file.getName());
e.printStackTrace();
return false;
}
return true;
}
public int getMapWidth() {
return decoder.getWidth();
}
public int getMapHeight() {
return decoder.getHeight();
}
public Dimension getMapSize() {
return new Dimension(decoder.getWidth(), decoder.getHeight());
}
public Dimension getSegments() {
return new Dimension(decoder.getHorSegmentCount(), decoder.getVerSegmentCount());
}
public class ImageLoadThread extends Thread {
private Image image;
protected Component component = new Component() {
};
protected MediaTracker tracker = new MediaTracker(component);
private int mediaTrackerID;
private boolean isFinished;
private boolean isCompleted;
/**
* Returns an ID to use with the MediaTracker in loading an image.
*/
private int getNextID() {
return ++mediaTrackerID;
}
public ImageLoadThread() {
setDaemon(true);
}
public void setLoadingImage(Image image) {
this.image = image;
synchronized (this) {
notifyAll();
}
}
public void run() {
while (true) {
synchronized (this) {
// load image
isFinished = false;
isCompleted = false;
int id = getNextID();
tracker.addImage(image, id);
try {
tracker.waitForID(id, 0);
isCompleted = true;
} catch (InterruptedException e) {
System.out.println("INTERRUPTED while loading Image");
}
tracker.removeImage(image, id);
isFinished = true;
image = null;
notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
// e.printStackTrace();
}
}
}
}
public boolean isCompleted() {
return isCompleted;
}
public boolean isFinished() {
return isFinished;
}
}
public MapDecoder getDecoder() {
return decoder;
}
}