/* * UQM Starmap image generator * SDL/SVG driver implementation * * Uses: SDL, SDL_ttf * * * The GPL applies * */ #include "mapdrv.h" #include #include #include #include #include "SDL_image.h" #include "SDL.h" #include "SDL_ttf.h" static double width, height; struct mg_font { char* name; TTF_Font* font; double size; }; struct mg_image { SDL_Surface* img; char *filename; double width; double height; }; static int drv_init(int argc, char *argv[]); static int drv_term(void); static void drv_usage(void); static int drv_begin(double w, double h); static int drv_write(void); static int drv_end(void); static mg_font_t drv_loadFont(const char* name, double size, const char*); static void drv_freeFont(mg_font_t); static mg_image_t drv_loadImage(const char*); static void drv_freeImage(mg_image_t); static int drv_getImageSize(mg_image_t, mg_rectf_t*); static int drv_setResolution(int dpi_x, int dpi_y); static void drv_setClipRect(const mg_rectf_t*); static void drv_drawPixel(double x, double y, mg_color_t, double weight); static void drv_drawLine(double x0, double y0, double x1, double y1, mg_color_t, double weight); static void drv_drawRect(const mg_rectf_t*, mg_color_t color, double weight); static void drv_drawFilledRect(const mg_rectf_t*, mg_color_t color); static void drv_drawEllipse(double xc, double yc, double xr, double yr, mg_color_t, double weight); static void drv_drawFilledEllipse(double xc, double yc, double xr, double yr, mg_color_t); static void drv_drawImage(double x, double y, mg_image_t); static void drv_drawText(mg_font_t, double x, double y, const unsigned char* utf8str, mg_color_t); static void drv_getTextSize(mg_font_t, const unsigned char* utf8str, mg_rectf_t*); const mg_driver_t sdlsvg_drv = { "sdlsvg", // name "SDL/SVG image writer", // description // func ptrs drv_init, drv_term, drv_usage, drv_setResolution, drv_begin, drv_write, drv_end, drv_loadFont, drv_freeFont, drv_loadImage, drv_freeImage, drv_getImageSize, // draw func ptrs drv_setClipRect, drv_drawPixel, drv_drawLine, drv_drawRect, drv_drawFilledRect, drv_drawEllipse, drv_drawFilledEllipse, drv_drawImage, drv_drawText, drv_getTextSize, }; static int parseArguments(int argc, char* argv[]); static const char* fontdir = "."; static const char* outdev = "starmap.svg"; static FILE* fout; static int dpix, dpiy; static inline int inchToPixelsY(double inch) { int v = (int)(inch * dpiy); // make sure it's at least 1 pixel wide if present if (!v && inch) v = 1; return v; } static char colbuffer[8]; static char *color_string(mg_color_t color) { snprintf(colbuffer, 8, "#%2.2x%2.2x%2.2x", color.r, color.g, color.b); return colbuffer; } static char alphabuffer[6]; static char *alpha_string(mg_color_t color) { snprintf(alphabuffer, 6, "%1.3f", color.a/255.0); return alphabuffer; } static int drv_init(int argc, char *argv[]) { int ret; ret = parseArguments(argc, argv); if (ret) return ret; if (SDL_Init(SDL_INIT_NOPARACHUTE)) { mg_verbose(1, "Cannot init SDL\n"); return EXIT_FAILURE; } if (TTF_Init()) { mg_verbose(1, "Cannot init SDL_ttf\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } static int drv_term(void) { if (fout) fclose(fout); fout = 0; TTF_Quit(); SDL_Quit(); return EXIT_SUCCESS; } static void drv_usage(void) { mg_verbose(0, "sdlsvg driver : [-f ] [-o ]\n" "Options:\n" "\t-f dir where font files are located\n" "\t-o save image as (default is 'starmap.svg')\n" ); } static int parseArguments(int argc, char* argv[]) { int ch; while (-1 != (ch = getopt(argc, argv, "-f:o:"))) { switch (ch) { case 'f': fontdir = optarg; break; case 'o': outdev = optarg; break; default: // non-option -- probably an arg to unknown option ; } } return EXIT_SUCCESS; } static int drv_setResolution(int dpi_x, int dpi_y) { dpix = dpi_x; dpiy = dpi_y; return EXIT_SUCCESS; } static inline double pixelsToInchX(int pixels) { return (double)pixels / dpix; } static inline double pixelsToInchY(int pixels) { return (double)pixels / dpiy; } static int drv_begin(double w, double h) { width = w; height = h; fout = fopen(outdev, "wt"); if (!fout) { mg_verbose(1, "Cannot open file '%s' -- %s\n", outdev, strerror(errno)); return EXIT_FAILURE; } fprintf(fout, "\n"); fprintf(fout, "\n"); fprintf(fout, "\n", w, h); return EXIT_SUCCESS; } static int drv_end(void) { if (fout) { fprintf(fout, "\n"); fclose(fout); fout = 0; } return EXIT_SUCCESS; } static int drv_write(void) { if (!fout) return EXIT_FAILURE; return EXIT_SUCCESS; } static TTF_Font* openFont(const char* filename, int size) { char buf[512]; char namebuf[128]; TTF_Font* font; strncpy(namebuf, filename, sizeof(namebuf)); namebuf[sizeof(namebuf) - 1] = '\0'; // first try the proper case filename snprintf(buf, sizeof(buf), "%s/%s", fontdir, namebuf); font = TTF_OpenFont(buf, size); if (font) return font; // then try the lowercase filename strlwr(namebuf); snprintf(buf, sizeof(buf), "%s/%s", fontdir, namebuf); font = TTF_OpenFont(buf, size); if (font) return font; // last try the uppercase filename strupr(namebuf); snprintf(buf, sizeof(buf), "%s/%s", fontdir, namebuf); font = TTF_OpenFont(buf, size); if (font) return font; mg_verbose(2, "TTF_OpenFont(): %s\n", TTF_GetError()); return 0; } static mg_font_t drv_loadFont(const char* name, double size, const char* filename) { struct mg_font* fnt; if (!filename) { mg_verbose(1, "sdlsvg: filename not specified for font '%s'\n", name); return 0; } fnt = calloc(1, sizeof(*fnt)); if (!fnt) return 0; fnt->size = size; fnt->name = strdup(name ? name : ""); fnt->font = openFont(filename, inchToPixelsY(fnt->size)); if (!fnt->font) { free(fnt); return 0; } return fnt; } static void drv_freeFont(mg_font_t fnt) { if (!fnt) return; if (fnt->name) free(fnt->name); if (fnt->font) TTF_CloseFont(fnt->font); free(fnt); } static mg_image_t drv_loadImage(const char* filename) { struct mg_image* img; img = calloc(1, sizeof(*img)); if (!img) return 0; img->img = IMG_Load(filename); if (!img->img) { free(img); return 0; } img->filename = strdup(filename); if (!img->filename) { drv_freeImage(img); return 0; } img->width = pixelsToInchX(img->img->w); img->height = pixelsToInchY(img->img->h); return img; } static void drv_freeImage(mg_image_t img) { if (!img) return; if (img->filename) free(img->filename); if (img->img) SDL_FreeSurface(img->img); free(img); } static int drv_getImageSize(mg_image_t img, mg_rectf_t* r) { if (!r) return EXIT_FAILURE; r->x = r->y = 0; if (img) { r->w = img->width; r->h = img->height; } else { r->w = 0; r->h = 0; } return EXIT_SUCCESS; } static int clipon = 0; static int clipcount = 0; static void drv_setClipRect(const mg_rectf_t* r) { if (clipon) fprintf(fout, "\n"); if (!r) clipon = 0; else { mg_color_t t; fprintf(fout, "\n", clipcount); t.r = t.g = t.b = t.a = 255; drv_drawFilledRect(r, t); fprintf(fout, "\n"); clipon = 1; fprintf(fout, "\n", clipcount++); } } static void drv_drawPixel(double xf, double yf, mg_color_t color, double weightf) { drv_drawLine(xf, yf, xf, yf, color, weightf); } static void drv_drawLine(double x0f, double y0f, double x1f, double y1f, mg_color_t color, double weightf) { fprintf(fout, "\n", x0f, y0f, x1f, y1f, color_string(color), weightf, alpha_string(color)); } static void drv_drawRect(const mg_rectf_t* r, mg_color_t color, double weightf) { // draw the box drv_drawLine(r->x, r->y, r->x + r->w, r->y, color, weightf); drv_drawLine(r->x, r->y + r->h, r->x + r->w, r->y + r->h, color, weightf); drv_drawLine(r->x, r->y, r->x, r->y + r->h, color, weightf); drv_drawLine(r->x + r->w, r->y, r->x + r->w, r->y + r->h, color, weightf); } static void drv_drawFilledRect(const mg_rectf_t* r, mg_color_t color) { if (!r) { mg_rectf_t t; t.x = t.y = 0; t.w = width; t.h = height; drv_drawFilledRect(&t, color); } else { fprintf(fout, "\n", r->x, r->y, r->w, r->h, color_string(color), alpha_string(color)); } } static void drv_drawEllipse(double xcf, double ycf, double xrf, double yrf, mg_color_t color, double weight) { fprintf(fout, "\n", xcf, ycf, xrf, yrf, color_string(color), alpha_string(color), weight); } static void drv_drawFilledEllipse(double xcf, double ycf, double xrf, double yrf, mg_color_t color) { fprintf(fout, "\n", xcf, ycf, xrf, yrf, color_string(color), alpha_string(color)); } static void drv_drawImage(double xf, double yf, struct mg_image* img) { fprintf(fout, "\n", img->filename, xf, yf, img->width, img->height); } static void drv_drawText(struct mg_font* fnt, double xf, double yf, const unsigned char* utf8str, mg_color_t color) { mg_rectf_t t; if (!fnt || !utf8str) return; drv_getTextSize(fnt, utf8str, &t); yf += t.h; fprintf(fout, "\n", xf, yf, fnt->size*96.0, fnt->name, color_string(color), alpha_string(color)); fprintf(fout, "%s", utf8str); } static void drv_getTextSize(struct mg_font* fnt, const unsigned char* utf8str, mg_rectf_t* r) { int w = 0; int h = 0; if (!r) return; if (!fnt || !utf8str) { r->w = 0; r->h = 0; return; } TTF_SizeUTF8(fnt->font, (const char*) utf8str, &w, &h); r->w = (double)w / dpix; r->h = (double)h / dpiy; }