433 lines
11 KiB
C
433 lines
11 KiB
C
|
/* Small preview management */
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <math.h>
|
||
|
#include "common.h"
|
||
|
#include "../shared/types.h"
|
||
|
#include "../shared/functions.h"
|
||
|
#include "../shared/constants.h"
|
||
|
#include "../shared/system.h"
|
||
|
#include "../water.h"
|
||
|
|
||
|
#define MAX_PREVIEWS 30
|
||
|
|
||
|
struct SmallPreview
|
||
|
{
|
||
|
double conf_scroll_xmin;
|
||
|
double conf_scroll_xmax;
|
||
|
double conf_scroll_ymin;
|
||
|
double conf_scroll_ymax;
|
||
|
|
||
|
double conf_scale_min;
|
||
|
double conf_scale_max;
|
||
|
double conf_scale_step;
|
||
|
|
||
|
double xoffset;
|
||
|
double yoffset;
|
||
|
double scaling;
|
||
|
|
||
|
Mutex* lock;
|
||
|
GdkPixbuf* pixbuf;
|
||
|
GtkImage* image;
|
||
|
|
||
|
int mousex;
|
||
|
int mousey;
|
||
|
|
||
|
int need_rerender;
|
||
|
int need_render;
|
||
|
int need_update;
|
||
|
SmallPreviewCallback renderer;
|
||
|
};
|
||
|
|
||
|
static int _previews_count = 0;
|
||
|
static SmallPreview _preview[MAX_PREVIEWS];
|
||
|
static Thread* _thread_update;
|
||
|
|
||
|
static inline void _forceRender(SmallPreview* preview)
|
||
|
{
|
||
|
gdk_pixbuf_fill(preview->pixbuf, 0x00000000);
|
||
|
preview->need_rerender = 0;
|
||
|
preview->need_render = 1;
|
||
|
}
|
||
|
|
||
|
static inline void _updateImage(SmallPreview* preview)
|
||
|
{
|
||
|
gtk_image_set_from_pixbuf(preview->image, preview->pixbuf);
|
||
|
}
|
||
|
|
||
|
static inline void _renderPixbuf(SmallPreview* preview)
|
||
|
{
|
||
|
int x, y, done;
|
||
|
Color col;
|
||
|
void* pixels = gdk_pixbuf_get_pixels(preview->pixbuf);
|
||
|
guint32* pixel;
|
||
|
int rowstride = gdk_pixbuf_get_rowstride(preview->pixbuf);
|
||
|
|
||
|
/* TODO Use pixbuf size */
|
||
|
for (x = 0; x < 256; x++)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
if (preview->need_rerender)
|
||
|
{
|
||
|
mutexRelease(preview->lock);
|
||
|
break;
|
||
|
}
|
||
|
done = 0;
|
||
|
for (y = 0; y < 256; y++)
|
||
|
{
|
||
|
pixel = (guint32*)(pixels + y * rowstride + x * 4);
|
||
|
if (!*pixel)
|
||
|
{
|
||
|
col = preview->renderer(preview, (double)(x - 128) * preview->scaling + preview->xoffset, (double)(y - 128) * preview->scaling + preview->yoffset, preview->xoffset, preview->yoffset, preview->scaling);
|
||
|
*pixel = (guint32)colorTo32BitRGBA(&col);
|
||
|
done = 1;
|
||
|
}
|
||
|
}
|
||
|
if (done)
|
||
|
{
|
||
|
preview->need_update = 1;
|
||
|
}
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void* _doRenders(void* data)
|
||
|
{
|
||
|
int i;
|
||
|
SmallPreview* preview;
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
for (i = 0; i < _previews_count; i++)
|
||
|
{
|
||
|
preview = _preview + i;
|
||
|
if (preview->need_rerender)
|
||
|
{
|
||
|
_forceRender(preview);
|
||
|
}
|
||
|
if (preview->need_render)
|
||
|
{
|
||
|
preview->need_render = 0;
|
||
|
_renderPixbuf(preview);
|
||
|
}
|
||
|
}
|
||
|
timeSleepMs(100);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int _doUpdates(void* data)
|
||
|
{
|
||
|
int i;
|
||
|
SmallPreview* preview;
|
||
|
|
||
|
for (i = 0; i < _previews_count; i++)
|
||
|
{
|
||
|
preview = _preview + i;
|
||
|
if (preview->need_update)
|
||
|
{
|
||
|
preview->need_update = 0;
|
||
|
_updateImage(preview);
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void _scrollPixbuf(SmallPreview* preview, int dx, int dy)
|
||
|
{
|
||
|
int xstart, ystart, xsize, ysize, y;
|
||
|
void* pixels = gdk_pixbuf_get_pixels(preview->pixbuf);
|
||
|
int rowstride = gdk_pixbuf_get_rowstride(preview->pixbuf);
|
||
|
|
||
|
preview->xoffset -= (double)dx * preview->scaling;
|
||
|
preview->yoffset -= (double)dy * preview->scaling;
|
||
|
|
||
|
/* TODO Use pixbuf size */
|
||
|
if (dx == 0 && dy == 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
else if (dx <= -256 || dx >= 256 || dy <= -256 || dy >= 256)
|
||
|
{
|
||
|
_forceRender(preview);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (dx < 0)
|
||
|
{
|
||
|
xstart = -dx;
|
||
|
xsize = 256 + dx;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
xstart = 0;
|
||
|
xsize = 256 - dx;
|
||
|
}
|
||
|
if (dy < 0)
|
||
|
{
|
||
|
ystart = -dy;
|
||
|
ysize = 256 + dy;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ystart = 0;
|
||
|
ysize = 256 - dy;
|
||
|
}
|
||
|
memmove(pixels + (ystart + dy) * rowstride + (xstart + dx) * 4, pixels + ystart * rowstride + xstart * 4, (ysize - 1) * rowstride + xsize * 4);
|
||
|
if (dy < 0)
|
||
|
{
|
||
|
memset(pixels + (256 + dy) * rowstride, 0, (-dy - 1) * rowstride + 256 * 4);
|
||
|
}
|
||
|
else if (dy > 0)
|
||
|
{
|
||
|
memset(pixels, 0, (dy - 1) * rowstride + 256 * 4);
|
||
|
}
|
||
|
if (dx < 0)
|
||
|
{
|
||
|
for (y = ystart + dy; y < ystart + dy + ysize; y++)
|
||
|
{
|
||
|
memset(pixels + y * rowstride + xsize * 4, 0, -dx * 4);
|
||
|
}
|
||
|
}
|
||
|
else if (dx > 0)
|
||
|
{
|
||
|
for (y = ystart + dy; y < ystart + dy + ysize; y++)
|
||
|
{
|
||
|
memset(pixels + y * rowstride, 0, dx * 4);
|
||
|
}
|
||
|
}
|
||
|
preview->need_render = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static inline int _fixScroll(SmallPreview* preview, int dx, int dy, int* new_dx, int* new_dy)
|
||
|
{
|
||
|
*new_dx = dx;
|
||
|
*new_dy = dy;
|
||
|
if (preview->xoffset - dx * preview->scaling > preview->conf_scroll_xmax)
|
||
|
{
|
||
|
*new_dx = (int)floor((preview->conf_scroll_xmax - preview->xoffset) / preview->scaling);
|
||
|
}
|
||
|
if (preview->xoffset - dx * preview->scaling < preview->conf_scroll_xmin)
|
||
|
{
|
||
|
*new_dx = (int)floor((preview->conf_scroll_xmin - preview->xoffset) / preview->scaling);
|
||
|
}
|
||
|
if (preview->yoffset - dy * preview->scaling > preview->conf_scroll_ymax)
|
||
|
{
|
||
|
*new_dy = (int)floor((preview->conf_scroll_ymax - preview->yoffset) / preview->scaling);
|
||
|
}
|
||
|
if (preview->yoffset - dy * preview->scaling < preview->conf_scroll_ymin)
|
||
|
{
|
||
|
*new_dy = (int)floor((preview->conf_scroll_ymin - preview->yoffset) / preview->scaling);
|
||
|
}
|
||
|
return (*new_dx == 0 && *new_dy == 0) ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
static inline int _fixScaling(SmallPreview* preview, double scaling, double* new_scaling)
|
||
|
{
|
||
|
double old_scaling = preview->scaling;
|
||
|
*new_scaling = scaling;
|
||
|
if (scaling < preview->conf_scale_min)
|
||
|
{
|
||
|
*new_scaling = preview->conf_scale_min;
|
||
|
}
|
||
|
if (scaling > preview->conf_scale_max)
|
||
|
{
|
||
|
*new_scaling = preview->conf_scale_max;
|
||
|
}
|
||
|
return (old_scaling == *new_scaling) ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
static int _cbMouseScroll(GtkEventBox* image, GdkEventScroll* event, gpointer data)
|
||
|
{
|
||
|
SmallPreview* preview = (SmallPreview*)data;
|
||
|
|
||
|
/* TODO Center the zoom on the cursor */
|
||
|
|
||
|
if (event->direction == GDK_SCROLL_UP)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
if (_fixScaling(preview, preview->scaling - preview->conf_scale_step, &preview->scaling))
|
||
|
{
|
||
|
preview->need_rerender = 1;
|
||
|
}
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
else if (event->direction == GDK_SCROLL_DOWN)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
if (_fixScaling(preview, preview->scaling + preview->conf_scale_step, &preview->scaling))
|
||
|
{
|
||
|
preview->need_rerender = 1;
|
||
|
}
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int _cbMouseButtonPressed(GtkEventBox* image, GdkEventButton* event, gpointer data)
|
||
|
{
|
||
|
SmallPreview* preview = (SmallPreview*)data;
|
||
|
|
||
|
if (event->button == 1)
|
||
|
{
|
||
|
preview->mousex = (int)event->x;
|
||
|
preview->mousey = (int)event->y;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int _cbMouseMove(GtkEventBox* image, GdkEventMotion* event, gpointer data)
|
||
|
{
|
||
|
SmallPreview* preview = (SmallPreview*)data;
|
||
|
int dx, dy;
|
||
|
|
||
|
if (event->state & GDK_BUTTON1_MASK)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
|
||
|
dx = (int)event->x - preview->mousex;
|
||
|
dy = (int)event->y - preview->mousey;
|
||
|
|
||
|
if (_fixScroll(preview, dx, dy, &dx, &dy))
|
||
|
{
|
||
|
_scrollPixbuf(preview, dx, dy);
|
||
|
}
|
||
|
|
||
|
preview->mousex = (int)event->x;
|
||
|
preview->mousey = (int)event->y;
|
||
|
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static Color _renderNone(SmallPreview* preview, double x, double y, double xoffset, double yoffset, double scaling)
|
||
|
{
|
||
|
return COLOR_BLACK;
|
||
|
}
|
||
|
|
||
|
void guiPreviewStart()
|
||
|
{
|
||
|
_thread_update = threadCreate(_doRenders, NULL);
|
||
|
g_timeout_add(200, _doUpdates, NULL);
|
||
|
}
|
||
|
|
||
|
void guiPreviewRedraw(SmallPreview* preview)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
preview->need_rerender = 1;
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
|
||
|
void guiPreviewRedrawAll()
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < _previews_count; i++)
|
||
|
{
|
||
|
guiPreviewRedraw(_preview + i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SmallPreview* guiPreviewNew(GtkImage* image)
|
||
|
{
|
||
|
GtkWidget* parent = gtk_widget_get_parent(GTK_WIDGET(image));
|
||
|
SmallPreview* preview;
|
||
|
|
||
|
/* TODO Check the parent can receive events (or is a GtkEventBox) */
|
||
|
if (_previews_count < MAX_PREVIEWS)
|
||
|
{
|
||
|
preview = _preview + _previews_count;
|
||
|
preview->lock = mutexCreate();
|
||
|
preview->conf_scroll_xmin = 0.0;
|
||
|
preview->conf_scroll_xmax = 0.0;
|
||
|
preview->conf_scroll_ymin = 0.0;
|
||
|
preview->conf_scroll_ymax = 0.0;
|
||
|
preview->conf_scale_min = 1.0;
|
||
|
preview->conf_scale_max = 1.0;
|
||
|
preview->conf_scale_step = 0.0;
|
||
|
preview->scaling = 1.0;
|
||
|
preview->xoffset = 0.0;
|
||
|
preview->yoffset = 0.0;
|
||
|
/* TODO Get size from GtkImage */
|
||
|
preview->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 1, 8, 256, 256);
|
||
|
preview->image = image;
|
||
|
preview->need_rerender = 0;
|
||
|
preview->need_render = 0;
|
||
|
preview->need_update = 0;
|
||
|
preview->renderer = _renderNone;
|
||
|
|
||
|
gtk_image_clear(image);
|
||
|
_forceRender(preview);
|
||
|
|
||
|
g_signal_connect(parent, "scroll-event", G_CALLBACK(_cbMouseScroll), (gpointer)preview);
|
||
|
g_signal_connect(parent, "button-press-event", G_CALLBACK(_cbMouseButtonPressed), (gpointer)preview);
|
||
|
g_signal_connect(parent, "motion-notify-event", G_CALLBACK(_cbMouseMove), (gpointer)preview);
|
||
|
|
||
|
_previews_count++;
|
||
|
return preview;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* TODO Return fake preview */
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void guiPreviewConfigScrolling(SmallPreview* preview, double xmin, double xmax, double ymin, double ymax)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
preview->conf_scroll_xmin = xmin;
|
||
|
preview->conf_scroll_xmax = xmax;
|
||
|
preview->conf_scroll_ymin = ymin;
|
||
|
preview->conf_scroll_ymax = ymax;
|
||
|
preview->need_rerender = 1;
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
|
||
|
void guiPreviewConfigScaling(SmallPreview* preview, double min, double max, double step)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
preview->conf_scale_min = min;
|
||
|
preview->conf_scale_max = max;
|
||
|
preview->conf_scale_step = step;
|
||
|
preview->need_rerender = 1;
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
|
||
|
void guiPreviewSetRenderer(SmallPreview* preview, SmallPreviewCallback renderer)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
preview->renderer = renderer;
|
||
|
preview->need_rerender = 1;
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
|
||
|
void guiPreviewSetViewport(SmallPreview* preview, double xoffset, double yoffset, double scaling)
|
||
|
{
|
||
|
mutexAcquire(preview->lock);
|
||
|
preview->xoffset = xoffset;
|
||
|
preview->yoffset = yoffset;
|
||
|
preview->scaling = scaling;
|
||
|
preview->need_rerender = 1;
|
||
|
mutexRelease(preview->lock);
|
||
|
}
|
||
|
|
||
|
void guiPreviewSetTerrainHeight(SmallPreview* preview)
|
||
|
{
|
||
|
/*preview->conf_scroll_x = 1;
|
||
|
preview->conf_scroll_y = 1;
|
||
|
preview->conf_zoom = 1;
|
||
|
preview->init_scaling = preview->scaling = 0.1;
|
||
|
preview->init_xoffset = preview->xoffset = 0.0;
|
||
|
preview->init_yoffset = preview->yoffset = 0.0;
|
||
|
|
||
|
preview->renderer = _renderTopDownHeight;*/
|
||
|
|
||
|
guiPreviewRedraw(preview);
|
||
|
}
|