1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
package cgeo.geocaching.connector.gc;
import cgeo.geocaching.cgCache;
import cgeo.geocaching.enumerations.CacheType;
import android.graphics.Bitmap;
/**
* icon decoder for cache icons
*
*/
public abstract class IconDecoder {
public static void parseMapPNG(final cgCache cache, Bitmap bitmap, UTFGridPosition xy, int zoomlevel) {
if (zoomlevel >= 14) {
parseMapPNG14(cache, bitmap, xy);
} else {
parseMapPNG13(cache, bitmap, xy);
}
}
private static final int[] OFFSET_X = new int[] { 0, -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, -2, -2, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2 };
private static final int[] OFFSET_Y = new int[] { 0, 0, 1, 1, 1, 0, -1, -1, -1, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2, -2, -2, -2 };
/**
* The icon decoder walks a spiral around the center pixel position of the cache
* and searches for characteristic colors.
*
* @param cache
* @param bitmap
* @param xy
*/
private static void parseMapPNG13(final cgCache cache, Bitmap bitmap, UTFGridPosition xy) {
final int xCenter = xy.getX() * 4 + 2;
final int yCenter = xy.getY() * 4 + 2;
final int bitmapWidth = bitmap.getWidth();
final int bitmapHeight = bitmap.getHeight();
int countMulti = 0;
int countFound = 0;
for (int i = 0; i < OFFSET_X.length; i++) {
// assert that we are still in the tile
final int x = xCenter + OFFSET_X[i];
if (x < 0 || x >= bitmapWidth) {
continue;
}
final int y = yCenter + OFFSET_Y[i];
if (y < 0 || y >= bitmapHeight) {
continue;
}
int color = bitmap.getPixel(x, y) & 0x00FFFFFF;
// transparent pixels are not interesting
if (color == 0) {
continue;
}
int red = (color & 0xFF0000) >> 16;
int green = (color & 0xFF00) >> 8;
int blue = color & 0xFF;
// these are quite sure, so one pixel is enough for matching
if (green > 0x80 && green > red && green > blue) {
cache.setType(CacheType.TRADITIONAL);
return;
}
if (blue > 0x80 && blue > red && blue > green) {
cache.setType(CacheType.MYSTERY);
return;
}
if (red > 0x90 && blue < 0x10 && green < 0x10) {
cache.setType(CacheType.EVENT);
return;
}
// next two are hard to distinguish, therefore we sample all pixels of the spiral
if (red > 0xFA && green > 0xD0) {
countMulti++;
}
if (red < 0xF3 && red > 0xa0 && green > 0x20 && blue < 0x80) {
countFound++;
}
}
// now check whether we are sure about found/multi
if (countFound > countMulti && countFound >= 2) {
cache.setFound(true);
}
if (countMulti > countFound && countMulti >= 5) {
cache.setType(CacheType.MULTI);
}
}
// Pixel colors in tile
private final static int COLOR_BORDER_GRAY = 0x5F5F5F;
private final static int COLOR_TRADITIONAL = 0x316013;
private final static int COLOR_MYSTERY = 0x243C97;
private final static int COLOR_MULTI = 0xFFDE19;
private final static int COLOR_FOUND = 0xFBEA5D;
// Offset inside cache icon
private final static int POSX_TRADI = 7;
private final static int POSY_TRADI = -12;
private final static int POSX_MULTI = 5; // for orange 8
private final static int POSY_MULTI = -9; // for orange 10
private final static int POSX_MYSTERY = 5;
private final static int POSY_MYSTERY = -13;
private final static int POSX_FOUND = 10;
private final static int POSY_FOUND = -8;
/**
* For level 14 find the borders of the icons and then use a single pixel and color to match.
*
* @param cache
* @param bitmap
* @param xy
*/
private static void parseMapPNG14(cgCache cache, Bitmap bitmap, UTFGridPosition xy) {
int x = xy.getX() * 4 + 2;
int y = xy.getY() * 4 + 2;
// search for left border
int countX = 0;
while ((bitmap.getPixel(x, y) & 0x00FFFFFF) != COLOR_BORDER_GRAY) {
if (--x < 0 || ++countX > 20) {
return;
}
}
// search for bottom border
int countY = 0;
while ((bitmap.getPixel(x, y) & 0x00FFFFFF) != 0x000000) {
if (++y >= Tile.TILE_SIZE || ++countY > 20) {
return;
}
}
try {
if ((bitmap.getPixel(x + POSX_TRADI, y + POSY_TRADI) & 0x00FFFFFF) == COLOR_TRADITIONAL) {
cache.setType(CacheType.TRADITIONAL);
return;
}
if ((bitmap.getPixel(x + POSX_MYSTERY, y + POSY_MYSTERY) & 0x00FFFFFF) == COLOR_MYSTERY) {
cache.setType(CacheType.MYSTERY);
return;
}
if ((bitmap.getPixel(x + POSX_MULTI, y + POSY_MULTI) & 0x00FFFFFF) == COLOR_MULTI) {
cache.setType(CacheType.MULTI);
return;
}
if ((bitmap.getPixel(x + POSX_FOUND, y + POSY_FOUND) & 0x00FFFFFF) == COLOR_FOUND) {
cache.setFound(true);
}
} catch (IllegalArgumentException e) {
// intentionally left blank
}
}
}
|