Starmap image generator, preview 1
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2254 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
Executable
+861
@@ -0,0 +1,861 @@
|
||||
/*
|
||||
* UQM Starmap image generator
|
||||
* SDL/PNG driver implementation
|
||||
*
|
||||
* Uses: SDL, SDL_image, SDL_ttf, parts of SDL_gfx, and libpng
|
||||
*
|
||||
*
|
||||
* The GPL applies
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mapdrv.h"
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
#include "SDL.h"
|
||||
#include "SDL_image.h"
|
||||
#include "SDL_ttf.h"
|
||||
#include "SDL_gfxPrimitives.h"
|
||||
#include <png.h>
|
||||
|
||||
struct mg_font
|
||||
{
|
||||
char* name;
|
||||
TTF_Font* font;
|
||||
int size;
|
||||
};
|
||||
|
||||
struct mg_image
|
||||
{
|
||||
SDL_Surface* img;
|
||||
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_setResolution(int dpi_x, int dpi_y);
|
||||
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 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 sdlpng_drv =
|
||||
{
|
||||
"sdlpng", // name
|
||||
"SDL/PNG 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,
|
||||
|
||||
};
|
||||
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
# define R_MASK 0xff000000
|
||||
# define G_MASK 0x00ff0000
|
||||
# define B_MASK 0x0000ff00
|
||||
# define A_MASK 0x000000ff
|
||||
|
||||
# define PNG_R_MASK 0x00ff0000
|
||||
# define PNG_G_MASK 0x0000ff00
|
||||
# define PNG_B_MASK 0x000000ff
|
||||
# define PNG_A_MASK 0xff000000
|
||||
|
||||
#else
|
||||
# define R_MASK 0x00ff0000
|
||||
# define G_MASK 0x0000ff00
|
||||
# define B_MASK 0x000000ff
|
||||
# define A_MASK 0xff000000
|
||||
|
||||
# define PNG_R_MASK 0x000000ff
|
||||
# define PNG_G_MASK 0x0000ff00
|
||||
# define PNG_B_MASK 0x00ff0000
|
||||
# define PNG_A_MASK 0xff000000
|
||||
#endif
|
||||
|
||||
static int parseArguments(int argc, char* argv[]);
|
||||
|
||||
static SDL_PixelFormat* imgfmt;
|
||||
static SDL_Surface* fmtimg;
|
||||
static SDL_Surface* outimg;
|
||||
|
||||
static int use32bpp;
|
||||
static const char* fontdir = ".";
|
||||
static const char* outdev = "starmap.png";
|
||||
static FILE* fout;
|
||||
static int dpix, dpiy;
|
||||
|
||||
static char* strcopy(const char* str)
|
||||
{
|
||||
int l;
|
||||
char* s;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
l = strlen(str);
|
||||
s = malloc(l + 1);
|
||||
if (!s)
|
||||
return 0;
|
||||
memcpy(s, str, l + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
imgfmt = 0;
|
||||
if (fmtimg)
|
||||
SDL_FreeSurface(fmtimg);
|
||||
fmtimg = 0;
|
||||
if (outimg)
|
||||
SDL_FreeSurface(outimg);
|
||||
outimg = 0;
|
||||
|
||||
if (fout)
|
||||
fclose(fout);
|
||||
fout = 0;
|
||||
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void drv_usage(void)
|
||||
{
|
||||
mg_verbose(0,
|
||||
"sdlpng driver : [-f <fontdir>] [-n] [-o <outfile>]\n"
|
||||
"Options:\n"
|
||||
"\t-f dir where font files are located\n"
|
||||
"\t-n use native 32bpp surface for rendering\n"
|
||||
"\t doubles memory requirement but may increase quality in some cases\n"
|
||||
"\t-o save image as <outfile> (default is 'starmap.png')\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int parseArguments(int argc, char* argv[])
|
||||
{
|
||||
char ch;
|
||||
|
||||
while (-1 != (ch = getopt(argc, argv, "-f:no:")))
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case 'f':
|
||||
fontdir = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
use32bpp = 1;
|
||||
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 int inchToPixelsX(double inch)
|
||||
{
|
||||
int v = (int)(inch * dpix);
|
||||
// make sure it's at least 1 pixel wide if present
|
||||
if (!v && inch)
|
||||
v = 1;
|
||||
return v;
|
||||
}
|
||||
|
||||
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 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)
|
||||
{
|
||||
int targetbpp;
|
||||
|
||||
fout = fopen(outdev, "wb");
|
||||
if (!fout)
|
||||
{
|
||||
mg_verbose(1, "Cannot open file '%s' -- %s\n", outdev, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (use32bpp)
|
||||
{ // SDL-native channel order; allows exact accelerated blits
|
||||
fmtimg = SDL_CreateRGBSurface(SDL_SWSURFACE, 0, 0, 32,
|
||||
R_MASK, G_MASK, B_MASK, A_MASK);
|
||||
targetbpp = 32;
|
||||
}
|
||||
else
|
||||
{ // PNG-native channel order; saves memory
|
||||
fmtimg = SDL_CreateRGBSurface(SDL_SWSURFACE, 0, 0, 32,
|
||||
PNG_R_MASK, PNG_G_MASK, PNG_B_MASK, PNG_A_MASK);
|
||||
targetbpp = 24;
|
||||
}
|
||||
|
||||
if (!fmtimg)
|
||||
{
|
||||
mg_verbose(1, "Cannot create format image\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
imgfmt = fmtimg->format;
|
||||
|
||||
outimg = SDL_CreateRGBSurface(SDL_SWSURFACE, inchToPixelsX(w), inchToPixelsY(h),
|
||||
targetbpp, imgfmt->Rmask, imgfmt->Gmask, imgfmt->Bmask, 0);
|
||||
if (!outimg)
|
||||
{
|
||||
mg_verbose(1, "Cannot create work image\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int drv_end(void)
|
||||
{
|
||||
imgfmt = 0;
|
||||
if (fmtimg)
|
||||
SDL_FreeSurface(fmtimg);
|
||||
fmtimg = 0;
|
||||
if (outimg)
|
||||
SDL_FreeSurface(outimg);
|
||||
outimg = 0;
|
||||
|
||||
if (fout)
|
||||
fclose(fout);
|
||||
fout = 0;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int drv_write(void)
|
||||
{
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
SDL_Surface* cimg = NULL;
|
||||
SDL_PixelFormat* fmt = outimg->format;
|
||||
png_bytep* lines = NULL;
|
||||
int i;
|
||||
|
||||
if (!outimg || !fout)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
mg_verbose(2, "Writing image to '%s'\n", outdev);
|
||||
|
||||
if (fmt->BytesPerPixel != 3 ||
|
||||
fmt->Rmask != PNG_R_MASK ||
|
||||
fmt->Gmask != PNG_G_MASK ||
|
||||
fmt->Bmask != PNG_B_MASK)
|
||||
{
|
||||
cimg = SDL_CreateRGBSurface(SDL_SWSURFACE, outimg->w, outimg->h, 24,
|
||||
PNG_R_MASK, PNG_G_MASK, PNG_B_MASK, 0);
|
||||
if (!cimg)
|
||||
return EXIT_FAILURE;
|
||||
SDL_BlitSurface(outimg, NULL, cimg, NULL);
|
||||
outimg = cimg;
|
||||
}
|
||||
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL);
|
||||
if (!png_ptr)
|
||||
{
|
||||
mg_verbose(1, "png_create_write_struct failed.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr)
|
||||
{
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
||||
mg_verbose(1, "png_create_info_struct failed.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
{
|
||||
mg_verbose(1, "png error.\n");
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
lines = calloc(outimg->h, sizeof(png_bytep));
|
||||
if (!lines)
|
||||
return EXIT_FAILURE;
|
||||
for (i = 0; i < outimg->h; ++i)
|
||||
lines[i] = (Uint8*)outimg->pixels + i * outimg->pitch;
|
||||
|
||||
png_init_io(png_ptr, fout);
|
||||
png_set_IHDR(png_ptr, info_ptr, outimg->w, outimg->h, 8,
|
||||
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_BASE);
|
||||
png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
|
||||
png_set_pHYs(png_ptr, info_ptr, dpix * 10000 / 254, dpiy * 10000 / 254,
|
||||
PNG_RESOLUTION_METER);
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
png_set_packing(png_ptr);
|
||||
|
||||
png_write_image(png_ptr, lines);
|
||||
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
if (cimg)
|
||||
SDL_FreeSurface(cimg);
|
||||
if (lines)
|
||||
free(lines);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static mg_font_t drv_loadFont(const char* name, double size, const char* filename)
|
||||
{
|
||||
char buf[512];
|
||||
struct mg_font* fnt;
|
||||
|
||||
fnt = calloc(1, sizeof(*fnt));
|
||||
if (!fnt)
|
||||
return 0;
|
||||
fnt->size = inchToPixelsY(size);
|
||||
fnt->name = strcopy(name);
|
||||
sprintf(buf, "%s/%s", fontdir, filename);
|
||||
fnt->font = TTF_OpenFont(buf, 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->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->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 void drv_setClipRect(const mg_rectf_t* r)
|
||||
{
|
||||
if (!r)
|
||||
{
|
||||
SDL_SetClipRect(outimg, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_Rect dr;
|
||||
dr.x = (int)(r->x * dpix);
|
||||
dr.y = (int)(r->y * dpiy);
|
||||
dr.w = (int)(r->w * dpix);
|
||||
dr.h = (int)(r->h * dpiy);
|
||||
SDL_SetClipRect(outimg, &dr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline Uint32 colorToGfx(mg_color_t clr)
|
||||
{
|
||||
return (clr.r << 24) | (clr.g << 16) | (clr.b << 8) | (clr.a);
|
||||
}
|
||||
|
||||
static inline Uint32 colorToSDL(mg_color_t clr)
|
||||
{
|
||||
return SDL_MapRGBA(imgfmt, clr.r, clr.g, clr.b, clr.a);
|
||||
}
|
||||
|
||||
static void drv_drawPixel(double xf, double yf, mg_color_t color, double weightf)
|
||||
{
|
||||
// weight is ignored here
|
||||
const int x0 = (int)(xf * dpix);
|
||||
const int y0 = (int)(yf * dpiy);
|
||||
const Uint32 clr = colorToGfx(color);
|
||||
pixelColor(outimg, x0, y0, clr);
|
||||
|
||||
(void)weightf; // hush little compiler
|
||||
}
|
||||
|
||||
static void drawHLine(int x0, int x1, int y, mg_color_t color, int weight)
|
||||
{
|
||||
int i;
|
||||
int y0 = y - weight / 2;
|
||||
const Uint32 clr = colorToGfx(color);
|
||||
Uint32 aaclr;
|
||||
Uint32 c;
|
||||
|
||||
color.a = 0xb0;
|
||||
aaclr = colorToGfx(color);
|
||||
|
||||
for (i = 0; i < weight; ++i, ++y0)
|
||||
{
|
||||
if (weight > 2 && (i == 0 || i == weight - 1))
|
||||
c = aaclr;
|
||||
else
|
||||
c = clr;
|
||||
|
||||
hlineColor(outimg, x0, x1, y0, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawVLine(int x, int y0, int y1, mg_color_t color, int weight)
|
||||
{
|
||||
int i;
|
||||
int x0 = x - weight / 2;
|
||||
const Uint32 clr = colorToGfx(color);
|
||||
Uint32 aaclr;
|
||||
Uint32 c;
|
||||
|
||||
color.a = 0xb0;
|
||||
aaclr = colorToGfx(color);
|
||||
|
||||
for (i = 0; i < weight; ++i, ++x0)
|
||||
{
|
||||
if (weight > 2 && (i == 0 || i == weight - 1))
|
||||
c = aaclr;
|
||||
else
|
||||
c = clr;
|
||||
|
||||
vlineColor(outimg, x0, y0, y1, c);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a pretty crude floating point algorithm
|
||||
// for drawing thick anti-aliased lines with rounded end-points
|
||||
// Not optimized at all, but we do not need speed here
|
||||
static void drv_drawLine(double x0f, double y0f, double x1f, double y1f, mg_color_t color, double weightf)
|
||||
{
|
||||
int x0 = (int)(x0f * dpix);
|
||||
int y0 = (int)(y0f * dpiy);
|
||||
int x1 = (int)(x1f * dpix);
|
||||
int y1 = (int)(y1f * dpiy);
|
||||
int dx, dy;
|
||||
double length;
|
||||
double sinl, cosl;
|
||||
double hw, thres;
|
||||
int x, y;
|
||||
const Uint32 clr = colorToGfx(color);
|
||||
const Uint32 clrna = clr & 0xffffff00;
|
||||
int weight;
|
||||
|
||||
if (x0 == x1)
|
||||
{
|
||||
drawVLine(x0, y0, y1, color, inchToPixelsX(weightf));
|
||||
return;
|
||||
}
|
||||
if (y0 == y1)
|
||||
{
|
||||
drawHLine(x0, x1, y0, color, inchToPixelsY(weightf));
|
||||
return;
|
||||
}
|
||||
|
||||
weight = (int)((weightf * dpix + weightf * dpiy) / 2);
|
||||
if (weight <= 1)
|
||||
{ // 1-pixel wide
|
||||
aalineColor(outimg, x0, y0, x1, y1, clr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (x1 < x0)
|
||||
{ // swap line endpoints
|
||||
int t;
|
||||
t = x0;
|
||||
x0 = x1;
|
||||
x1 = t;
|
||||
t = y0;
|
||||
y0 = y1;
|
||||
y1 = t;
|
||||
}
|
||||
|
||||
dx = x1 - x0;
|
||||
dy = y1 - y0;
|
||||
length = sqrt(dx*dx + dy*dy);
|
||||
sinl = (double)dy / length;
|
||||
cosl = (double)dx / length;
|
||||
hw = (double)weight / 2;
|
||||
thres = hw - 0.6;
|
||||
if (thres < 0)
|
||||
thres = 0;
|
||||
|
||||
for (x = x0 - weight; x < x1 + weight; ++x)
|
||||
{
|
||||
double py = (double)(x - x0) / dx * dy;
|
||||
double ly = y0 + py;
|
||||
int iy;
|
||||
int sy;
|
||||
|
||||
if (x < x0)
|
||||
sy = y0;
|
||||
else if (x > x1)
|
||||
sy = y1;
|
||||
else
|
||||
sy = (int)ly;
|
||||
|
||||
for (iy = -1; iy <= 1; ++iy)
|
||||
{
|
||||
if (iy == 0)
|
||||
{
|
||||
++iy;
|
||||
++sy;
|
||||
}
|
||||
|
||||
for (y = sy; ; y += iy)
|
||||
{
|
||||
double oy = py + (y - ly) * fabs(sinl);
|
||||
double dist;
|
||||
|
||||
if (y1 < y0)
|
||||
oy = -oy;
|
||||
|
||||
// distance from ideal line point dictates opacity
|
||||
if (oy < 0)
|
||||
{
|
||||
dist = sqrt((x - x0) * (x - x0) + (y - y0) * (y - y0));
|
||||
}
|
||||
else if (oy > abs(dy))
|
||||
{
|
||||
dist = sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
|
||||
}
|
||||
else
|
||||
{
|
||||
dist = fabs((double)y - ly) * fabs(cosl);
|
||||
}
|
||||
|
||||
if (dist <= thres)
|
||||
{
|
||||
pixelColor(outimg, x, y, clr);
|
||||
}
|
||||
else
|
||||
{ // anti-alias the pixel
|
||||
int a = (int)((1 - (dist - thres)) * 255);
|
||||
if (a <= 0)
|
||||
break;
|
||||
pixelColor(outimg, x, y, clrna | ((color.a * a) >> 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const Uint32 clr = colorToSDL(color);
|
||||
|
||||
if (!r)
|
||||
{ // whole surface
|
||||
SDL_FillRect(outimg, NULL, clr);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_Rect dr;
|
||||
dr.x = (int)(r->x * dpix);
|
||||
dr.y = (int)(r->y * dpiy);
|
||||
dr.w = (int)(r->w * dpix);
|
||||
dr.h = (int)(r->h * dpiy);
|
||||
SDL_FillRect(outimg, &dr, clr);
|
||||
}
|
||||
}
|
||||
|
||||
static void drv_drawEllipse(double xcf, double ycf, double xrf, double yrf, mg_color_t color, double weight)
|
||||
{
|
||||
// weight currently ignored
|
||||
int xc = (int)(xcf * dpix);
|
||||
int yc = (int)(ycf * dpiy);
|
||||
int xr = (int)(xrf * dpix);
|
||||
int yr = (int)(yrf * dpiy);
|
||||
const Uint32 clr = colorToGfx(color);
|
||||
|
||||
aaellipseColor(outimg, xc, yc, xr, yr, clr);
|
||||
|
||||
(void)weight; // hush little compiler
|
||||
}
|
||||
|
||||
// This is a pretty crude floating point algorithm
|
||||
// for drawing anti-aliased filled ellipse
|
||||
// Not really optimized at all, but we do not need speed here
|
||||
static void drv_drawFilledEllipse(double xcf, double ycf, double xrf, double yrf, mg_color_t color)
|
||||
{
|
||||
const int xc = (int)(xcf * dpix);
|
||||
const int yc = (int)(ycf * dpiy);
|
||||
const int xr = (int)(xrf * dpix);
|
||||
const int yr = (int)(yrf * dpiy);
|
||||
const Uint32 clr = colorToGfx(color);
|
||||
const Uint32 clrna = clr & 0xffffff00;
|
||||
int x, y;
|
||||
double radius, radius_2, rad_thres;
|
||||
double fx_2, fy_2;
|
||||
|
||||
// the (more or less) classic elliptic equation is
|
||||
// (fx*x)^2 + (fy*y)^2 = r^2 (classic: fx=r/a, fy=r/b)
|
||||
// we'll take a=r (or b=r) and transform
|
||||
// x^2 + (fy*y)^2 = r^2
|
||||
// or more precisely, along the major axis
|
||||
if (xr >= yr)
|
||||
radius = xr;
|
||||
else
|
||||
radius = yr;
|
||||
|
||||
radius_2 = radius * radius;
|
||||
fx_2 = radius / xr;
|
||||
fx_2 = fx_2 * fx_2;
|
||||
fy_2 = radius / yr;
|
||||
fy_2 = fy_2 * fy_2;
|
||||
rad_thres = (radius + 1) * (radius + 1);
|
||||
|
||||
for (y = -yr; y <= yr; y++)
|
||||
{
|
||||
double y_2 = y * y * fy_2;
|
||||
|
||||
for (x = -xr; x <= xr; x++)
|
||||
{
|
||||
double r_2 = y_2 + x * x * fx_2;
|
||||
|
||||
if (r_2 >= rad_thres)
|
||||
{ // outside bounds
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r_2 <= radius_2)
|
||||
{ // solid pixels inside the ellipse
|
||||
pixelColor(outimg, xc + x, yc + y, clr);
|
||||
}
|
||||
else
|
||||
{ // anti-aliased edge
|
||||
int a = (int) ((1 - (sqrt(r_2) - radius)) * 255);
|
||||
if (a < 0)
|
||||
continue; // should never happen
|
||||
pixelColor(outimg, xc + x, yc + y, clrna | ((color.a * a) >> 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void setImageAlpha(SDL_Surface* img, int alpha)
|
||||
{
|
||||
SDL_PixelFormat* fmt = img->format;
|
||||
int x, y;
|
||||
|
||||
if (!fmt->Amask)
|
||||
{
|
||||
SDL_SetAlpha(img, SDL_SRCALPHA, alpha);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fmt->BytesPerPixel != 4)
|
||||
{
|
||||
mg_verbose(1, "Unsuppoted surface format for alpha -- %d bytes/pixel\n", (int)fmt->BytesPerPixel);
|
||||
return;
|
||||
}
|
||||
|
||||
// adjust the alpha channel accordingly
|
||||
for (y = 0; y < img->h; ++y)
|
||||
{
|
||||
Uint32* dst = (Uint32*)((Uint8*)img->pixels + img->pitch * y);
|
||||
|
||||
for (x = 0; x < img->w; ++x, ++dst)
|
||||
{
|
||||
Uint32 p = *dst;
|
||||
Uint32 a = (p & fmt->Amask) >> fmt->Ashift;
|
||||
p &= ~fmt->Amask;
|
||||
a = a * alpha / 255;
|
||||
*dst = p | (a << fmt->Ashift);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drv_drawImage(double xf, double yf, struct mg_image* img)
|
||||
{
|
||||
SDL_Rect dr;
|
||||
|
||||
dr.x = (int)(xf * dpix);
|
||||
dr.y = (int)(yf * dpiy);
|
||||
|
||||
SDL_BlitSurface(img->img, NULL, outimg, &dr);
|
||||
}
|
||||
|
||||
static void drv_drawText(struct mg_font* fnt, double xf, double yf, const unsigned char* utf8str, mg_color_t color)
|
||||
{
|
||||
SDL_Rect dr;
|
||||
SDL_Color clr = {color.r, color.g, color.b, 0};
|
||||
SDL_Surface* text;
|
||||
|
||||
if (!fnt || !utf8str)
|
||||
return;
|
||||
|
||||
dr.x = (int)(xf * dpix);
|
||||
dr.y = (int)(yf * dpiy);
|
||||
|
||||
text = TTF_RenderUTF8_Blended(fnt->font, utf8str, clr);
|
||||
if (!text)
|
||||
return;
|
||||
if (color.a != 0xff)
|
||||
setImageAlpha(text, color.a);
|
||||
SDL_BlitSurface(text, NULL, outimg, &dr);
|
||||
SDL_FreeSurface(text);
|
||||
}
|
||||
|
||||
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, utf8str, &w, &h);
|
||||
r->w = (double)w / dpix;
|
||||
r->h = (double)h / dpiy;
|
||||
}
|
||||
Reference in New Issue
Block a user