/*
GNU General Public License
CacheWolf is a software for PocketPC, Win and Linux that
enables paperless caching.
It supports the sites geocaching.com and opencaching.de
Copyright (C) 2006 CacheWolf development team
See http://www.cachewolf.de/ for more information.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package CacheWolf.navi;
import CacheWolf.Preferences;
import CacheWolf.utils.Common;
import ewe.fx.Color;
import ewe.fx.Dimension;
import ewe.fx.Image;
import ewe.fx.ImageDecodingException;
import ewe.fx.ImageNotFoundException;
import ewe.fx.Point;
import ewe.fx.UnsupportedImageFormatException;
import ewe.fx.mImage;
import ewe.graphics.AniImage;
import ewe.io.BufferedInputStream;
import ewe.io.FileInputStream;
import ewe.io.IOException;
import ewe.util.ByteArray;
/**
* class that can be used with any x and any y
* it will save that location and
* make itself automatically invisible if it is not on the screen.
* Call setScreenSize to set the ScreenSize
*
* @author pfeffer
*
*/
public class MapImage extends AniImage {
// contains the theoretical location even if it the location is out of the screen.
// if the image is on the screen, it contains the same as location
public Point locAlways = new Point();
public static Dimension screenDim;
boolean hidden = false;
/**
* constructs an empty MapImage
*/
public MapImage() {
super();
if (screenDim == null)
screenDim = new Dimension(0, 0);
}
/**
* constructs the MapImage from a filename representing an Image in the Filesystem<br>
* or (if filename contains a '!')
* extracts the Image from a Cachebox .pack file
* @param filename
* @throws ImageDecodingException
* @throws UnsupportedImageFormatException
* @throws ImageNotFoundException
* @throws ewe.sys.SystemResourceException
*/
public MapImage(String filename) throws ImageDecodingException, UnsupportedImageFormatException, ImageNotFoundException, ewe.sys.SystemResourceException {
// f kommt aus MapInfoObject.getImagePathAndName
if (screenDim == null)
screenDim = new Dimension(0, 0);
Preferences.itself().log("create MapImage from: " + filename);
if (filename.indexOf("!") < 0) {
// the following code is only necessary because of an Bug in ewe 1.49, which doesn't read from a fakefilesystem.
// If there were no bug, calling super(f) would be sufficient
// super(f);
// copied from super()
ewe.io.File file = ewe.sys.Vm.newFileObject();
file.set(null, filename);
try {
// ByteArray imbytes = ewe.io.IO.readAllBytes(input, knownSize, stopAfterKnownSize);(file, null, true);
// this would be possible if ewe 1.49 wouldn't have another bug:
// fakefilesystem doesn't implement (override) length(), it only overrides getLenght(),
// that's why readallBytes will call the original File implementation
// and causes a NullpointerException
setImage(new Image(new FileInputStream(filename).toReadableStream(), 0), 0);
freeSource();
} catch (IOException e) {
throw new ImageNotFoundException(filename); // in order to behave the same way as super would have
}
} else {
// it is a packfile
String p[] = ewe.util.mString.split(filename, '!');
int bboxMinX = Common.parseInt(p[1]);
int bboxMinY = Common.parseInt(p[2]);
int bboxStride = Common.parseInt(p[3]);
long OffsetToIndex = Long.valueOf(p[4]).longValue();
int ZoomWanted = Common.parseInt(p[5]);
int xWanted = Common.parseInt(p[6]);
int yWanted = Common.parseInt(p[7]);
ByteArray ba = null;
Image im;
try {
int index = (yWanted - bboxMinY) * bboxStride + (xWanted - bboxMinX) - 1;
long offset = OffsetToIndex + index * 8;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(p[0]), 20480);
long skipped = 0;
do {
skipped = skipped + bis.skip(offset);
} while (offset - skipped > 0);
long tileOffset = readReverseLong(bis);
long nextOffset = readReverseLong(bis);
int length = (int) (nextOffset - tileOffset);
Preferences.itself().log(" tileOffset/length for x/y/zoom (" + xWanted + "/" + yWanted + "/" + ZoomWanted + ")= " + tileOffset + "/" + length);
if (length == 0) {
Preferences.itself().log("wanted == 0 (nextOffset - tileOffset)");
bis.close();
throw new ImageNotFoundException(filename); // in order to behave the same way as file
}
skipped = 0;
do {
skipped = skipped + bis.skip(tileOffset - offset - 16);
} while (tileOffset - offset - 16 - skipped > 0);
byte[] buffer = new byte[length];
int readTilNow = 0;
int stillToRead = length;
do {
int anzRead = bis.read(buffer, readTilNow, stillToRead);
readTilNow = readTilNow + anzRead;
stillToRead = stillToRead - anzRead;
} while (stillToRead > 0);
bis.close();
/*
// check for support / conversion
byte[] signature = new byte[]
{ (byte) 137, (byte) 80, (byte) 78, (byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10 };
if (Arrays.equals(signature, get(buffer, 0, 8)))
{
// es ist ein png
byte BitDepth = buffer[24];
// byte ColourType = buffer[25];
// byte CompressionMethod = buffer[26];
// BitDepth not supported by pixmap
switch (BitDepth)
{
case 4:
// Logger.DEBUG("[PackBase] unsupported png in Pack " + this.Filename + " tile: " + desc);
InputStream in = new ByteArrayInputStream(buffer);
BufferedImage img = ImageIO.read(in);
ByteArrayOutputStream bas = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", bas);
byte[] data = bas.toByteArray();
bas.close();
return data;
// break;
case 8:
// supported
break;
default:
// perhaps supported
break;
}
}
*/
ba = new ByteArray(buffer);
im = new Image(ba, 0);
setImage(im);
} catch (Exception exc) {
Preferences.itself().log(exc + " Error getting image from pack-file " + p[0] + " for " + p[6] + "/" + p[7] + "/" + p[5] + " Bufferlength " + ba.length);
// ignore throw new ImageNotFoundException(f); // in order to behave the same way as super would have
}
}
}
private long readReverseLong(BufferedInputStream bis) throws IOException {
int byte8 = bis.read();
int byte7 = bis.read();
int byte6 = bis.read();
int byte5 = bis.read();
int byte4 = bis.read();
int byte3 = bis.read();
int byte2 = bis.read();
int byte1 = bis.read();
return (long) (((byte1 & 0xFF) << 56) + ((byte2 & 0xFF) << 48) + ((byte3 & 0xFF) << 40) + ((byte4 & 0xFF) << 32) + ((byte5 & 0xFF) << 24) + ((byte6 & 0xFF) << 16) + ((byte7 & 0xFF) << 8) + (byte8 & 0xFF));
}
/**
* constructs from an existing Instance of mImage
*
* @param im
*/
public MapImage(mImage im) {
super(im);
if (screenDim == null)
screenDim = new Dimension(0, 0);
}
/*
*
*/
/**
* Best you call this routine before you make any instance of MapImage<br>
* If the windows size changes after instantiation call screenDimChanged() for every symbol.<br>
*
* @param w
* @param h
*/
public static void setScreenSize(int w, int h) {
screenDim = new Dimension(w, h);
}
/**
* Overrides: setImage(...) in mImage<br>
* make an empty MapImage work<br>
* or<br>
* changes an existing one
*
*/
public void setImage(Image im, Color c) {
super.setImage(im, c);
if (screenDim == null)
screenDim = new Dimension(0, 0);
}
// Overrides: setLocation(...) in AniImage
public void setLocation(int x, int y) {
locAlways.x = x;
locAlways.y = y;
if (!hidden && isOnScreen()) {
super.setLocation(x, y);
properties &= ~mImage.IsInvisible;
} else {
properties |= mImage.IsInvisible;
super.move(0, 0);
}
}
/**
* // Overrides: move(...) in AniImage<br>
* setting the localways to x,y
* Moves the image
* if it is not hidden and on Screen it is moved to x,y and set visible.<br>
* else it is set invisible and moved to 0,0
* The move of AniImage does:
* If the new location is different to the current location then the HasMoved bit in properties will be set.<br>
* It will NOT clear this bit if the image actually has not moved.<br>
* This bit must be cleared manually.<br>
*/
public void move(int x, int y) {
locAlways.x = x;
locAlways.y = y;
if (!hidden && isOnScreen()) {
super.move(x, y);
properties &= ~mImage.IsInvisible;
} else {
properties |= mImage.IsInvisible;
super.move(0, 0);
}
}
/**
* uses the locAlways position instead of the Rect locations position<br>
* but uses the locations Dimension width / height<br>
* to check against the Screens Dimension screenDim (saved here)<br>
* @return isOnScreen
*
*/
public boolean isOnScreen() {
if ((locAlways.x + location.width > 0 && locAlways.x < screenDim.width) && (locAlways.y + location.height > 0 && locAlways.y < screenDim.height))
return true;
else
return false;
}
/**
* corrects the visibility by doing the move with the remembered locAlways position
*
*/
public void screenDimChanged() {
move(locAlways.x, locAlways.y);
}
/**
* the Image will not be moved. Simply setting the properties.
*/
public void hide() {
hidden = true;
properties |= mImage.IsInvisible;
}
/**
* the Image will be moved to the remembered locAlways position and checked if it is on screen.
*/
public void unhide() {
hidden = false;
move(locAlways.x, locAlways.y);
}
}