Hi Nick

btw any progress on true color pois with colormode 32 - although in my
experience only Mapsource/Basecamp seem to render this type correctly.

I've now implemented colour modes 16 and 32 for the true colour
image type. As usual I ignore any ColorMode=N declaration and
determine the lowest colour mode that is required to represent the
image and use that.

Attached is the patch and a sample .txt file showing the different
ways of writing the image. I've tested it against TYPWiz2 and you
can round trip between the two programs. I'm not actually used the TYP files...

..Steve

Index: src/uk/me/parabola/imgfmt/app/typ/AlphaAdder.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/imgfmt/app/typ/AlphaAdder.java	(revision )
+++ src/uk/me/parabola/imgfmt/app/typ/AlphaAdder.java	(revision )
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 or
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+package uk.me.parabola.imgfmt.app.typ;
+
+/**
+ * Interface for adding an alpha value to a previously saved colour value.
+ *
+ * The current TYP editors place the alpha value after the colour definition, eg:
+ *
+ *   "#992299" alpha 5
+ *
+ * so we need to add the alpha value to a colour that has already been read
+ * in.
+ *
+ * @author Steve Ratcliffe
+ */
+public interface AlphaAdder {
+
+	/**
+	 * Add an alpha value to the last colour that was saved.
+	 *
+	 * @param alpha A true alpha value ie 0 is transparent, 255 opaque.
+	 */
+	public void addAlpha(int alpha);
+}
Index: src/uk/me/parabola/mkgmap/scan/TokenScanner.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/mkgmap/scan/TokenScanner.java	(revision 2186)
+++ src/uk/me/parabola/mkgmap/scan/TokenScanner.java	(revision )
@@ -318,6 +318,12 @@
 		return new WordInfo(sb.toString(), quotec != 0);
 	}
 
