diff --git a/config.cpp b/config.cpp index 49c28823..a4ddc0d3 100644 --- a/config.cpp +++ b/config.cpp @@ -317,6 +317,14 @@ void initConfig() { addsaver(crystal::compass_probability, "compass-probability"); addsaver(crystal::view_coordinates, "crystal-coordinates"); + + addsaver(shot::shotx, "shotx"); + addsaver(shot::shoty, "shoty"); + addsaver(shot::make_svg, "shotsvg"); + addsaver(shot::transparent, "shottransparent"); + addsaver(shot::gamma, "shotgamma"); + addsaver(shot::caption, "shotcaption"); + addsaver(shot::fade, "shotfade"); #if CAP_TEXTURE addsaver(texture::texture_aura, "texture-aura", false); @@ -1861,7 +1869,9 @@ unordered_map params = { {"sang", conformal::spiral_angle}, {"spiralx", conformal::spiral_x}, {"spiraly", conformal::spiral_y}, - {"cprob", crystal::compass_probability} + {"cprob", crystal::compass_probability}, + {"gamma", shot::gamma}, + {"fade", shot::fade} }; } diff --git a/control.cpp b/control.cpp index c9a8c70f..0f62f052 100644 --- a/control.cpp +++ b/control.cpp @@ -319,6 +319,10 @@ void handleKeyNormal(int sym, int uni) { sym = 0; } + #if ISMOBILE + if(uni == 'A' && !cheater) pushScreen(shot::menu); + #endif + if(DEFAULTNOR(sym)) handlePanning(sym, uni); #ifdef SCALETUNER diff --git a/hyper.h b/hyper.h index 055afecd..0304151e 100644 --- a/hyper.h +++ b/hyper.h @@ -964,7 +964,6 @@ void calcparam(); #if CAP_SDL color_t& qpixel(SDL_Surface *surf, int x, int y); void setvideomode(); -void saveHighQualityShot(const char *fname = NULL, const char *caption = NULL, int fade = 255); #endif #if CAP_CONFIG @@ -1862,13 +1861,22 @@ void clearMessages(); void resetGeometry(); +namespace shot { + extern int shotx, shoty, shotformat; + extern bool make_svg; + extern ld gamma; + void menu(); + void default_screenshot_content(); + void take(string fname, const function& what = default_screenshot_content); + } + namespace svg { void circle(int x, int y, int size, color_t col, color_t fillcolor); void polygon(int *polyx, int *polyy, int polyi, color_t col, color_t outline, double linewidth); void text(int x, int y, int size, const string& str, bool frame, color_t col, int align); extern bool in; extern string link; - void render(const char *fname = NULL, const function& what = drawfullmap); + void render(const string& fname, const function& what = shot::default_screenshot_content); } extern int sightrange_bonus, genrange_bonus, gamerange_bonus; @@ -2228,9 +2236,6 @@ namespace tour { extern bool doCross; void optimizeview(); -extern int pngres; -extern int pngformat; - extern bool noGUI; extern bool dronemode; diff --git a/hypgraph.cpp b/hypgraph.cpp index 4e927c7a..06c9a887 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -722,7 +722,6 @@ bool in_smart_range(const transmatrix& T) { ld x2 = current_display->radius * abs(h3[0] - h1[0]) / epsilon; ld y2 = current_display->radius * abs(h3[1] - h1[1]) * vid.stretch / epsilon; ld scale = sqrt(hypot(x1, y1) * hypot(x2, y2)) * scalefactor * hcrossf7; - if(svg::in) scale /= svg::divby; return scale > vid.smart_range_detail && x - 2 * max(x1, x2) < current_display->xtop + current_display->xsize && diff --git a/mapeditor.cpp b/mapeditor.cpp index 553f6335..0ce4bae0 100644 --- a/mapeditor.cpp +++ b/mapeditor.cpp @@ -460,11 +460,10 @@ namespace mapeditor { displayButton(8, vid.yres-8-fs*10, XLAT("F2 = save"), SDLK_F2, 0); displayButton(8, vid.yres-8-fs*9, XLAT("F3 = load"), SDLK_F3, 0); displayButton(8, vid.yres-8-fs*7, XLAT("F5 = restart"), SDLK_F5, 0); + #if CAP_SHOT displayButton(8, vid.yres-8-fs*6, XLAT("F6 = HQ shot"), SDLK_F6, 0); + #endif displayButton(8, vid.yres-8-fs*5, XLAT("F7 = player on/off"), SDLK_F7, 0); -#if CAP_SVG - displayButton(8, vid.yres-8-fs*4, XLAT("F8 = SVG shot"), SDLK_F8, 0); -#endif displayButton(8, vid.yres-8-fs*3, XLAT("SPACE = map/graphics"), ' ', 0); displayButton(8, vid.yres-8-fs*2, XLAT("ESC = return to the game"), SDLK_ESCAPE, 0); } @@ -644,30 +643,6 @@ namespace mapeditor { } } - #if CAP_SDL - void saveHighQualityShotX() { - static string hqfile = "hqshot.png"; - if(anyshiftclick) - saveHighQualityShot(); - else - dialog::openFileDialog(hqfile, XLAT("F6 = HQ shot"), ".png", [] () { - saveHighQualityShot(hqfile.c_str()); - return true; - }); - } - - void saveSvgShotX() { - static string hqfile = "svgshot.svg"; - if(anyshiftclick) - svg::render(); - else - dialog::openFileDialog(hqfile, XLAT("F8 = SVG shot"), ".svg", [] () { - svg::render(hqfile.c_str()); - return true; - }); - } - #endif - void editAt(cellwalker where, manual_celllister& cl) { if(painttype == 4 && radius) { @@ -836,14 +811,9 @@ namespace mapeditor { return false; } }); -#if CAP_SDL +#if CAP_SHOT else if(sym == SDLK_F6) { - saveHighQualityShotX(); - } -#endif -#if CAP_SVG - else if(sym == SDLK_F8) { - saveSvgShotX(); + pushScreen(shot::menu); } #endif else if(sym == SDLK_F7) { @@ -1486,18 +1456,12 @@ namespace mapeditor { drawplayer = !drawplayer; } -#if CAP_SDL - if(sym == SDLK_F6) { - saveHighQualityShotX(); +#if CAP_SHOT + else if(sym == SDLK_F6) { + pushScreen(shot::menu); } #endif -#if CAP_SVG - if(sym == SDLK_F8) { - saveSvgShotX(); - } -#endif - if(sym == SDLK_ESCAPE) popScreen(); if(sym == SDLK_F1) { diff --git a/rogueviz-video.cpp b/rogueviz-video.cpp index 30696abc..66728546 100644 --- a/rogueviz-video.cpp +++ b/rogueviz-video.cpp @@ -12,7 +12,6 @@ namespace rogueviz { void rvvideo(const string &fname) { if(kind == kCollatz) { - pngformat = 2; sightrange_bonus = 3; genrange_bonus = 3; dronemode = true; vid.camera_angle = -45; rog3 = true; patterns::whichShape = '8'; @@ -146,8 +145,10 @@ struct storydata { int s; int e; const char *text; } story[] = { if(i == 0) drawthemap(); shmup::turn(100); printf("%s\n", buf); - pngres = 1080; - saveHighQualityShot(buf, caption, fade); + shot::shoty = 1080; shot::shotx = 1920; + shot::caption = caption; + shot::fade = fade; + shot::take(buf); } return; @@ -162,7 +163,7 @@ struct storydata { int s; int e; const char *text; } story[] = { if(i == 0) drawthemap(); centerpc(100); printf("%s\n", buf); - saveHighQualityShot(buf); + shot::take(buf); } } diff --git a/rogueviz.cpp b/rogueviz.cpp index 1949c3a4..84343c21 100644 --- a/rogueviz.cpp +++ b/rogueviz.cpp @@ -1592,14 +1592,6 @@ bool turn(int delta) { // shmup::pc[0]->rebase(); } -void fixparam() { - if(!legend.empty() && !nohud) { - if((svg::in || inHighQual) && pngformat == 0) - vid.xres = vid.xres * 22/16; - current_display->xcenter = current_display->ycenter; - } - } - #ifndef CAP_RVSLIDES #define CAP_RVSLIDES (CAP_TOUR && !ISWEB) #endif @@ -2259,7 +2251,6 @@ auto hooks = #endif addHook(clearmemory, 0, close) + addHook(hooks_prestats, 100, rogueviz_hud) + - addHook(hooks_calcparam, 100, fixparam) + addHook(shmup::hooks_draw, 100, drawVertex) + addHook(shmup::hooks_describe, 100, describe_monster) + addHook(shmup::hooks_turn, 100, turn) + diff --git a/screenshot.cpp b/screenshot.cpp index 356b521d..61e9a155 100644 --- a/screenshot.cpp +++ b/screenshot.cpp @@ -23,11 +23,9 @@ namespace svg { bool invisible(color_t col) { return (col & 0xFF) == 0; } - ld gamma = .5; - void fixgamma(unsigned int& color) { unsigned char *c = (unsigned char*) (&color); - for(int i=1; i<4; i++) c[i] = 255 * pow(float(c[i] / 255.0), float(gamma)); + for(int i=1; i<4; i++) c[i] = 255 * pow(float(c[i] / 255.0), float(shot::gamma)); } int svgsize; @@ -154,37 +152,23 @@ namespace svg { fprintf(f, "\n"); } - void render(const char *fname, const function& what) { - - dynamicval v(vid, vid); + void render(const string& fname, const function& what) { dynamicval v2(in, true); - // dynamicval v5(ringcolor, 0x808080FF); + dynamicval v3(vid.usingGL, false); - vid.usingGL = false; - vid.xres = vid.yres = svgsize ? svgsize : vid.use_smart_range ? pngres*divby : min(1 << (get_sightrange()+7), 16384); - calcparam(); - dynamicval v6(inHighQual, true); - darken = 0; - - time_t timer; - timer = time(NULL); - - char buf[128]; strftime(buf, 128, "svgshot-%y%m%d-%H%M%S.svg", localtime(&timer)); - if(!fname) fname = buf; - - f = fopen(fname, "wt"); + f = fopen(fname.c_str(), "wt"); fprintf(f, "\n", coord(vid.xres), coord(vid.yres)); what(); fprintf(f, "\n"); fclose(f); - addMessage(XLAT("Saved the SVG shot to %1 (sightrange %2)", fname, its(get_sightrange()))); } - + #if CAP_COMMANDLINE int read_args() { using namespace arg; if(argis("-svgsize")) { - shift(); sscanf(argcs(), "%d/%d", &svg::svgsize, &svg::divby); + shift(); sscanf(argcs(), "%d/%d", &shot::shoty, &svg::divby); + if(shot::shotformat == -1) shot::shotformat = 0; } else if(argis("-svgfont")) { shift(); svg::font = args(); @@ -192,7 +176,7 @@ int read_args() { // (this is helpful with Inkscape's PDF+TeX output feature; define \myfont yourself) } else if(argis("-svggamma")) { - shift_arg_formula(svg::gamma); + shift_arg_formula(shot::gamma); } else if(argis("-svgshot")) { PHASE(3); shift(); start_game(); @@ -208,10 +192,6 @@ auto ah = addHook(hooks_args, 0, read_args); } #endif -#if CAP_SDL -int pngres = 2000; -int pngformat = 0; - #if CAP_PNG void IMAGESAVE(SDL_Surface *s, const char *fname) { SDL_Surface *s2 = SDL_PNGFormatAlpha(s); @@ -220,85 +200,143 @@ void IMAGESAVE(SDL_Surface *s, const char *fname) { } #endif -hookset *hooks_hqshot; +#if CAP_SHOT +namespace shot { -void saveHighQualityShot(const char *fname, const char *caption, int fade) { +purehookset hooks_hqshot; - resetbuffer rb; +int shotx = 2000, shoty = 2000; +bool make_svg = false; +bool transparent = true; +ld gamma = 1; +int shotformat = -1; +string caption; +ld fade = 1; - // int maxrange = getDistLimit() * 3/2; +void set_shotx() { + if(shotformat == -1) return; + shotx = shoty; + if(shotformat == 1) shotx = shotx * 4/3; + if(shotformat == 2) shotx = shotx * 16/9; + if(shotformat == 3) { + shotx = shotx * 22/16; + while(shotx & 15) shotx++; + } + } - // dynamicval v3(sightrange, (cheater && sightrange < maxrange) ? maxrange : sightrange); +#if CAP_SDL +int shot_aa = 1; +#endif + +void default_screenshot_content() { + #if CAP_RUG + if(rug::rugged) { + if(rug::in_crystal()) rug::physics(); + rug::drawRugScene(); + } + else + #endif + drawfullmap(); + + if(caption != "") + displayfr(vid.xres/2, vid.fsize+vid.fsize/4, 3, vid.fsize*2, caption, forecolor, 8); + callhooks(hooks_hqshot); + drawStats(); + } + +#if CAP_PNG +void postprocess(string fname, SDL_Surface *sdark, SDL_Surface *sbright) { + if(gamma == 1 && shot_aa == 1) { + IMAGESAVE(sdark, fname.c_str()); + return; + } + + SDL_Surface *sout = SDL_CreateRGBSurface(SDL_SWSURFACE,shotx,shoty,32,0xFF<<16,0xFF<<8,0xFF, (sdark == sbright) ? 0 : (0xFF<<24)); + for(int y=0; y 255) v = 255; + part(pix, p) = v; + } + } + IMAGESAVE(sout, fname.c_str()); + } +#endif + +void take(string fname, const function& what) { if(cheater) doOvergenerate(); - - time_t timer; - timer = time(NULL); + + int multiplier = make_svg ? svg::divby : shot_aa; dynamicval v(vid, vid); dynamicval v2(inHighQual, true); - dynamicval v6(auraNOGL, fname ? true : false); - - vid.xres = vid.yres = pngres; - if(pngformat == 1) vid.xres = vid.yres * 4/3; - if(pngformat == 2) vid.xres = vid.yres * 16/9; - if(pngformat == 3) { - vid.xres = vid.yres * 22/16; - while(vid.xres & 15) vid.xres++; - } - - // if(vid.pmodel == 0) vid.scale = 0.99; - calcparam(); - - renderbuffer glbuf(vid.xres, vid.yres, vid.usingGL); - glbuf.enable(); - current_display->set_viewport(0); - - // printf("format = %d, %d x %d\n", pngformat, vid.xres, vid.yres); - + dynamicval v6(auraNOGL, true); + vid.smart_range_detail *= multiplier; darken = 0; - int numi = (fname?1:2); - - for(int i=0; iset_viewport(0); + + dynamicval v8(backcolor, transparent ? 0xFF000000 : backcolor); + #if CAP_RUG + if(rug::rugged && !rug::renderonce) rug::prepareTexture(); + #endif + glbuf.clear(backcolor); + what(); + + SDL_Surface *sdark = glbuf.render(); + + if(transparent) { + renderbuffer glbuf1(vid.xres, vid.yres, vid.usingGL); + backcolor = 0xFFFFFFFF; + #if CAP_RUG + if(rug::rugged && !rug::renderonce) rug::prepareTexture(); + #endif + glbuf1.enable(); + glbuf1.clear(backcolor); + current_display->set_viewport(0); + what(); + + postprocess(fname, sdark, glbuf1.render()); + } + else postprocess(fname, sdark, sdark); + #endif + } } #if CAP_COMMANDLINE @@ -307,13 +345,14 @@ int png_read_args() { if(argis("-pngshot")) { PHASE(3); shift(); start_game(); printf("saving PNG screenshot to %s\n", argcs()); - saveHighQualityShot(argcs()); + make_svg = false; + shot::take(argcs()); } else if(argis("-pngsize")) { - shift(); pngres = argi(); + shift(); shoty = argi(); if(shotformat == -1) shotformat = 0; } else if(argis("-pngformat")) { - shift(); pngformat = argi(); + shift(); shotformat = argi(); } else return 1; return 0; @@ -322,6 +361,61 @@ int png_read_args() { auto ah_png = addHook(hooks_args, 0, png_read_args); #endif +void menu() { + cmode = sm::SIDE; + gamescreen(0); + if(!CAP_SVG) make_svg = false; + if(!CAP_PNG) make_svg = true; + dialog::init(XLAT("screenshots"), iinf[itPalace].color, 150, 100); + dialog::addSelItem(XLAT("format"), make_svg ? "SVG" : "PNG", 'f'); + dialog::add_action([] { make_svg = !make_svg; }); + dialog::addSelItem(XLAT("pixels (X)"), its(shotx), 'x'); + dialog::add_action([] { shotformat = -1; dialog::editNumber(shotx, 500, 8000, 100, 2000, XLAT("pixels (X)"), ""); }); + dialog::addSelItem(XLAT("pixels (Y)"), its(shoty), 'y'); + dialog::add_action([] { shotformat = -1; dialog::editNumber(shoty, 500, 8000, 100, 2000, XLAT("pixels (Y)"), ""); }); + if(make_svg) { + using namespace svg; + dialog::addSelItem(XLAT("precision"), "1/"+its(divby), 'p'); + dialog::add_action([] { divby *= 10; if(divby > 1000000) divby = 1; }); + } + else { + dialog::addSelItem(XLAT("supersampling"), its(shot_aa), 's'); + dialog::add_action([] { shot_aa *= 2; if(shot_aa > 16) shot_aa = 1; }); + } + dialog::addBoolItem(XLAT("transparent"), transparent, 't'); + dialog::add_action([] { transparent = !transparent; }); + + dialog::addSelItem(XLAT("gamma"), fts(gamma), 'g'); + dialog::add_action([] { dialog::editNumber(gamma, 0, 2, .1, .5, XLAT("gamma"), "higher value = darker"); }); + + dialog::addSelItem(XLAT("brightness"), fts(fade), 'b'); + dialog::add_action([] { dialog::editNumber(fade, 0, 2, .1, 1, XLAT("brightness"), "higher value = lighter"); }); + + dialog::addBoolItem(XLAT("show the HUD"), nohud, 'h'); + dialog::add_action([] { nohud = !nohud; }); + + dialog::addItem(XLAT("customize colors and aura"), 'c'); + dialog::add_action([] { pushScreen(show_color_dialog); }); + + menuitem_sightrange('r'); + + dialog::addBreak(100); + + dialog::addItem(XLAT("take screenshot"), 'z'); + dialog::add_action([] () { + static string pngfile = "hqshot.png"; + static string svgfile = "svgshot.svg"; + string& file = make_svg ? svgfile : pngfile; + dialog::openFileDialog(file, XLAT("screenshot"), make_svg ? ".svg" : ".png", [&file] () { + shot::take(file); + return true; + }); + }); + dialog::addBack(); + dialog::display(); + } + +} #endif #if CAP_ANIMATIONS @@ -544,7 +638,7 @@ bool record_animation() { char buf[1000]; snprintf(buf, 1000, animfile.c_str(), i); - saveHighQualityShot(buf); + shot::take(buf); rollback(); } lastticks = ticks = SDL_GetTicks(); @@ -747,9 +841,9 @@ void show() { } #endif if(conformal::model_has_orientation()) - animator(XLAT("model rotation"), ballangle_rotation, 'r'); + animator(XLAT("model rotation"), ballangle_rotation, 'R'); else if(among(pmodel, mdHyperboloid, mdHemisphere, mdBall)) - animator(XLAT("3D rotation"), ballangle_rotation, 'r'); + animator(XLAT("3D rotation"), ballangle_rotation, '3'); dialog::addSelItem(XLAT("animate parameters"), fts(a), 'a'); dialog::add_action([] () { @@ -766,7 +860,10 @@ void show() { dialog::addBoolItem(XLAT("history mode"), (conformal::on || conformal::includeHistory), 'h'); dialog::add_action([] () { pushScreen(conformal::history_menu); }); - #if CAP_FILES + #if CAP_SHOT + dialog::addItem(XLAT("shot settings"), 's'); + dialog::add_action([] () { pushScreen(shot::menu); }); + if(needs_highqual) dialog::addInfo(XLAT("some parameters will only change in recorded animation")); else @@ -775,7 +872,7 @@ void show() { dialog::add_action([] () { dialog::editNumber(noframes, 0, 300, 30, 5, XLAT("frames to record"), ""); }); dialog::addSelItem(XLAT("record to a file"), animfile, 'R'); dialog::add_action([] () { - dialog::openFileDialog(animfile, XLAT("record to a file"), ".png", record_animation); + dialog::openFileDialog(animfile, XLAT("record to a file"), shot::make_svg ? ".svg" : ".png", record_animation); }); #endif dialog::addBack(); diff --git a/sysconfig.h b/sysconfig.h index 4b8f94b3..2b47b07f 100644 --- a/sysconfig.h +++ b/sysconfig.h @@ -134,6 +134,10 @@ #define CAP_EDIT (CAP_FILES && !ISWEB && !ISMINI) #endif +#ifndef CAP_SHOT +#define CAP_SHOT (CAP_FILES && (CAP_SVG || CAP_PNG)) +#endif + #ifndef CAP_ODS #define CAP_ODS 0 #endif diff --git a/textures.cpp b/textures.cpp index 4c1286b4..2ab8d0f5 100644 --- a/textures.cpp +++ b/textures.cpp @@ -482,8 +482,10 @@ void texture_config::saveFullTexture(string tn) { texture::saving = true; drawscreen(); - dynamicval dv(pngres, data.twidth); - saveHighQualityShot(tn.c_str()); + dynamicval dvx(shot::shotx, data.twidth); + dynamicval dvy(shot::shoty, data.twidth); + dynamicval dvf(shot::shotformat, -1); + shot::take(tn.c_str()); texture::saving = false; drawscreen();