diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 000000000..bed56d701 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,3 @@ +Release +Debug +build diff --git a/tools/.project b/tools/.project new file mode 100644 index 000000000..d48fc1949 --- /dev/null +++ b/tools/.project @@ -0,0 +1,11 @@ + + + tools + + + + + + + + diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 000000000..23e09fb98 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 2.6) + +project (Tools) +find_package(PNG REQUIRED) + + + +add_definitions(-g) +add_definitions(-O2) +add_definitions(-Wall) +add_definitions(-DSDL) +add_definitions(-Wno-multichar) +add_definitions(-fno-strict-aliasing) +add_definitions(-fopenmp) +# add_definitions(-fstrict-aliasing) + +include_directories(../native) +include_directories(../native/base) +include_directories(../native/math/lin) +include_directories(../native/image) +include_directories(../native/ext/libzip) +include_directories(../native/ext/etcpack) +include_directories(/usr/local/include) +include_directories(${PNG_INCLUDE_DIR}) + +link_directories(/usr/X11R6/lib) +link_directories(/opt/local/lib) + +# Horrible horrible hack +include_directories(/usr/include/freetype2) +include_directories(/usr/local/include/freetype2) +include_directories(/opt/local/include/freetype2) + +add_subdirectory(../native/base base) +add_subdirectory(../native/gfx gfx) +add_subdirectory(../native/file file) +add_subdirectory(../native/image image) +add_subdirectory(../native/math math) +add_subdirectory(../native/ext/libzip libzip) +add_subdirectory(../native/ext/etcpack etcpack) + + +add_executable(atlastool atlastool.cpp) +target_link_libraries(atlastool ${PNG_LIBRARY} freetype z image etcpack etcdec file zip gomp) + +add_executable(zimtool zimtool.cpp) +target_link_libraries(zimtool ${PNG_LIBRARY} freetype z image etcpack etcdec file zip gomp) diff --git a/tools/README.txt b/tools/README.txt new file mode 100644 index 000000000..f22674b84 --- /dev/null +++ b/tools/README.txt @@ -0,0 +1,3 @@ +These are simple tools to create the ZIM texture format and texture atlases based on it. + +Documentation is TODO but they are easy to use. \ No newline at end of file diff --git a/tools/atlastool.cpp b/tools/atlastool.cpp new file mode 100644 index 000000000..12a7f580e --- /dev/null +++ b/tools/atlastool.cpp @@ -0,0 +1,643 @@ +// Sprite packing method borrowed from glorp engine and heavily modified. +// For license safety, just run this as a build tool, don't build it into your game/program. +// https://github.com/zorbathut/glorp + +// data we need to provide: +// sx, sy +// dx, dy +// ox, oy +// wx + +// line height +// dist-per-pixel + +#include +#include +#include FT_FREETYPE_H +#include +#include +#include +#include +#include + +#include "image/png_load.h" +#include "image/zim_save.h" + +#define CHECK(x) if (!(x)) { printf("%i: CHECK failed on this line\n", __LINE__); exit(1); } + +using namespace std; +static int global_id; +static bool highcolor = false; + +enum Effect { + FX_COPY = 0, + FX_RED_TO_ALPHA_SOLID_WHITE = 1, // for alpha fonts + FX_RED_TO_INTENSITY_ALPHA_255 = 2, + FX_PREMULTIPLY_ALPHA = 3, + FX_PINK_TO_ALPHA = 4, // for alpha fonts + FX_INVALID = 5, +}; + +const char *effect_str[5] = { + "copy", "r2a", "r2i", "pre", "p2a", +}; + +Effect GetEffect(const char *text) { + for (int i = 0; i < 5; i++) { + if (!strcmp(text, effect_str[i])) { + return (Effect)i; + } + } + return FX_INVALID; +} + +template +struct Image { + vector > dat; + void resize(int x, int y) { + dat.resize(y); + for(int i = 0; i < y; i++) + dat[i].resize(x); + } + int width() const { + return (int)dat[0].size(); + } + int height() const { + return (int)dat.size(); + } + void copyfrom(const Image &img, int ox, int oy, int effect) { + CHECK(img.dat[0].size() + ox <= dat[0].size()); + CHECK(img.dat.size() + oy <= dat.size()); + for (int y = 0; y < (int)img.dat.size(); y++) { + for (int x = 0; x < (int)img.dat[y].size(); x++) { + switch (effect) { + case FX_COPY: + dat[y + oy][ox + x] = img.dat[y][x]; + break; + case FX_RED_TO_ALPHA_SOLID_WHITE: + dat[y + oy][ox + x] = 0x00FFFFFF | (img.dat[y][x] << 24); + break; + case FX_RED_TO_INTENSITY_ALPHA_255: + dat[y + oy][ox + x] = 0xFF000000 | img.dat[y][x] | (img.dat[y][x] << 8) | (img.dat[y][x] << 16); + break; + case FX_PREMULTIPLY_ALPHA: + { + unsigned int color = img.dat[y][x]; + unsigned int a = color >> 24; + unsigned int r = (color & 0xFF) * a >> 8, g = (color & 0xFF00) * a>> 8, b = (color & 0xFF0000) * a >> 8; + color = (color & 0xFF000000) | (r & 0xFF) | (g & 0xFF00) | (b & 0xFF0000); + // Simulate 4444 + color = color & 0xF0F0F0F0; + color |= color >> 4; + dat[y + oy][ox + x] = color; + break; + } + case FX_PINK_TO_ALPHA: + dat[y + oy][ox + x] = ((img.dat[y][x]&0xFFFFFF) == 0xFF00FF) ? 0x00FFFFFF : (img.dat[y][x] | 0xFF000000); + break; + default: + dat[y + oy][ox + x] = 0xFFFF00FF; + break; + } + } + } + } + void set(int sx, int sy, int ex, int ey, unsigned char fil) { + for(int y = sy; y < ey; y++) + fill(dat[y].begin() + sx, dat[y].begin() + ex, fil); + } + bool LoadPNG(const char *png_name) { + unsigned char *img_data; + int w, h; + if (1 != pngLoad(png_name, &w, &h, &img_data, false)) { + printf("Failed to load %s\n", png_name); + exit(1); + return false; + } + dat.resize(h); + for (int y = 0; y < h; y++) { + dat[y].resize(w); + memcpy(&dat[y][0], img_data + 4 * y * w, 4 * w); + } + free(img_data); + return true; + } + void SavePNG(const char *png_name) { + // Save PNG + FILE *fil = fopen(png_name, "wb"); + png_structp png_ptr; + png_infop info_ptr; + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + CHECK(png_ptr); + info_ptr = png_create_info_struct(png_ptr); + CHECK(info_ptr); + png_init_io(png_ptr, fil); + //png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); + png_set_IHDR(png_ptr, info_ptr, dat[0].size(), dat.size(), 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_write_info(png_ptr, info_ptr); + for(int y = 0; y < (int)dat.size(); y++) { + png_write_row(png_ptr, (png_byte*)&dat[y][0]); + } + png_write_end(png_ptr, NULL); + png_destroy_write_struct(&png_ptr, &info_ptr); + } + void SaveZIM(const char *zim_name, int zim_format) { + uint8 *image_data = new uint8[width() * height() * 4]; + for (int y = 0; y < height(); y++) { + memcpy(image_data + y * width() * 4, &dat[y][0], width() * 4); + } + ::SaveZIM(zim_name, width(), height(), width() * 4, zim_format | ZIM_DITHER, image_data); + } +}; + +template +bool operator<(const Image &lhs, const Image &rhs) { + return lhs.dat.size() * lhs.dat[0].size() > rhs.dat.size() * rhs.dat[0].size(); +} + +struct Data { + // item ID + int id; + // dimensions of its spot in the world + int sx, sy, ex, ey; + // offset from the origin + float ox, oy; + // distance to move the origin forward + float wx; + + int effect; +}; + +bool operator<(const Data &lhs, const Data &rhs) { + return lhs.id < rhs.id; // should be unique +} + +string out_prefix; + +int NextPowerOf2(int x) { + int powof2 = 1; + // Double powof2 until >= val + while (powof2 < x) powof2 <<= 1; + return powof2; +} + +struct Bucket { + vector, Data> > items; + void AddItem(const Image &img, const Data &dat) { + items.push_back(make_pair(img, dat)); + } + vector Resolve(int image_width, Image &dest) { + // Place all the little images - whatever they are. + // Uses greedy fill algorithm. Slow but works surprisingly well, CPUs are fast. + Image masq; + masq.resize(image_width, 1); + dest.resize(image_width, 1); + sort(items.begin(), items.end()); + for(int i = 0; i < (int)items.size(); i++) { + int idx = items[i].first.dat[0].size(); + int idy = items[i].first.dat.size(); + CHECK(idx <= image_width); + bool found = false; + for(int ty = 0; ty < 2047 && !found; ty++) { + if(ty + idy + 1 > (int)dest.dat.size()) { + masq.resize(image_width, ty + idy + 1); + dest.resize(image_width, ty + idy + 1); + } + // Brute force packing. + for(int tx = 0; tx < image_width - (int)items[i].first.dat[0].size() && !found; tx++) { + bool valid = !(masq.dat[ty][tx] || masq.dat[ty + idy - 1][tx] || masq.dat[ty][tx + idx - 1] || masq.dat[ty + idy - 1][tx + idx - 1]); + if (valid) { + for(int ity = 0; ity < idy && valid; ity++) + for(int itx = 0; itx < idx && valid; itx++) + if(masq.dat[ty + ity][tx + itx]) { + valid = false; + } + } + if (valid) { + dest.copyfrom(items[i].first, tx, ty, items[i].second.effect); + masq.set(tx, ty, tx + idx + 1, ty + idy + 1, 255); + + items[i].second.sx = tx; + items[i].second.sy = ty; + + items[i].second.ex = tx + idx; + items[i].second.ey = ty + idy; + + found = true; + + // printf("Placed %d at %dx%d-%dx%d\n", items[i].second.id, tx, ty, tx + idx, ty + idy); + } + } + } + } + + if ((int)dest.dat.size() > image_width) { + printf("PACKING FAIL : height=%i", (int)dest.dat.size()); + exit(1); + } + dest.resize(image_width, NextPowerOf2(dest.dat.size())); + + // Output the glyph data. + vector dats; + for(int i = 0; i < (int)items.size(); i++) + dats.push_back(items[i].second); + return dats; + } +}; + +const int supersample = 16; +const int distmult = 64 * 3; // this is "one pixel in the final version equals 64 difference". reduce this number to increase the "blur" radius, increase it to make things "sharper" +const int maxsearch = (128 * supersample + distmult - 1) / distmult; + +struct Closest { + FT_Bitmap bmp; + Closest(FT_Bitmap bmp) : bmp(bmp) { } + float find_closest(int x, int y, char search) { + int best = 1 << 30; + for(int i = 1; i <= maxsearch; i++) { + if(i * i >= best) + break; + for(int f = -i; f < i; f++) { + int dist = i * i + f * f; + if(dist >= best) continue; + if(safe_access(x + i, y + f) == search || safe_access(x - f, y + i) == search || safe_access(x - i, y - f) == search || safe_access(x + f, y - i) == search) + best = dist; + } + } + return sqrt((float)best); + } + char safe_access(int x, int y) { + if(x < 0 || y < 0 || x >= bmp.width || y >= bmp.rows) + return 0; + return bmp.buffer[x + y * bmp.width]; + } +}; + +void RasterizeFont(const char *fontfile, int fontsize, float *metrics_height, Bucket *bucket) { + FT_Library freetype; + CHECK(FT_Init_FreeType(&freetype) == 0); + + FT_Face font; + CHECK(FT_New_Face(freetype, fontfile, 0, &font) == 0); + + printf("%d glyphs, %08x flags, %d units, %d strikes\n", (int)font->num_glyphs, (int)font->face_flags, (int)font->units_per_EM, (int)font->num_fixed_sizes); + + CHECK(FT_Set_Pixel_Sizes(font, 0, fontsize * supersample) == 0); + + // Character range. TODO: Make definable. We might want unicode + // Convert all characters to bitmaps. + for(int kar = 32; kar < 128; kar++) { + Image img; + if (0 != FT_Load_Char(font, kar, FT_LOAD_RENDER|FT_LOAD_MONOCHROME)) { + img.resize(1,1); + Data dat; + + dat.id = global_id++; + + dat.sx = 0; + dat.sy = 0; + dat.ex = 0; + dat.ey = 0; + dat.ox = 0; + dat.oy = 0; + dat.wx = 0; + dat.effect = FX_RED_TO_ALPHA_SOLID_WHITE; + bucket->AddItem(img, dat); + continue; + } + + // printf("%dx%d %p\n", font->glyph->bitmap.width, font->glyph->bitmap.rows, font->glyph->bitmap.buffer); + const int bord = (128 + distmult - 1) / distmult + 1; + if(font->glyph->bitmap.buffer) { + FT_Bitmap tempbitmap; + FT_Bitmap_New(&tempbitmap); + FT_Bitmap_Convert(freetype, &font->glyph->bitmap, &tempbitmap, 1); + Closest closest(tempbitmap); + + // No resampling, just sets the size of the image. + img.resize((tempbitmap.width + supersample - 1) / supersample + bord * 2, (tempbitmap.rows + supersample - 1) / supersample + bord * 2); + int lmx = img.dat[0].size(); + int lmy = img.dat.size(); + + // AA by finding distance to character. Probably a fairly decent approximation but why not do it right? + for(int y = 0; y < lmy; y++) { + int cty = (y - bord) * supersample + supersample / 2; + for(int x = 0; x < lmx; x++) { + int ctx = (x - bord) * supersample + supersample / 2; + float dist; + if(closest.safe_access(ctx, cty)) { + dist = closest.find_closest(ctx, cty, 0); + } else { + dist = -closest.find_closest(ctx, cty, 1); + } + dist = dist / supersample * distmult + 127.5; + dist = floor(dist + 0.5); + if(dist < 0) dist = 0; + if(dist > 255) dist = 255; + + // Only set the red channel. We process when adding the image. + img.dat[y][x] = (unsigned char)dist; + } + } + FT_Bitmap_Done(freetype, &tempbitmap); + } else { + img.resize(1, 1); + } + + Data dat; + + dat.id = global_id++; + + dat.sx = 0; + dat.sy = 0; + dat.ex = img.dat[0].size(); + dat.ey = img.dat.size(); + dat.ox = (float)font->glyph->metrics.horiBearingX / 64 / supersample - bord; + dat.oy = -(float)font->glyph->metrics.horiBearingY / 64 / supersample - bord; + dat.wx = (float)font->glyph->metrics.horiAdvance / 64 / supersample; + + dat.effect = FX_RED_TO_ALPHA_SOLID_WHITE; + bucket->AddItem(img, dat); + } + + *metrics_height = font->size->metrics.height; + FT_Done_FreeType(freetype); +} + + +bool LoadImage(const char *imagefile, Effect effect, Bucket *bucket) { + Image img; + + bool success = false; + if (!strcmp(imagefile, "white.png")) { + img.dat.resize(16); + for (int i = 0; i < 16; i++) { + img.dat[i].resize(16); + for (int j = 0; j < 16; j++) { + img.dat[i][j] = 0xFFFFFFFF; + } + } + success = true; + } else { + success = img.LoadPNG(imagefile); + printf("loaded image: %ix%i\n", (int)img.dat[0].size(), (int)img.dat.size()); + } + if (!success) { + return false; + } + + Data dat; + memset(&dat, 0, sizeof(dat)); + dat.id = global_id++; + dat.sx = 0; + dat.sy = 0; + dat.ex = img.dat[0].size(); + dat.ey = img.dat.size(); + dat.effect = effect; + bucket->AddItem(img, dat); + return true; +} + +// Use the result array, and recorded data, to generate C++ tables for everything. +struct FontDesc { + string name; + + int first_char_id; + int last_char_id; + + float ascend; + float descend; + float height; + + float metrics_height; + + void ComputeHeight(const vector &results, float distmult) { + ascend = 0; + descend = 0; + for(int i = first_char_id; i < last_char_id; i++) { + ascend = max(ascend, -results[i].oy); + descend = max(descend, results[i].ey - results[i].sy + results[i].oy); + } + + height = metrics_height / 64.0 / supersample; + } + + void OutputSelf(FILE *fil, float tw, float th, const vector &results) { + fprintf(fil, "const AtlasFont font_%s = {\n", name.c_str()); + fprintf(fil, " %ff, // padding\n", height - ascend - descend); + fprintf(fil, " %ff, // height\n", ascend + descend); + fprintf(fil, " %ff, // ascend\n", ascend); + fprintf(fil, " %ff, // distslope\n", distmult / 256.0); + fprintf(fil, " {\n"); + CHECK(last_char_id - first_char_id == 96); + for(int i = first_char_id; i < last_char_id; i++) { + fprintf(fil, " {%ff, %ff, %ff, %ff, %1.4ff, %1.4ff, %1.4ff, %i, %i}, // %i\n", + /*results[i].id, */ + results[i].sx / tw, + results[i].sy / th, + results[i].ex / tw, + results[i].ey / th, + results[i].ox, + results[i].oy, + results[i].wx, + results[i].ex - results[i].sx, results[i].ey - results[i].sy, (i - first_char_id + 32)); + } + fprintf(fil, " },\n"); + fprintf(fil, "};\n"); + } + + void OutputIndex(FILE *fil) { + fprintf(fil, " &font_%s,\n", name.c_str()); + } + + void OutputHeader(FILE *fil, int index) { + fprintf(fil, "#define %s %i\n", name.c_str(), index); + } +}; + +struct ImageDesc { + string name; + Effect effect; + int result_index; + + void OutputSelf(FILE *fil, float tw, float th, const vector &results) { + int i = result_index; + float toffx = 0.5f / tw; + float toffy = 0.5f / th; + fprintf(fil, " {%ff, %ff, %ff, %ff, %d, %d},\n", + results[i].sx / tw + toffx, + results[i].sy / th + toffy, + results[i].ex / tw - toffx, + results[i].ey / th - toffy, + results[i].ex - results[i].sx, + results[i].ey - results[i].sy); + } + + void OutputHeader(FILE *fil, int index) { + fprintf(fil, "#define %s %i\n", name.c_str(), index); + } +}; + +int main(int argc, char **argv) { + // initProgram(&argc, const_cast(&argv)); + // /usr/share/fonts/truetype/msttcorefonts/Arial_Black.ttf + // /usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-R.ttf + + CHECK(argc >= 3); + if (argc > 3) + { + highcolor = true; + printf("RGBA8888 enabled!\n"); + } + printf("Reading script %s\n", argv[1]); + const char *atlas_name = argv[2]; + string image_name = string(atlas_name) + "_atlas.zim"; + out_prefix = argv[2]; + + vector fonts; + vector images; + + Bucket bucket; + + char line[512]; + FILE *script = fopen(argv[1], "r"); + if (!fgets(line, 512, script)) { + printf("Error fgets-ing\n"); + } + int image_width; + sscanf(line, "%i", &image_width); + printf("Texture width: %i\n", image_width); + while (!feof(script)) { + if (!fgets(line, 511, script)) break; + if (!strlen(line)) break; + char *rest = strchr(line, ' '); + if (rest) + { + *rest = 0; + rest++; + } + char *word = line; + if (!strcmp(word, "font")) { + // Font! + char fontname[256]; + char fontfile[256]; + int pixheight; + sscanf(rest, "%s %s %i", fontname, fontfile, &pixheight); + printf("Font: %s (%s) in size %i\n", fontname, fontfile, pixheight); + + FontDesc fnt; + fnt.first_char_id = (int)bucket.items.size(); + float metrics_height; + RasterizeFont(fontfile, pixheight, &metrics_height, &bucket); + fnt.name = fontname; + fnt.last_char_id = (int)bucket.items.size(); + CHECK(fnt.last_char_id - fnt.first_char_id == 96); + + fnt.metrics_height = metrics_height; + fonts.push_back(fnt); + } else if (!strcmp(word, "image")) { + char imagename[256]; + char imagefile[256]; + char effectname[256]; + sscanf(rest, "%s %s %s", imagename, imagefile, effectname); + Effect effect = GetEffect(effectname); + printf("Image %s with effect %s (%i)\n", imagefile, effectname, (int)effect); + ImageDesc desc; + desc.name = imagename; + desc.effect = effect; + desc.result_index = (int)bucket.items.size(); + images.push_back(desc); + if (!LoadImage(imagefile, effect, &bucket)) { + fprintf(stderr, "Failed to load image %s\n", imagefile); + } + } else { + fprintf(stderr, "Warning: Failed to parse line starting with %s\n", line); + } + } + fclose(script); + // Script read, all subimages have been generated. + + // Place the subimages onto the main texture. Also writes to png. + Image dest; + // Place things on the bitmap. + printf("Resolving...\n"); + + vector results = bucket.Resolve(image_width, dest); + if (highcolor) { + printf("Writing .ZIM %ix%i RGBA8888...\n", dest.width(), dest.height()); + dest.SaveZIM(image_name.c_str(), ZIM_RGBA8888); + } else { + printf("Writing .ZIM %ix%i RGBA4444...\n", dest.width(), dest.height()); + dest.SaveZIM(image_name.c_str(), ZIM_RGBA4444); + } + // Also save PNG for debugging. + printf("Writing .PNG %s\n", (image_name + ".png").c_str()); + dest.SavePNG((image_name + ".png").c_str()); + + printf("Done. Outputting source files %s_atlas.cpp/h.\n", out_prefix.c_str()); + // Sort items by ID. + sort(results.begin(), results.end()); + + FILE *cpp_fil = fopen((out_prefix + "_atlas.cpp").c_str(), "wb"); + fprintf(cpp_fil, "// C++ generated by atlastool from %s (hrydgard@gmail.com)\n\n", argv[1]); + fprintf(cpp_fil, "#include \"%s\"\n\n", (out_prefix + "_atlas.h").c_str()); + for (int i = 0; i < (int)fonts.size(); i++) { + FontDesc &xfont = fonts[i]; + xfont.ComputeHeight(results, distmult); + xfont.OutputSelf(cpp_fil, dest.width(), dest.height(), results); + } + + if (fonts.size()) { + fprintf(cpp_fil, "const AtlasFont *%s_fonts[%i] = {\n", atlas_name, (int)fonts.size()); + for (int i = 0; i < (int)fonts.size(); i++) { + fonts[i].OutputIndex(cpp_fil); + } + fprintf(cpp_fil, "};\n"); + } + + if (images.size()) { + fprintf(cpp_fil, "const AtlasImage %s_images[%i] = {\n", atlas_name, (int)images.size()); + for (int i = 0; i < (int)images.size(); i++) { + images[i].OutputSelf(cpp_fil, dest.width(), dest.height(), results); + } + fprintf(cpp_fil, "};\n"); + } + + fprintf(cpp_fil, "const Atlas %s_atlas = {\n", atlas_name); + fprintf(cpp_fil, " \"%s\",\n", image_name.c_str()); + if (fonts.size()) { + fprintf(cpp_fil, " %s_fonts, %i,\n", atlas_name, (int)fonts.size()); + } else { + fprintf(cpp_fil, " 0, 0,\n"); + } + if (images.size()) { + fprintf(cpp_fil, " %s_images, %i,\n", atlas_name, (int)images.size()); + } else { + fprintf(cpp_fil, " 0, 0,\n"); + } + fprintf(cpp_fil, "};\n"); + // Should output a list pointing to all the fonts as well. + fclose(cpp_fil); + FILE *h_fil = fopen((out_prefix + "_atlas.h").c_str(), "wb"); + fprintf(h_fil, "// Header generated by atlastool from %s (hrydgard@gmail.com)\n\n", argv[1]); + fprintf(h_fil, "#pragma once\n"); + fprintf(h_fil, "#include \"gfx/texture_atlas.h\"\n\n"); + if (fonts.size()) { + fprintf(h_fil, "// FONTS_%s\n", atlas_name); + for (int i = 0; i < (int)fonts.size(); i++) { + fonts[i].OutputHeader(h_fil, i); + } + fprintf(h_fil, "\n\n"); + } + if (images.size()) { + fprintf(h_fil, "// IMAGES_%s\n", atlas_name); + for (int i = 0; i < (int)images.size(); i++) { + images[i].OutputHeader(h_fil, i); + } + fprintf(h_fil, "\n\n"); + } + fprintf(h_fil, "extern const Atlas %s_atlas;\n", atlas_name); + fprintf(h_fil, "extern const AtlasImage %s_images[%i];\n", atlas_name, (int)images.size()); + fclose(h_fil); + // TODO: Turn into C++ arrays. +} diff --git a/tools/atlastool/atlastool.vcxproj b/tools/atlastool/atlastool.vcxproj new file mode 100644 index 000000000..ed62624e8 --- /dev/null +++ b/tools/atlastool/atlastool.vcxproj @@ -0,0 +1,104 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0FDAD650-7436-4803-A36B-10CF9F6F9C2B} + Win32Proj + atlastool + + + + Application + true + MultiByte + + + Application + false + false + MultiByte + + + + + + + + + + + + + true + D:\dev\LIB;C:\DXSDK\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; + D:\dev\LIB;C:\DXSDK\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib + + + false + D:\dev\LIB;C:\DXSDK\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; + D:\dev\LIB;C:\DXSDK\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib + $(SolutionDir)..\..\tools\build\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..;..\..\SDL\include;..\..\..\libpng;%(AdditionalIncludeDirectories); + + + Console + true + freetype.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..;..\..\SDL\include;..\..\..\libpng;%(AdditionalIncludeDirectories); + + + Console + true + true + true + freetype.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;%(AdditionalDependencies) + + + + + + + + {2dbe9ec0-0b03-4b8f-8b8a-75aca6253807} + + + {f761046e-6c38-4428-a5f1-38391a37bb34} + + + {e8b58922-9827-493d-81e0-4b6e6bd77171} + + + + + + \ No newline at end of file diff --git a/tools/audiotool.cpp b/tools/audiotool.cpp new file mode 100644 index 000000000..68cfee7ae --- /dev/null +++ b/tools/audiotool.cpp @@ -0,0 +1,17 @@ +// TODO + +#include + +#include "audio/wav_load.h" +#include "base/logging.h" + + +struct AudioClip { + int length; + +}; + + +int main(int argc, char **argv) { + CHECK(argc == 3); +} diff --git a/tools/b.sh b/tools/b.sh new file mode 100644 index 000000000..015dc7e52 --- /dev/null +++ b/tools/b.sh @@ -0,0 +1,3 @@ +mkdir -p build +(cd build; cmake .. && make -j5; cd ..) +#cp build/atlastool /home/henrik/bin diff --git a/tools/zimtool.cpp b/tools/zimtool.cpp new file mode 100644 index 000000000..851f8a793 --- /dev/null +++ b/tools/zimtool.cpp @@ -0,0 +1,98 @@ +#include +#include +#include + +#include "base/logging.h" +#include "image/png_load.h" +#include "image/zim_save.h" + +char magic[5] = "ZIMG"; + +bool FLAGS_flip = false; + +const char *format_strings[4] = {"8888", "4444", "565", "ETC1"}; +int formats[4] = {ZIM_RGBA8888, ZIM_RGBA4444, ZIM_RGB565, ZIM_ETC1}; + +void printusage() { + fprintf(stderr, "Usage: zimtool infile.png outfile.zim [-f=FORMAT] [-m] [-g]\n"); + fprintf(stderr, "Formats: 8888 4444 565 ETC1\n"); +} + +int filesize(const char *filename) { + FILE *f = fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + int sz = ftell(f); + fclose(f); + return sz; +} + +int main(int argc, char **argv) { + // Parse command line arguments. + const char *FLAGS_infile; + const char *FLAGS_outfile; + if (argc >= 3) { + FLAGS_infile = argv[1]; + FLAGS_outfile = argv[2]; + } else { + fprintf(stderr, "ERROR: Not enough parameters.\n"); + printusage(); + return 1; + } + + int flags = 0; + bool format_set = false; + for (int i = 3; i < argc; i++) { + if (argv[i][0] != '-') { + fprintf(stderr, "Additional arguments must start with '-'\n"); + return 1; + } + switch (argv[i][1]) { + case 'm': + flags |= ZIM_HAS_MIPS; + // Generates mips directly here. We can generate gamma + // corrected mips, and mips for ETC1. + break; + case 'g': + flags |= ZIM_GEN_MIPS; + break; + case 'c': + flags |= ZIM_CLAMP; + break; + case 'f': + { + for (int j = 0; j < 4; j++) { + if (!strcmp(format_strings[j], argv[i] + 3)) { + flags |= j; + format_set = true; + } + } + } + break; + } + } + if ((flags & ZIM_FORMAT_MASK) == ZIM_ETC1) { + if (flags & ZIM_GEN_MIPS) { + fprintf(stderr, "WARNING: Cannot generate ETC1 mips at runtime\n"); + flags &= ~ZIM_GEN_MIPS; + } + } + if (!format_set) { + fprintf(stderr, "Must set format\n"); + printusage(); + return 1; + } + + uint8_t *image_data; + int width, height; + if (1 != pngLoad(FLAGS_infile, &width, &height, &image_data, FLAGS_flip)) { + fprintf(stderr, "Input not a PNG file\n"); + printusage(); + return 1; + } + + SaveZIM(FLAGS_outfile, width, height, width * 4, flags, image_data); + int in_file_size = filesize(FLAGS_infile); + int out_file_size = filesize(FLAGS_outfile); + fprintf(stdout, "Converted %s to %s. %i b to %i b. %ix%i, %s.\n", FLAGS_infile, FLAGS_outfile, in_file_size, out_file_size, width, height, format_strings[flags & ZIM_FORMAT_MASK]); + return 0; +} diff --git a/tools/zimtool/zimtool.vcxproj b/tools/zimtool/zimtool.vcxproj new file mode 100644 index 000000000..e82758ea9 --- /dev/null +++ b/tools/zimtool/zimtool.vcxproj @@ -0,0 +1,102 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {7055E1C3-0B42-40E4-80B0-8146A647FC5B} + Win32Proj + zimtool + + + + Application + true + MultiByte + + + Application + false + false + MultiByte + + + + + + + + + + + + + true + D:\dev\LIB;C:\DXSDK\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; + D:\dev\LIB;C:\DXSDK\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib + + + false + D:\dev\LIB;C:\DXSDK\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; + D:\dev\LIB;C:\DXSDK\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib + $(SolutionDir)..\..\tools\build\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..;..\..\..\glew;..\..\SDL\include;%(AdditionalIncludeDirectories); + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..;..\..\..\glew;..\..\SDL\include;%(AdditionalIncludeDirectories); + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;%(AdditionalDependencies) + + + + + + + + {2dbe9ec0-0b03-4b8f-8b8a-75aca6253807} + + + {f761046e-6c38-4428-a5f1-38391a37bb34} + + + {e8b58922-9827-493d-81e0-4b6e6bd77171} + + + + + + \ No newline at end of file diff --git a/tools/zimtool/zimtool.vcxproj.filters b/tools/zimtool/zimtool.vcxproj.filters new file mode 100644 index 000000000..1b836b2f4 --- /dev/null +++ b/tools/zimtool/zimtool.vcxproj.filters @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file