+	/**
+	 * Check the value of the next token without consuming it.
+	 *
+	 * @param val String value to compare against.
+	 * @return True if the next token has the same value as the argument.
+	 */
 	public boolean checkToken(String val) {
 		skipSpace();
 		Token tok = peekToken();
Index: src/uk/me/parabola/imgfmt/app/typ/TrueImage.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/imgfmt/app/typ/TrueImage.java	(revision 2186)
+++ src/uk/me/parabola/imgfmt/app/typ/TrueImage.java	(revision )
@@ -12,34 +12,106 @@
  */
 package uk.me.parabola.imgfmt.app.typ;
 
+import uk.me.parabola.imgfmt.app.BitWriter;
 import uk.me.parabola.imgfmt.app.ImgFileWriter;
 
 /**
  * A true colour image.
  *
- * This represented by an array of int.
+ * The image is represented by an array of int, with each int in RGBA format.
  *
  * @author Steve Ratcliffe
  */
 public class TrueImage implements Image {
 	private final ColourInfo colourInfo;
 	private final int[] image;
-
+	
+	// If this is mode 16, then the transparent colour is set.
+	private int transparentPixel;
+
 	public TrueImage(ColourInfo colourInfo, int[] image) {
+		analyzeColours(image, colourInfo);
 		this.colourInfo = colourInfo;
 		this.image = image;
 	}
 
+	/**
+	 * Write out the image. It is a set of pixel values that are full RGB values, rather than
+	 * table driven as in the other image type. If the colour mode is 32 the colours have an
+	 * extra 4 bit opacity value following.
+	 *
+	 * If the colour mode is 16, then the transparent pixel is written just before the image
+	 * itself.
+	 */
 	public void write(ImgFileWriter writer) {
 		int width = colourInfo.getWidth();
 		int height = colourInfo.getHeight();
 
-		int n = width * height;
-		for (int count = 0; count < n; count++) {
-			int col = image[count];
-			writer.put((byte) ((col>>8) & 0xff));
-			writer.put((byte) ((col>>16) & 0xff));
-			writer.put((byte) ((col>>24) & 0xff));
+		int mode = colourInfo.getColourMode();
+
+		// For mode 16, the transparent pixel precedes the pixmap data.
+		if (mode == 16) {
+			writer.put((byte) (transparentPixel>>8));
+			writer.put((byte) (transparentPixel>>16));
+			writer.put((byte) (transparentPixel>>24));
 		}
+
+		boolean hasAlpha = mode == 32;
+		for (int h = 0; h < height; h++) {
+			// Each row is padded to a byte boundary, creating a new bit writer for every
+			// row ensures that happens.
+			BitWriter bitWriter = new BitWriter();
+
+			for (int w = 0; w < width; w++) {
+				int col = image[h * width + w];
+
+				bitWriter.putn(col>>8 & 0xff, 8);
+				bitWriter.putn(col>>16 & 0xff, 8);
+				bitWriter.putn(col>>24 & 0xff, 8);
+
+				if (hasAlpha) {
+					int alpha = 0xff - (col & 0xff);
+					alpha = ColourInfo.alphaRound4(alpha);
+					bitWriter.putn(alpha, 4);
+				}
+			}
+			writer.put(bitWriter.getBytes(), 0, bitWriter.getLength());
+		}
+	}
+
+	/**
+	 * Analyze the colours and determine if this should be a mode 16 or 32 image.
+	 *
+	 * By default it will be a mode 0 image. If there is any transparency the appropriate
+	 * colour mode will be selected.
+	 *
+	 * @param image An images as an array of integers. Each integer is a colour in RGBA format.
+	 * @param colourInfo The colour mode will be set in this.
+	 */
+	private void analyzeColours(int[] image, ColourInfo colourInfo) {
+		boolean hasTransparent = false;
+		boolean hasAlpha = false;
+
+		int nPixels = colourInfo.getWidth() * colourInfo.getHeight();
+		for (int i = 0; i < nPixels; i++) {
+			int col = image[i];
+			int a = col & 0xff;
+			if (a == 0) {
+				// Completely transparent, change all transparent pixels to the same value
+				if (hasTransparent)
+					image[i] = transparentPixel;
+				else
+					transparentPixel = image[i];
+				hasTransparent = true;
+			} else if (a < 255) {
+				// Partially transparent
+				hasAlpha = true;
+			}
+		}
+
+		if (hasAlpha)
+			colourInfo.setColourMode(32);
+		else if (hasTransparent)
+			colourInfo.setColourMode(16);
 	}
 }
Index: src/uk/me/parabola/mkgmap/typ/CommonSection.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/mkgmap/typ/CommonSection.java	(revision 2186)
+++ src/uk/me/parabola/mkgmap/typ/CommonSection.java	(revision )
@@ -16,6 +16,7 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import uk.me.parabola.imgfmt.app.typ.AlphaAdder;
 import uk.me.parabola.imgfmt.app.typ.BitmapImage;
 import uk.me.parabola.imgfmt.app.typ.ColourInfo;
 import uk.me.parabola.imgfmt.app.typ.Image;
@@ -188,7 +189,7 @@
 	 * Get any keywords that are on the end of the colour line. Must not step
 	 * over the new line boundary.
 	 */
-	private void readExtraColourInfo(TokenScanner scanner, ColourInfo colourInfo) {
+	private void readExtraColourInfo(TokenScanner scanner, AlphaAdder colour) {
 		while (!scanner.isEndOfFile()) {
 			Token tok = scanner.nextRawToken();
 			if (tok.isEol())
@@ -196,6 +197,7 @@
 
 			String word = tok.getValue();
 
+			// TypWiz uses alpha, TypViewer uses "canalalpha"
 			if (word.endsWith("alpha")) {
 				scanner.validateNext("=");
 				String aval = scanner.nextValue();
@@ -204,7 +206,7 @@
 					// Convert to rgba format
 					int alpha = Integer.decode(aval);
 					alpha = 255 - ((alpha<<4) + alpha);
-					colourInfo.addAlpha(alpha);
+					colour.addAlpha(alpha);
 				} catch (NumberFormatException e) {
 					throw new SyntaxException(scanner, "Bad number for alpha value " + aval);
 				}
@@ -263,34 +265,98 @@
 	}
 
 
+	/**
+	 * The true image format is represented by one colour value for each pixel in the
+	 * image.
+	 *
+	 * The colours are on several lines surrounded by double quotes.
+	 * <pre>
+	 * "#ff9900 #ffaa11 #feab10 #feab10"
+	 * "#f79900 #f7aa11 #feab10 #feab20"
+	 * ...
+	 * </pre>
+	 * There can be any number of colours on the same line, and the spaces are not needed.
+	 *
+	 * Transparency is represented by using RGBA values "#ffeeff00" or by appending alpha=N
+	 * to the end of the colour line. If using the 'alpha=N' method, then there can be only one
+	 * colour per line (well it is only the last colour value that is affected if more than one).
+	 *
+	 * <pre>
+	 * "#ff8801" alpha=2
+	 * </pre>
+	 *
+	 * The alpha values go from 0 to 15 where 0 is opaque and 15 transparent.
+	 */
 	private Image readTrueImage(TokenScanner scanner, ColourInfo colourInfo) {
 		int width = colourInfo.getWidth();
 		int height = colourInfo.getHeight();
-		int[] image = new int[width * height];
+		final int[] image = new int[width * height];
 
 		int nPixels = width * height;
 
 		int count = 0;
 		while (count < nPixels) {
 			scanner.validateNext("\"");
+			count = readTrueImageLine(scanner, image, count);
+		}
 
+		if (scanner.checkToken("\"")) {
+			// An extra colour, so this is probably meant to be a mode=16 image.
+			// Remove the first pixel and shuffle the rest down, unset the alpha
+			// on all the transparent pixels.
+			int transPixel = image[0];
+			for (int i = 1; i < nPixels; i++) {
+				int pix = image[i];
+				if (pix == transPixel)
+					pix &= ~0xff;
+				image[i-1] = pix;
+			}
+
+			// Add the final pixel
+			scanner.validateNext("\"");
+			readTrueImageLine(scanner, image, nPixels-1);
+		}
+
+		return new TrueImage(colourInfo, image);
+	}
+
+	/**
+	 * Read a single line of pixel colours.
+	 *
+	 * There can be one or more colours on the line and the colours are surrounded
+	 * by quotes.  The can be trailing attribute that sets the opacity of
+	 * the final pixel.
+	 */
+	private int readTrueImageLine(TokenScanner scanner, final int[] image, int count) {
-			do {
-				scanner.validateNext("#");
-				String col = scanner.nextValue();
-				try {
+		do {
+			scanner.validateNext("#");
+			String col = scanner.nextValue();
+			try {
-					int val = Integer.parseInt(col, 16);
+				int val = (int) Long.parseLong(col, 16);
-					if (col.length() <= 6)
-						val = (val << 8) + 0xff;
+				if (col.length() <= 6)
+					val = (val << 8) + 0xff;
 
-					image[count++] = val;
-				} catch (NumberFormatException e) {
+				image[count++] = val;
+			} catch (NumberFormatException e) {
-					throw new SyntaxException(scanner, "Not a valid colour value " + col);
+				throw new SyntaxException(scanner, "Not a valid colour value ");
-				}
-			} while (scanner.checkToken("#"));
-			scanner.validateNext("\"");
+			}
+		} while (scanner.checkToken("#"));
+		scanner.validateNext("\"");
+
+		// Look for any trailing alpha=N stuff.
+		final int lastColourIndex = count - 1;
+		readExtraColourInfo(scanner, new AlphaAdder() {
+			/**
+			 * Add the alpha value to the last colour that was read in.
+			 *
+			 * @param alpha A true alpha value ie 0 is transparent, 255 opaque.
+			 */
+			public void addAlpha(int alpha) {
+				image[lastColourIndex] = (image[lastColourIndex] & ~0xff) | (alpha & 0xff);
-		}
+			}
+		});
 
-		return new TrueImage(colourInfo, image);
+		return count;
 	}
 
 	/**
Index: src/uk/me/parabola/imgfmt/app/typ/ColourInfo.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/uk/me/parabola/imgfmt/app/typ/ColourInfo.java	(revision 2186)
+++ src/uk/me/parabola/imgfmt/app/typ/ColourInfo.java	(revision )
@@ -28,7 +28,7 @@
  *
  * @author Steve Ratcliffe
  */
-public class ColourInfo implements Writeable {
+public class ColourInfo implements Writeable, AlphaAdder {
 	private static final int S_NIGHT = 1;
 	private static final int S_DAY_TRANSPARENT = 0x2;
 	private static final int S_NIGHT_TRANSPARENT = 0x4;
@@ -170,7 +170,7 @@
 	 * @param alpha The original alpha value eg 0xf0.
 	 * @return Rounded alpha to four bits eg 0xe.
 	 */
-	private int alphaRound4(int alpha) {
+	static int alphaRound4(int alpha) {
 		int top = (alpha >> 4) & 0xf;
 		int low = alpha & 0xf;
 
@@ -234,6 +234,10 @@
 
 	public int getColourMode() {
 		return colourMode;
+	}
+
+	public void setColourMode(int colourMode) {
+		this.colourMode = (char) colourMode;
 	}
 
 	public void setSimple(boolean simple) {
[_id]
FID=12
ProductCode=1
CodePage=1252
[End]

[_drawOrder]
[end]

[_point] 
Type=0x06000
String1=0x00,Mode 0 - compact
DayXpm="4 4 0 1"
"#FFF5FF#FFF5FE#FF65FF#FF65FF"
"#FF65FF#FF65FF#FF65FF#FF65FF"
"#FFF58F#FFF58F#FFF58F#FFF58F"
"#FFF5FF#FFF5FF#FFF5FF#FFF5FF"
[end]

; Both the following mode 32 points are the same, just written
; different ways.
[_point] 
Type=0x06001
String1=0x00,A mode 32 point
DayXpm="4 4 0 1"
"#cc0000 #cc0000 #cc0000 #cc000000"
"#cc000011 #00000033 #00000066 #00000099"
"#000000bb #000000dd #000000 #000000"
"#000000 #000000 #000000 #000000"
; The spaces are not required above
[end]

[_point] 
Type=0x06002
String1=0x00,Alternate representation of mode32
DayXpm="4 4 0 1"
"#cc0000" alpha=0
"#cc0000" alpha=0
"#cc0000" alpha=0
"#cc0000" alpha=15
"#cc0000" alpha=14
"#000000" alpha=12
"#000000" alpha=9
"#000000" alpha=6
"#000000" alpha=4
"#000000" alpha=2
"#000000" alpha=0
"#000000" alpha=0
"#000000" alpha=0
"#000000" alpha=0
"#000000" alpha=0
"#000000" alpha=0
[end]

; The follow 3 mode 16 points are different ways of writing the same
; thing
[_point] 
Type=0x06002
String1=0x00,Mode 16
DayXpm="4 4 0 1"
"#880000"
"#880000"
"#880000"
"#cc0000" alpha=15
"#cd0000" alpha=15
"#000000" alpha=15
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
[end]

[_point] 
Type=0x06003
String1=0x00,Mode 16
DayXpm="4 4 0 1"
"#880000"
"#880000"
"#880000"
"#cc000000"
"#cd000000"
"#00000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
[end]

[_point] 
; transparent colour is first as an extra value in the colour list
Type=0x06004
ExtendedLabels=N
String1=0x00,Alt mode 16
DayXpm="4 4 0 1"
"#cc0000"
"#880000"
"#880000"
"#880000"
"#cc0000"
"#cc0000"
"#cc0000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
"#000000"
[end]
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to