Encode cursor hotspots into GIF comments

This commit is contained in:
2015-08-21 14:27:39 -04:00
parent 95559b5a51
commit c0c3093140
27 changed files with 49 additions and 28 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 B

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 870 B

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 875 B

After

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 889 B

View File

@@ -0,0 +1,10 @@
When editing the GIF files in this folder, you need to be aware how the hotspot is stored.
Each of these GIF files encodes the cursor's hotspot into a GIF comment with the following
syntax (case-sensitive):
Hotspot(x,y)
where x and y are substituted with the actual integer coordinates. There should be no extra
spaces anywhere in that. (However, anything after the closing parenthesis will be ignored.)
If you edit any of these images, be sure to update the hotspot if necessary!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 B

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 870 B

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 863 B

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 868 B

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 880 B

After

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 859 B

After

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 880 B

After

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 B

After

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

After

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 860 B

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 875 B

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 B

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 863 B

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 887 B

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 883 B

After

Width:  |  Height:  |  Size: 899 B

View File

@@ -42,43 +42,54 @@ namespace ResMgr {
} }
/// Load a cursor from a GIF file. /// Load a cursor from a GIF file.
/// The cursor's hotspot location is stored in a .hot file, as plaintext: /// The cursor's hotspot location is stored in a GIF comment, with the following syntax (case-sensitive):
/// simply the X value followed by the Y value, separated by whitespace. /// "Hotspot(x,y)"
template<> inline CursorRsrc* resLoader<CursorRsrc>::operator() (std::string fname) { template<> inline CursorRsrc* resLoader<CursorRsrc>::operator() (std::string fname) {
// TODO: Store the hotspots on disk instead of hardcoded here
static const std::map<std::string,location> cursor_hs = {
{"wand", {4, 1}}, {"eyedropper", {1, 14}}, {"brush", {5, 13}}, {"spraycan", {8, 8}},
{"eraser", {8, 8}}, {"topleft", {8, 8}}, {"bottomright", {8, 8}}, {"hand", {14, 0}},
{"NW", {3, 3}}, {"N", {9, 3}}, {"NE", {12, 3}},
{"W", {2, 7}}, {"wait", {8, 8}}, {"E", {14, 7}},
{"SW", {3, 12}}, {"S", {9, 12}}, {"SE", {12, 12}},
{"sword", {1, 1}}, {"boot", {2, 6}}, {"drop", {14, 0}}, {"target", {8, 8}},
{"talk", {2, 13}}, {"key", {3, 2}}, {"look", {7, 6}}, {"watch", {8,8}},
};
fs::path fpath = resPool<CursorRsrc>::rel2abs(fname + ".gif"); fs::path fpath = resPool<CursorRsrc>::rel2abs(fname + ".gif");
fs::path hotpath = resPool<CursorRsrc>::rel2abs(fname + ".hot"); if(!fs::exists(fpath))
int x = 0, y = 0; throw xResMgrErr("Failed to load GIF cursor: " + fname);
if(fs::exists(hotpath)) { int x = 0, y = 0, f_sz;
std::ifstream fin(hotpath.c_str()); std::ifstream fin(fpath.c_str(), std::ios::binary);
fin >> x >> y; fin.seekg(0, std::ios::end);
fin.close(); f_sz = fin.tellg();
} else { fin.clear();
auto entry = cursor_hs.find(fname); fin.seekg(0, std::ios::beg);
if(entry == cursor_hs.end()) bool found_hotspot = false;
std::cerr << "Cursor hotspot missing: " << fname << std::endl; while(fin && !found_hotspot) {
else { unsigned char c = fin.get();
std::cerr << "Cursor hotspot missing (using fallback value): " << fname << std::endl; if(c != 0x21) continue;
location hs = entry->second; c = fin.get();
x = hs.x; y = hs.y; if(c != 0xfe) continue;
// If we get here, we've probably found a GIF comment
std::string str;
int count;
found_hotspot = true;
do {
count = fin.get();
if(count + fin.tellg() >= f_sz) {
found_hotspot = false;
break;
}
std::copy_n(std::istream_iterator<std::string::value_type>(fin), count, std::back_inserter(str));
} while(count > 0);
if(found_hotspot) {
if(str.substr(0,7) == "Hotspot") {
size_t open_paren = str.find_first_of('('), comma = str.find_first_of(','), close_paren = str.find_first_of(')');
std::string x_str = str.substr(open_paren + 1, comma - open_paren - 1);
std::string y_str = str.substr(comma + 1, close_paren - comma - 1);
x = std::stoi(x_str);
y = std::stoi(y_str);
} else found_hotspot = false;
} }
} }
if(!found_hotspot)
std::cerr << "Cursor hotspot missing: " << fname << std::endl;
// TODO: Handle errors? // TODO: Handle errors?
CursorRsrc* cur = new Cursor(fpath.string(),x,y); CursorRsrc* cur = new Cursor(fpath.string(),x,y);
return cur; return cur;
throw xResMgrErr("Failed to load GIF cursor: " + fname);
} }
/// Load a font form a TTF file. /// Load a font from a TTF file.
template<> inline FontRsrc* resLoader<FontRsrc>::operator() (std::string fname) { template<> inline FontRsrc* resLoader<FontRsrc>::operator() (std::string fname) {
fs::path fpath = resPool<FontRsrc>::rel2abs(fname + ".ttf"); fs::path fpath = resPool<FontRsrc>::rel2abs(fname + ".ttf");
FontRsrc* theFont = new FontRsrc; FontRsrc* theFont = new FontRsrc;