mirror of
https://github.com/nothings/stb-imv
synced 2024-11-21 21:11:51 +03:00
commenting, refactoring, add preferences for label
This commit is contained in:
parent
1ff2efa350
commit
36385459b4
382
imv.c
382
imv.c
@ -45,7 +45,7 @@ void error(char *str) { MessageBox(NULL, str, "imv(stb) error", MB_OK); }
|
|||||||
|
|
||||||
// OutputDebugString with varargs, can be compiled out
|
// OutputDebugString with varargs, can be compiled out
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
int do_debug;
|
int do_debug=1;
|
||||||
void ods(char *str, ...)
|
void ods(char *str, ...)
|
||||||
{
|
{
|
||||||
if (do_debug) {
|
if (do_debug) {
|
||||||
@ -551,7 +551,7 @@ char helptext_left[] =
|
|||||||
"CTRL-MINUS: zoom out\n"
|
"CTRL-MINUS: zoom out\n"
|
||||||
"RIGHT, SPACE: next image\n"
|
"RIGHT, SPACE: next image\n"
|
||||||
"LEFT, BACKSPACE: previous image\n"
|
"LEFT, BACKSPACE: previous image\n"
|
||||||
" CTRL-O: open image\n"
|
" (CTRL-) O: open image\n"
|
||||||
" P: change preferences\n"
|
" P: change preferences\n"
|
||||||
" F: toggle frame\n"
|
" F: toggle frame\n"
|
||||||
"SHIFT-F: toggle white stripe in frame\n"
|
"SHIFT-F: toggle white stripe in frame\n"
|
||||||
@ -577,11 +577,8 @@ char helptext_right[] =
|
|||||||
// looks a little nicer so why not
|
// looks a little nicer so why not
|
||||||
void draw_nice(HDC hdc, char *text, RECT *rect, uint flags)
|
void draw_nice(HDC hdc, char *text, RECT *rect, uint flags)
|
||||||
{
|
{
|
||||||
#if 1
|
|
||||||
int i,j;
|
int i,j;
|
||||||
SetTextColor(hdc, RGB(80,80,80));
|
SetTextColor(hdc, RGB(80,80,80));
|
||||||
//for (i=-1; i <= 1; i += 1)
|
|
||||||
//for (j=-1; j <= 1; j += 1)
|
|
||||||
for (i=2; i >= 1; i -= 1)
|
for (i=2; i >= 1; i -= 1)
|
||||||
for (j=2; j >= 1; j -= 1)
|
for (j=2; j >= 1; j -= 1)
|
||||||
{
|
{
|
||||||
@ -591,7 +588,6 @@ void draw_nice(HDC hdc, char *text, RECT *rect, uint flags)
|
|||||||
SetTextColor(hdc, RGB(0,0,0));
|
SetTextColor(hdc, RGB(0,0,0));
|
||||||
DrawText(hdc, text, -1, &r, flags);
|
DrawText(hdc, text, -1, &r, flags);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
SetTextColor(hdc, RGB(255,255,255));
|
SetTextColor(hdc, RGB(255,255,255));
|
||||||
DrawText(hdc, text, -1, rect, flags);
|
DrawText(hdc, text, -1, rect, flags);
|
||||||
}
|
}
|
||||||
@ -613,6 +609,20 @@ void set_error(volatile ImageFile *z)
|
|||||||
}
|
}
|
||||||
|
|
||||||
HFONT label_font;
|
HFONT label_font;
|
||||||
|
int label_font_height=12;
|
||||||
|
|
||||||
|
// build the font for the filename label
|
||||||
|
void build_label_font(void)
|
||||||
|
{
|
||||||
|
LOGFONT lf;
|
||||||
|
memset(&lf, 0, sizeof(lf));
|
||||||
|
lf.lfHeight = label_font_height;
|
||||||
|
lf.lfOutPrecision = OUT_TT_PRECIS; // prefer truetype to raster fonts
|
||||||
|
strcpy(lf.lfFaceName, "Times New Roman");
|
||||||
|
if (label_font) DeleteObject(label_font);
|
||||||
|
label_font = CreateFontIndirect(&lf);
|
||||||
|
}
|
||||||
|
|
||||||
int show_frame = TRUE; // show border or not?
|
int show_frame = TRUE; // show border or not?
|
||||||
int show_label = FALSE; // display the help text or not
|
int show_label = FALSE; // display the help text or not
|
||||||
|
|
||||||
@ -1029,7 +1039,7 @@ void toggle_frame(void)
|
|||||||
int best_lru = 0;
|
int best_lru = 0;
|
||||||
|
|
||||||
// when we change which file is the one being viewed/resized,
|
// when we change which file is the one being viewed/resized,
|
||||||
// call this function
|
// call this function to update our globals and fit to window
|
||||||
void update_source(ImageFile *q)
|
void update_source(ImageFile *q)
|
||||||
{
|
{
|
||||||
source = q->image;
|
source = q->image;
|
||||||
@ -1251,7 +1261,7 @@ void queue_disk_command(DiskCommand *dc, int which, int make_current)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// if it's not inactive and none of the above, it's an error
|
||||||
if (z->status != LOAD_inactive) {
|
if (z->status != LOAD_inactive) {
|
||||||
if (make_current) {
|
if (make_current) {
|
||||||
set_error(z);
|
set_error(z);
|
||||||
@ -1346,8 +1356,10 @@ void advance(int dir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ctrl-O, or initial command if no filename: run
|
// ctrl-O, or initial command if no filename: run
|
||||||
// GetOpenFileName(), load the specified filelist,
|
// GetOpenFileName(), set as the active filename,
|
||||||
//
|
// load the specified filelist, set cur_loc into
|
||||||
|
// the filelist, and force it to load (and prefetch)
|
||||||
|
// with 'advance'
|
||||||
static char filenamebuffer[4096];
|
static char filenamebuffer[4096];
|
||||||
void open_file(void)
|
void open_file(void)
|
||||||
{
|
{
|
||||||
@ -1368,18 +1380,31 @@ void open_file(void)
|
|||||||
advance(0);
|
advance(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleaner casting in C--remember, C macros of the form "foo()" don't
|
||||||
|
// conflict with uses for 'foo' without a following open parenthesis,
|
||||||
|
// so this doesn't cause problems
|
||||||
#define int(x) ((int) (x))
|
#define int(x) ((int) (x))
|
||||||
|
|
||||||
|
|
||||||
|
// discrete resize operation (from keyboard or mousewheel):
|
||||||
|
// we want to resize in nice steps, but finer grained than 2x at a time.
|
||||||
|
// so we resize at sqrt(2) at a time. to prevent rounding errors (so that
|
||||||
|
// when you size up and back down to 1:1, so it's really 1:1), we actually
|
||||||
|
// compute a 'zoom factor' that's log2, so zoom=0 is unzoomed, zoom=1 is
|
||||||
|
// zoomed 2x in each dimension.
|
||||||
void resize(int step)
|
void resize(int step)
|
||||||
{
|
{
|
||||||
// first characterize the current size relative to the raw size
|
|
||||||
int x = source->x, y = source->y;
|
int x = source->x, y = source->y;
|
||||||
|
|
||||||
float s;
|
float s;
|
||||||
int x2,y2;
|
int x2,y2;
|
||||||
int zoom=0;
|
int zoom=0; // this is log2(zoom_factor)*2; that is, a 0=1x, 2=2x, 4=4x, 6=8x
|
||||||
|
|
||||||
|
// if we have a current image, use that
|
||||||
if (cur) {
|
if (cur) {
|
||||||
|
// first characterize the current size relative to the raw size
|
||||||
|
// we do this by linearly probing possible values for zoom
|
||||||
|
// @TODO: refactor to combine these loops
|
||||||
if (cur->x > source->x + FRAME*2 || cur->y > source->y + FRAME*2) {
|
if (cur->x > source->x + FRAME*2 || cur->y > source->y + FRAME*2) {
|
||||||
for(;;) {
|
for(;;) {
|
||||||
s = (float) pow(2, zoom/2.0f + 0.25f);
|
s = (float) pow(2, zoom/2.0f + 0.25f);
|
||||||
@ -1409,6 +1434,7 @@ void resize(int step)
|
|||||||
y2 = int(y*s) + 2*FRAME;
|
y2 = int(y*s) + 2*FRAME;
|
||||||
} while (x2 == cur->x || y2 == cur->y);
|
} while (x2 == cur->x || y2 == cur->y);
|
||||||
} else {
|
} else {
|
||||||
|
// if no current image (e.g. an error), just resize relative to current in power-of-two steps
|
||||||
RECT rect;
|
RECT rect;
|
||||||
GetAdjustedWindowRect(win, &rect);
|
GetAdjustedWindowRect(win, &rect);
|
||||||
x2 = rect.right - rect.left;
|
x2 = rect.right - rect.left;
|
||||||
@ -1417,10 +1443,10 @@ void resize(int step)
|
|||||||
x2 <<= 1, y2 <<= 1;
|
x2 <<= 1, y2 <<= 1;
|
||||||
if (step < 0 && x2 >= 64 && y2 >= 64)
|
if (step < 0 && x2 >= 64 && y2 >= 64)
|
||||||
x2 >>= 1, y2 >>= 1;
|
x2 >>= 1, y2 >>= 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// compute top left to keep same center
|
||||||
RECT rect;
|
RECT rect;
|
||||||
GetAdjustedWindowRect(win, &rect);
|
GetAdjustedWindowRect(win, &rect);
|
||||||
x = (rect.left + rect.right)>>1;
|
x = (rect.left + rect.right)>>1;
|
||||||
@ -1433,6 +1459,7 @@ void resize(int step)
|
|||||||
display_mode = zoom==0 ? DISPLAY_actual : DISPLAY_current;
|
display_mode = zoom==0 ? DISPLAY_actual : DISPLAY_current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when mouse button is down, what mode are we in?
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
MODE_none,
|
MODE_none,
|
||||||
@ -1440,42 +1467,50 @@ enum
|
|||||||
MODE_resize,
|
MODE_resize,
|
||||||
} dragmode;
|
} dragmode;
|
||||||
|
|
||||||
#define setmode(x) (dragmode = x)
|
#define setmode(x) (dragmode = (x))
|
||||||
#define ismode(x) (dragmode == x)
|
#define ismode(x) (dragmode == (x))
|
||||||
#define anymode() !ismode(MODE_none)
|
#define anymode() !ismode(MODE_none)
|
||||||
|
|
||||||
static int ex,ey; // mousedown location relative to top left
|
static int ex,ey; // mousedown location relative to top left
|
||||||
static int ex2,ey2; // mousedown location relative to bottom right
|
static int ex2,ey2; // mousedown location relative to bottom right
|
||||||
static int wx,wy;
|
static int rx,ry; // sides that are being resized
|
||||||
static int rx,ry,rx2,ry2;
|
|
||||||
|
|
||||||
|
// compute the borders to the resize-vs-move regions (e.g. for mouse cursor)
|
||||||
static void cursor_regions(int *x0, int *y0, int *x1, int *y1)
|
static void cursor_regions(int *x0, int *y0, int *x1, int *y1)
|
||||||
{
|
{
|
||||||
RECT rect;
|
RECT rect;
|
||||||
int w,h,w2,h2;
|
int w,h,w2;
|
||||||
GetWindowRect(win, &rect);
|
GetClientRect(win, &rect);
|
||||||
w = rect.right - rect.left;
|
// client dimensions
|
||||||
h = rect.bottom - rect.top;
|
w = rect.right;
|
||||||
|
h = rect.bottom;
|
||||||
|
|
||||||
|
// size of resize regions is identical in both axes, so
|
||||||
|
// use smaller window size
|
||||||
|
w = stb_min(w,h);
|
||||||
|
|
||||||
// compute size of handles
|
// compute size of handles
|
||||||
w2 = w >> 4; h2 = h >> 4;
|
// this is a pretty ad-hoc logic that is designed to:
|
||||||
if (w2 < 12) {
|
// - make them bigger with bigger windows, so easier to grab
|
||||||
w2 = w >> 2;
|
// - but not too big
|
||||||
if (w2 < 4) w2 = w >> 1;
|
// - make them smaller with smaller windows, so there's still an ample 'move' region
|
||||||
} else if (w2 > 100) w2 = 100;
|
// - but not too small
|
||||||
if (h2 < 12) {
|
if (w < 16) w2 = w >> 1;
|
||||||
h2 = h >> 2;
|
else if (w < 200) w2 = w >> 2;
|
||||||
if (h2 < 4) h2 = h >> 1;
|
else if (w < 800) w2 = w >> 3;
|
||||||
} else if (h2 > 100) h2 = 100;
|
else if (w < 1600) w2 = w >> 4;
|
||||||
if (h2 < w2) w2 = h2;
|
else w2 = 100;
|
||||||
if (w2 < h2) h2 = w2;
|
|
||||||
*x0 = w2;
|
*x0 = w2;
|
||||||
*x1 = w - w2;
|
*x1 = w - w2;
|
||||||
*y0 = h2;
|
*y0 = w2;
|
||||||
*y1 = h - h2;
|
*y1 = h - w2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static cursor cache of standard windows cursor for resizing
|
||||||
HCURSOR c_def, c_ne_sw, c_e_w, c_nw_se, c_n_s;
|
HCURSOR c_def, c_ne_sw, c_e_w, c_nw_se, c_n_s;
|
||||||
|
|
||||||
|
// given the cursor position in client coordinates, set the cursor shape
|
||||||
void set_cursor(int x, int y)
|
void set_cursor(int x, int y)
|
||||||
{
|
{
|
||||||
int x0,y0,x1,y1;
|
int x0,y0,x1,y1;
|
||||||
@ -1489,27 +1524,35 @@ void set_cursor(int x, int y)
|
|||||||
else SetCursor(c_def);
|
else SetCursor(c_def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all windows mouse messages that involve the cursor position route here
|
||||||
|
// (i.e. all mouse messages except mousewheel)
|
||||||
void mouse(UINT ev, int x, int y)
|
void mouse(UINT ev, int x, int y)
|
||||||
{
|
{
|
||||||
switch (ev) {
|
switch (ev) {
|
||||||
case WM_LBUTTONDBLCLK:
|
case WM_LBUTTONDBLCLK:
|
||||||
toggle_display();
|
toggle_display();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_LBUTTONDOWN:
|
case WM_LBUTTONDOWN:
|
||||||
|
// if we're not in drag/size (and how could we be?!?)
|
||||||
if (!anymode()) {
|
if (!anymode()) {
|
||||||
RECT rect;
|
RECT rect;
|
||||||
int x0,y0,x1,y1;
|
int x0,y0,x1,y1;
|
||||||
|
|
||||||
|
// determine which region the user clicked in; there are 9 'quadrants',
|
||||||
|
// three in each dimension, so we measure each dimension separately
|
||||||
cursor_regions(&x0,&y0,&x1,&y1);
|
cursor_regions(&x0,&y0,&x1,&y1);
|
||||||
rx = ry = 0;
|
rx = (x < x0) ? -1 : (x > x1);
|
||||||
if (x < x0) rx = -1;
|
ry = (y < y0) ? -1 : (y > y1);
|
||||||
if (x > x1) rx = 1;
|
// if the middle region it's a drag; any other region is a resize
|
||||||
if (y < y0) ry = -1;
|
|
||||||
if (y > y1) ry = 1;
|
|
||||||
if (rx || ry)
|
if (rx || ry)
|
||||||
setmode(MODE_resize);
|
setmode(MODE_resize);
|
||||||
else
|
else
|
||||||
setmode(MODE_drag);
|
setmode(MODE_drag);
|
||||||
|
// capture the mouse until they let go
|
||||||
SetCapture(win);
|
SetCapture(win);
|
||||||
|
// record the position of the mouse cursor relative to the window
|
||||||
|
// sides, so we can resize properly
|
||||||
GetAdjustedWindowRect(win, &rect);
|
GetAdjustedWindowRect(win, &rect);
|
||||||
ex = x;
|
ex = x;
|
||||||
ey = y;
|
ey = y;
|
||||||
@ -1517,48 +1560,64 @@ void mouse(UINT ev, int x, int y)
|
|||||||
ey2 = y - (rect.bottom-rect.top);
|
ey2 = y - (rect.bottom-rect.top);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_MOUSEMOVE:
|
case WM_MOUSEMOVE:
|
||||||
switch(dragmode) {
|
switch(dragmode) {
|
||||||
default: assert(0);
|
default: assert(0);
|
||||||
case MODE_none:
|
case MODE_none:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// in drag mode, a mouse move just moves the window
|
||||||
case MODE_drag: {
|
case MODE_drag: {
|
||||||
RECT rect;
|
RECT rect;
|
||||||
GetWindowRect(win, &rect);
|
GetWindowRect(win, &rect);
|
||||||
MoveWindow(win, rect.left + x-ex, rect.top + y-ey, rect.right - rect.left, rect.bottom - rect.top, TRUE);
|
MoveWindow(win, rect.left + x-ex, rect.top + y-ey, rect.right - rect.left, rect.bottom - rect.top, TRUE);
|
||||||
set_cursor(x,y);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
case MODE_resize: {
|
case MODE_resize: {
|
||||||
RECT rect;
|
RECT rect;
|
||||||
GetAdjustedWindowRect(win, &rect);
|
|
||||||
assert(rx || ry);
|
assert(rx || ry);
|
||||||
display_mode = DISPLAY_current;
|
|
||||||
|
|
||||||
|
GetAdjustedWindowRect(win, &rect);
|
||||||
|
display_mode = DISPLAY_current; // resizing the window forces it out of 'actual' mode
|
||||||
|
|
||||||
|
// "LIMIT" controls how small a window can be in each dimension
|
||||||
#define LIMIT 16
|
#define LIMIT 16
|
||||||
if (rx < 0) rect.left = stb_min(rect.left+x-ex, rect.right-LIMIT);
|
|
||||||
if (rx > 0) rect.right = stb_max(rect.left+LIMIT, rect.left+x-ex2);
|
// for each direction we're resizing, compute the new position of that edge
|
||||||
if (ry < 0) rect.top = stb_min(rect.top+y-ey, rect.bottom-LIMIT);
|
if (rx < 0) rect.left = stb_min(rect.left+x-ex , rect.right - LIMIT);
|
||||||
if (ry > 0) rect.bottom = stb_max(rect.top+LIMIT, rect.top+y-ey2);
|
if (rx > 0) rect.right = stb_max(rect.left+x-ex2, rect.left + LIMIT);
|
||||||
|
if (ry < 0) rect.top = stb_min(rect.top +y-ey , rect.bottom - LIMIT);
|
||||||
|
if (ry > 0) rect.bottom = stb_max(rect.top +y-ey2, rect.top + LIMIT);
|
||||||
|
|
||||||
|
// then force the window to resize to the new rect
|
||||||
enqueue_resize(rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top);
|
enqueue_resize(rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_RBUTTONUP:
|
case WM_RBUTTONUP:
|
||||||
|
// right mouse click when not modal exits
|
||||||
if (!anymode())
|
if (!anymode())
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
// otherwise, disrupt a modal operation
|
// otherwise, disrupt a modal operation
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
case WM_LBUTTONUP:
|
case WM_LBUTTONUP:
|
||||||
ReleaseCapture();
|
ReleaseCapture();
|
||||||
setmode(MODE_none);
|
setmode(MODE_none);
|
||||||
set_cursor(x,y);
|
set_cursor(x,y); // return cursor to normal setting
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int physmem;
|
static unsigned int physmem; // available physical memory according to GlobalMemoryStatus
|
||||||
|
|
||||||
char *reg_root = "Software\\SilverSpaceship\\imv";
|
char *reg_root = "Software\\SilverSpaceship\\imv";
|
||||||
int reg_get(char *str, void *data, int len)
|
int reg_get(char *str, void *data, int len)
|
||||||
{
|
{
|
||||||
@ -1591,12 +1650,16 @@ int reg_set(char *str, void *data, int len)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we use very short strings for these to avoid wasting space, since
|
||||||
|
// people shouldn't be mucking with them directly anyway!
|
||||||
void reg_save(void)
|
void reg_save(void)
|
||||||
{
|
{
|
||||||
int temp = max_cache_bytes >> 20;
|
int temp = max_cache_bytes >> 20;
|
||||||
reg_set("ac", &alpha_background, 6);
|
reg_set("ac", &alpha_background, 6);
|
||||||
reg_set("up", &upsample_cubic, 4);
|
reg_set("up", &upsample_cubic, 4);
|
||||||
reg_set("cache", &temp, 4);
|
reg_set("cache", &temp, 4);
|
||||||
|
reg_set("lfs", &label_font_height, 4);
|
||||||
|
reg_set("label", &show_label, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_load(void)
|
void reg_load(void)
|
||||||
@ -1604,16 +1667,15 @@ void reg_load(void)
|
|||||||
int temp;
|
int temp;
|
||||||
reg_get("ac", &alpha_background, 6);
|
reg_get("ac", &alpha_background, 6);
|
||||||
reg_get("up", &upsample_cubic, 4);
|
reg_get("up", &upsample_cubic, 4);
|
||||||
|
reg_get("lfs", &label_font_height, 4);
|
||||||
|
reg_get("label", &show_label, 4);
|
||||||
if (reg_get("cache", &temp, 4))
|
if (reg_get("cache", &temp, 4))
|
||||||
max_cache_bytes = temp << 20;
|
max_cache_bytes = temp << 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HWND dialog;
|
static HWND dialog; // preferences dialog
|
||||||
static LRESULT send_dialog(int id, UINT msg, WPARAM p1, LPARAM p2)
|
|
||||||
{
|
|
||||||
return SendMessage(GetDlgItem(dialog,id),msg,p1,p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// set an edit control's text from an integer
|
||||||
static void set_dialog_number(int id, int value)
|
static void set_dialog_number(int id, int value)
|
||||||
{
|
{
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
@ -1621,6 +1683,7 @@ static void set_dialog_number(int id, int value)
|
|||||||
SetWindowText(GetDlgItem(dialog, id), buffer);
|
SetWindowText(GetDlgItem(dialog, id), buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get an edit control's text as an integer
|
||||||
static int get_dialog_number(int id)
|
static int get_dialog_number(int id)
|
||||||
{
|
{
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
@ -1629,6 +1692,8 @@ static int get_dialog_number(int id)
|
|||||||
return atoi(buffer);
|
return atoi(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clamp an edit control's text into an integer range (and
|
||||||
|
// remove non-integer chacters)
|
||||||
static void dialog_clamp(int id, int low, int high)
|
static void dialog_clamp(int id, int low, int high)
|
||||||
{
|
{
|
||||||
int x = get_dialog_number(id);
|
int x = get_dialog_number(id);
|
||||||
@ -1638,39 +1703,52 @@ static void dialog_clamp(int id, int low, int high)
|
|||||||
set_dialog_number(id,x);
|
set_dialog_number(id,x);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern unsigned char *rom_images[];
|
extern unsigned char *rom_images[]; // preference images
|
||||||
|
|
||||||
|
// preferences dialog windows procedure
|
||||||
BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam)
|
BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam)
|
||||||
{
|
{
|
||||||
static Image *pref_image;
|
static Image *pref_image;
|
||||||
int i;
|
int i;
|
||||||
dialog = hdlg;
|
|
||||||
|
dialog = hdlg; // store dialog handle to not pass it to above functions
|
||||||
|
|
||||||
switch(imsg)
|
switch(imsg)
|
||||||
{
|
{
|
||||||
case WM_INITDIALOG: {
|
case WM_INITDIALOG: {
|
||||||
|
// pick a random preference image
|
||||||
int n = ((rand() >> 6) % 3);
|
int n = ((rand() >> 6) % 3);
|
||||||
{
|
|
||||||
int x,y,i,j,k;
|
int x,y,i,j,k;
|
||||||
|
// decode it
|
||||||
uint8 *data = stbi_load_from_memory(rom_images[n],2000,&x,&y,NULL,1);
|
uint8 *data = stbi_load_from_memory(rom_images[n],2000,&x,&y,NULL,1);
|
||||||
pref_image = bmp_alloc(x,y);
|
pref_image = bmp_alloc(x,y);
|
||||||
|
if (data && pref_image) {
|
||||||
|
// convert monochrome to BGR
|
||||||
for (j=0; j < y; ++j)
|
for (j=0; j < y; ++j)
|
||||||
for (i=0; i < x; ++i)
|
for (i=0; i < x; ++i)
|
||||||
for (k=0; k < 3; ++k)
|
for (k=0; k < 3; ++k)
|
||||||
pref_image->pixels[pref_image->stride*j + BPP*i + k] = data[j*x+i];
|
pref_image->pixels[pref_image->stride*j + BPP*i + k] = data[j*x+i];
|
||||||
}
|
}
|
||||||
|
|
||||||
send_dialog(DIALOG_upsample, BM_SETCHECK, upsample_cubic, 0);
|
// copy preferences into dialog
|
||||||
|
SendMessage(GetDlgItem(hdlg, DIALOG_upsample), BM_SETCHECK, upsample_cubic, 0);
|
||||||
|
SendMessage(GetDlgItem(hdlg, DIALOG_showlabel), BM_SETCHECK, show_label, 0);
|
||||||
for (i=0; i < 6; ++i)
|
for (i=0; i < 6; ++i)
|
||||||
set_dialog_number(DIALOG_r1+i, alpha_background[0][i]);
|
set_dialog_number(DIALOG_r1+i, alpha_background[0][i]);
|
||||||
set_dialog_number(DIALOG_cachesize, max_cache_bytes >> 20);
|
set_dialog_number(DIALOG_cachesize, max_cache_bytes >> 20);
|
||||||
|
set_dialog_number(DIALOG_labelheight, label_font_height);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
case WM_PAINT: {
|
case WM_PAINT: {
|
||||||
|
// draw the preferences image
|
||||||
RECT z;
|
RECT z;
|
||||||
int x,y;
|
int x,y;
|
||||||
HWND pic = GetDlgItem(hdlg, DIALOG_image);
|
HWND pic = GetDlgItem(hdlg, DIALOG_image);
|
||||||
GetWindowRect(pic,&z);
|
GetWindowRect(pic,&z);
|
||||||
|
// not clear why these next two lines work/are required, but it's what petzold does
|
||||||
InvalidateRect(pic,NULL,TRUE);
|
InvalidateRect(pic,NULL,TRUE);
|
||||||
UpdateWindow(pic);
|
UpdateWindow(pic);
|
||||||
|
// center it
|
||||||
x = (z.right - z.left - pref_image->x) >> 1;
|
x = (z.right - z.left - pref_image->x) >> 1;
|
||||||
y = (z.bottom - z.top - pref_image->y) >> 1;
|
y = (z.bottom - z.top - pref_image->y) >> 1;
|
||||||
platformDrawBitmap(GetDC(pic), x,y,pref_image->pixels,pref_image->x,pref_image->y,pref_image->stride,0);
|
platformDrawBitmap(GetDC(pic), x,y,pref_image->pixels,pref_image->x,pref_image->y,pref_image->stride,0);
|
||||||
@ -1680,7 +1758,8 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam)
|
|||||||
int k = LOWORD(wparam);
|
int k = LOWORD(wparam);
|
||||||
int n = HIWORD(wparam);
|
int n = HIWORD(wparam);
|
||||||
switch(k) {
|
switch(k) {
|
||||||
// validate the dialog entries
|
// validate the dialog entries into range and numeric only, when
|
||||||
|
// they lose focus only
|
||||||
case DIALOG_r1: case DIALOG_g1: case DIALOG_b1:
|
case DIALOG_r1: case DIALOG_g1: case DIALOG_b1:
|
||||||
case DIALOG_r2: case DIALOG_g2: case DIALOG_b2:
|
case DIALOG_r2: case DIALOG_g2: case DIALOG_b2:
|
||||||
if (n == EN_KILLFOCUS) dialog_clamp(k,0,255);
|
if (n == EN_KILLFOCUS) dialog_clamp(k,0,255);
|
||||||
@ -1688,15 +1767,24 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam)
|
|||||||
case DIALOG_cachesize:
|
case DIALOG_cachesize:
|
||||||
if (n == EN_KILLFOCUS) dialog_clamp(k,1,(physmem>>22)*3); // 3/4 of phys mem
|
if (n == EN_KILLFOCUS) dialog_clamp(k,1,(physmem>>22)*3); // 3/4 of phys mem
|
||||||
break;
|
break;
|
||||||
|
case DIALOG_labelheight:
|
||||||
|
if (n == EN_KILLFOCUS) dialog_clamp(k,1,200);
|
||||||
|
break;
|
||||||
|
|
||||||
case IDOK: {
|
case IDOK: {
|
||||||
|
// user clicked ok... copy out current values to check for changes
|
||||||
unsigned char cur[6];
|
unsigned char cur[6];
|
||||||
int up = upsample_cubic;
|
|
||||||
memcpy(cur, alpha_background, 6);
|
memcpy(cur, alpha_background, 6);
|
||||||
|
|
||||||
// load the settings back out of the dialog box
|
// load the settings back out of the dialog box
|
||||||
for (i=0; i < 6; ++i)
|
for (i=0; i < 6; ++i)
|
||||||
alpha_background[0][i] = get_dialog_number(DIALOG_r1+i);
|
alpha_background[0][i] = get_dialog_number(DIALOG_r1+i);
|
||||||
max_cache_bytes = get_dialog_number(DIALOG_cachesize) << 20;
|
max_cache_bytes = get_dialog_number(DIALOG_cachesize) << 20;
|
||||||
upsample_cubic = send_dialog(DIALOG_upsample, BM_GETCHECK,0,0) == BST_CHECKED;
|
label_font_height = get_dialog_number(DIALOG_labelheight);
|
||||||
|
upsample_cubic = BST_CHECKED == SendMessage(GetDlgItem(hdlg,DIALOG_upsample ), BM_GETCHECK,0,0);
|
||||||
|
show_label = BST_CHECKED == SendMessage(GetDlgItem(hdlg,DIALOG_showlabel), BM_GETCHECK,0,0);
|
||||||
|
|
||||||
|
// if alpha_background changed, clear the cache of any images that used it
|
||||||
if (memcmp(alpha_background, cur, 6)) {
|
if (memcmp(alpha_background, cur, 6)) {
|
||||||
stb_mutex_begin(cache_mutex);
|
stb_mutex_begin(cache_mutex);
|
||||||
for (i=0; i < MAX_CACHED_IMAGES; ++i) {
|
for (i=0; i < MAX_CACHED_IMAGES; ++i) {
|
||||||
@ -1712,32 +1800,51 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stb_mutex_end(cache_mutex);
|
stb_mutex_end(cache_mutex);
|
||||||
|
// force a reload of the current image
|
||||||
advance(0);
|
advance(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save the data out to the registry
|
||||||
reg_save();
|
reg_save();
|
||||||
|
|
||||||
|
// rebuild the label font
|
||||||
|
build_label_font();
|
||||||
|
|
||||||
|
// redraw window -- only needed if changed label state, but we can live with always
|
||||||
|
InvalidateRect(win, NULL, FALSE);
|
||||||
|
|
||||||
/* FALL THROUGH */
|
/* FALL THROUGH */
|
||||||
}
|
}
|
||||||
case IDCANCEL:
|
case IDCANCEL:
|
||||||
imfree(pref_image);
|
|
||||||
pref_image = NULL;
|
|
||||||
EndDialog(hdlg,0);
|
EndDialog(hdlg,0);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case WM_DESTROY:
|
||||||
|
// we're closing the dialog, so clear the cached image
|
||||||
|
imfree(pref_image);
|
||||||
|
pref_image = NULL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// missing VK definitions in old compiler
|
||||||
#ifndef VK_OEM_PLUS
|
#ifndef VK_OEM_PLUS
|
||||||
#define VK_OEM_PLUS 0xbb
|
#define VK_OEM_PLUS 0xbb
|
||||||
#define VK_OEM_MINUS 0xbd
|
#define VK_OEM_MINUS 0xbd
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef VK_SLASH
|
#ifndef VK_SLASH
|
||||||
#define VK_SLASH 0xbf
|
#define VK_SLASH 0xbf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ok, surely windows doesn't BY DESIGN require you to store your
|
||||||
|
// HINSTANCE in a global, does it? but I couldn't find a 'GetCurrentInstance'
|
||||||
|
// or some such to tell you what instance a thread came from. But the
|
||||||
|
// HINSTANCE is needed to launch the preferences dialog. Oh well!
|
||||||
HINSTANCE inst;
|
HINSTANCE inst;
|
||||||
|
|
||||||
int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
@ -1751,6 +1858,11 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
case WM_APP_LOAD_ERROR:
|
case WM_APP_LOAD_ERROR:
|
||||||
case WM_APP_DECODE_ERROR:
|
case WM_APP_DECODE_ERROR:
|
||||||
{
|
{
|
||||||
|
// if the load/decode threads get an error, they send this message
|
||||||
|
// to make sure the main thread gets woken up. then we have to decide
|
||||||
|
// whether to show it or not; we do that by scanning the whole cache
|
||||||
|
// to see what the most recently-browsed-and-displayable image is,
|
||||||
|
// and store that in 'best'.
|
||||||
int best_lru=0,i;
|
int best_lru=0,i;
|
||||||
volatile ImageFile *best = NULL;
|
volatile ImageFile *best = NULL;
|
||||||
for (i=0; i < MAX_CACHED_IMAGES; ++i) {
|
for (i=0; i < MAX_CACHED_IMAGES; ++i) {
|
||||||
@ -1763,14 +1875,17 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (best->status == LOAD_error_reading || best->status == LOAD_error_decoding) {
|
// if the most recently-browsed and displayable image is an error, show it
|
||||||
|
if (best->status == LOAD_error_reading || best->status == LOAD_error_decoding)
|
||||||
set_error(best);
|
set_error(best);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_APP_DECODED: {
|
case WM_APP_DECODED: {
|
||||||
// scan the filelist for the highest-lru, decoded image
|
// if the decode thread finishes, it sends us this message. note that
|
||||||
|
// we skip files that had an error; but we use a global variable for 'best_lru'
|
||||||
|
// so we won't ever retreat. I'm not sure how this really interacts with
|
||||||
|
// the above loop, though. maybe they should be combined.
|
||||||
int i;
|
int i;
|
||||||
ImageFile *best = NULL;
|
ImageFile *best = NULL;
|
||||||
for (i=0; i < stb_arr_len(fileinfo); ++i) {
|
for (i=0; i < stb_arr_len(fileinfo); ++i) {
|
||||||
@ -1787,13 +1902,15 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
o(("Post-decode, found a best image, better than any before.\n"));
|
o(("Post-decode, found a best image, better than any before.\n"));
|
||||||
update_source(best);
|
update_source(best);
|
||||||
}
|
}
|
||||||
|
// since we've decoded a new image, our cache might be too big,
|
||||||
|
// so try flushing it
|
||||||
flush_cache(FALSE);
|
flush_cache(FALSE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_MOUSEWHEEL: {
|
case WM_MOUSEWHEEL: {
|
||||||
int zdelta = (short) HIWORD(wParam);
|
int zdelta = (short) HIWORD(wParam);
|
||||||
// ignore scaling factor and step 1 by 1
|
// ignore wheel scaling factor and step 1 by 1
|
||||||
if (zdelta > 0) resize(1);
|
if (zdelta > 0) resize(1);
|
||||||
if (zdelta < 0) resize(-1);
|
if (zdelta < 0) resize(-1);
|
||||||
break;
|
break;
|
||||||
@ -1809,6 +1926,7 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case WM_SETCURSOR: {
|
case WM_SETCURSOR: {
|
||||||
|
// there's no GetCursorPos for the client window?
|
||||||
POINT p;
|
POINT p;
|
||||||
if (GetCursorPos(&p)) {
|
if (GetCursorPos(&p)) {
|
||||||
RECT rect;
|
RECT rect;
|
||||||
@ -1911,6 +2029,7 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
resize(-1);
|
resize(-1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'O':
|
||||||
case MY_CTRL | 'O':
|
case MY_CTRL | 'O':
|
||||||
open_file();
|
open_file();
|
||||||
break;
|
break;
|
||||||
@ -1934,8 +2053,11 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// number of threads to use in resizer
|
||||||
|
#define MAX_RESIZE 4
|
||||||
int resize_threads;
|
int resize_threads;
|
||||||
|
|
||||||
|
// whether 'cur' (the resized image currently displayed) actually comes from 'source'
|
||||||
int cur_is_current(void)
|
int cur_is_current(void)
|
||||||
{
|
{
|
||||||
if (!cur_filename) return FALSE;
|
if (!cur_filename) return FALSE;
|
||||||
@ -1943,43 +2065,41 @@ int cur_is_current(void)
|
|||||||
return !strcmp(cur_filename, source_c->filename);
|
return !strcmp(cur_filename, source_c->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_RESIZE 4
|
|
||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||||
{
|
{
|
||||||
char filenamebuffer[4096];
|
|
||||||
int argc;
|
int argc;
|
||||||
char **argv = stb_tokens_quoted(lpCmdLine, " ", &argc);
|
char **argv = stb_tokens_quoted(lpCmdLine, " ", &argc);
|
||||||
|
char filenamebuffer[4096];
|
||||||
|
|
||||||
MEMORYSTATUS mem;
|
MEMORYSTATUS mem;
|
||||||
MSG msg;
|
MSG msg;
|
||||||
WNDCLASSEX wndclass;
|
WNDCLASSEX wndclass;
|
||||||
HWND hWnd;
|
HWND hWnd;
|
||||||
|
|
||||||
int image_x, image_y;
|
// initial loaded image
|
||||||
|
int image_x, image_y, image_n;
|
||||||
unsigned char *image_data;
|
unsigned char *image_data;
|
||||||
int image_n;
|
|
||||||
|
|
||||||
inst = hInstance;
|
inst = hInstance;
|
||||||
|
|
||||||
resize_threads = stb_processor_count();
|
// concatenate the version number onto the help text, because
|
||||||
|
// we can't do this statically with the current build process
|
||||||
|
strcat(helptext_center, VERSION);
|
||||||
|
|
||||||
if (resize_threads > MAX_RESIZE) resize_threads = MAX_RESIZE;
|
// determine the number of threads to use in the resizer
|
||||||
|
resize_threads = stb_min(stb_processor_count(), MAX_RESIZE);
|
||||||
#ifdef _DEBUG
|
|
||||||
do_debug = IsDebuggerPresent();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
// compute the amount of physical memory to set a guess for the cache size
|
||||||
GlobalMemoryStatus(&mem);
|
GlobalMemoryStatus(&mem);
|
||||||
if (mem.dwTotalPhys == 0) --mem.dwTotalPhys;
|
if (mem.dwTotalPhys == 0) --mem.dwTotalPhys;
|
||||||
physmem = mem.dwTotalPhys;
|
physmem = mem.dwTotalPhys;
|
||||||
max_cache_bytes = physmem / 6;
|
max_cache_bytes = physmem / 6;
|
||||||
if (max_cache_bytes > 256 << 20) max_cache_bytes = 256 << 20;
|
if (max_cache_bytes > 256 << 20) max_cache_bytes = 256 << 20;
|
||||||
|
|
||||||
|
// load the registry preferences, if they're there (AFTER the above)
|
||||||
reg_load();
|
reg_load();
|
||||||
|
|
||||||
strcat(helptext_center, VERSION);
|
// create the main window class
|
||||||
|
|
||||||
/* Register the frame class */
|
|
||||||
memset(&wndclass, 0, sizeof(wndclass));
|
memset(&wndclass, 0, sizeof(wndclass));
|
||||||
wndclass.cbSize = sizeof(wndclass);
|
wndclass.cbSize = sizeof(wndclass);
|
||||||
wndclass.style = CS_OWNDC | CS_DBLCLKS;
|
wndclass.style = CS_OWNDC | CS_DBLCLKS;
|
||||||
@ -1991,27 +2111,22 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
wndclass.lpszMenuName = szAppName;
|
wndclass.lpszMenuName = szAppName;
|
||||||
wndclass.lpszClassName = szAppName;
|
wndclass.lpszClassName = szAppName;
|
||||||
wndclass.hIconSm = LoadIcon(hInstance, szAppName);
|
wndclass.hIconSm = LoadIcon(hInstance, szAppName);
|
||||||
|
if (!RegisterClassEx(&wndclass))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// cache the cursors
|
||||||
c_def = LoadCursor(NULL, IDC_ARROW);
|
c_def = LoadCursor(NULL, IDC_ARROW);
|
||||||
c_nw_se = LoadCursor(NULL, IDC_SIZENWSE);
|
c_nw_se = LoadCursor(NULL, IDC_SIZENWSE);
|
||||||
c_ne_sw = LoadCursor(NULL, IDC_SIZENESW);
|
c_ne_sw = LoadCursor(NULL, IDC_SIZENESW);
|
||||||
c_e_w = LoadCursor(NULL, IDC_SIZEWE);
|
c_e_w = LoadCursor(NULL, IDC_SIZEWE);
|
||||||
c_n_s = LoadCursor(NULL, IDC_SIZENS);
|
c_n_s = LoadCursor(NULL, IDC_SIZENS);
|
||||||
|
|
||||||
if (!RegisterClassEx(&wndclass))
|
build_label_font();
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
{
|
|
||||||
LOGFONT lf;
|
|
||||||
memset(&lf, 0, sizeof(lf));
|
|
||||||
lf.lfHeight = 12;
|
|
||||||
lf.lfOutPrecision = OUT_TT_PRECIS; // prefer truetype to raster fonts
|
|
||||||
strcpy(lf.lfFaceName, "Times New Roman");
|
|
||||||
label_font = CreateFontIndirect(&lf);
|
|
||||||
}
|
|
||||||
|
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
|
// if run with no arguments, get an initial filename
|
||||||
OPENFILENAME o;
|
OPENFILENAME o;
|
||||||
memset(&o, 0, sizeof(o));
|
memset(&o, 0, sizeof(o));
|
||||||
o.lStructSize = sizeof(o);
|
o.lStructSize = sizeof(o);
|
||||||
@ -2023,31 +2138,48 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
return 0;
|
return 0;
|
||||||
filename = filenamebuffer;
|
filename = filenamebuffer;
|
||||||
} else {
|
} else {
|
||||||
|
// else grab the first one... what about additional names? should
|
||||||
|
// we launch more windows? or initialize the filelist to them, I guess?
|
||||||
filename = argv[0];
|
filename = argv[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allocate worker threads
|
||||||
resize_workers = stb_workq_new(resize_threads, resize_threads * 4);
|
resize_workers = stb_workq_new(resize_threads, resize_threads * 4);
|
||||||
cache_mutex = stb_mutex_new();
|
|
||||||
disk_command_queue = stb_sem_new(1,1);
|
|
||||||
decode_queue = stb_sem_new(1,1);
|
|
||||||
decode_mutex = stb_mutex_new();
|
|
||||||
|
|
||||||
|
// load initial image
|
||||||
image_data = stbi_load(filename, &image_x, &image_y, &image_n, BPP);
|
image_data = stbi_load(filename, &image_x, &image_y, &image_n, BPP);
|
||||||
if (image_data == NULL) {
|
if (image_data == NULL) {
|
||||||
|
// we treat errors on initial image differently: message box and exit...
|
||||||
|
// now that we handle errors nicely, this is kind of dumb... but what
|
||||||
|
// size should the initial window be?
|
||||||
char *why = stbi_failure_reason();
|
char *why = stbi_failure_reason();
|
||||||
char buffer[512];
|
char buffer[512];
|
||||||
sprintf(buffer, "'%s': %s", filename, why);
|
sprintf(buffer, "'%s': %s", filename, why);
|
||||||
error(buffer);
|
error(buffer);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fix the filename & path for consistency with readdir()
|
||||||
stb_fixpath(filename);
|
stb_fixpath(filename);
|
||||||
|
// extract just the path
|
||||||
stb_splitpath(path_to_file, filename, STB_PATH);
|
stb_splitpath(path_to_file, filename, STB_PATH);
|
||||||
|
|
||||||
|
// allocate semaphores / mutexes
|
||||||
|
cache_mutex = stb_mutex_new();
|
||||||
|
decode_mutex = stb_mutex_new();
|
||||||
|
decode_queue = stb_sem_new(1,1);
|
||||||
|
disk_command_queue = stb_sem_new(1,1);
|
||||||
|
|
||||||
|
// go ahead and start the other tasks
|
||||||
stb_create_thread(diskload_task, NULL);
|
stb_create_thread(diskload_task, NULL);
|
||||||
stb_create_thread(decode_task, NULL);
|
stb_create_thread(decode_task, NULL);
|
||||||
|
|
||||||
|
// create the source image by converting the image data to BGR,
|
||||||
|
// pre-blending alpha
|
||||||
source = malloc(sizeof(*source));
|
source = malloc(sizeof(*source));
|
||||||
make_image(source, image_x, image_y, image_data, image_n);
|
make_image(source, image_x, image_y, image_data, image_n);
|
||||||
|
|
||||||
|
// create a cache entry in case they start browsing later
|
||||||
cache[0].status = LOAD_available;
|
cache[0].status = LOAD_available;
|
||||||
cache[0].image = source;
|
cache[0].image = source;
|
||||||
cache[0].lru = lru_stamp++;
|
cache[0].lru = lru_stamp++;
|
||||||
@ -2056,13 +2188,15 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
stb_sdict_add(file_cache, filename, (void *) &cache[0]);
|
stb_sdict_add(file_cache, filename, (void *) &cache[0]);
|
||||||
source_c = (ImageFile *) &cache[0];
|
source_c = (ImageFile *) &cache[0];
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
int x,y;
|
int x,y;
|
||||||
int w2 = source->x+FRAME*2, h2 = source->y+FRAME*2;
|
int w2 = source->x+FRAME*2, h2 = source->y+FRAME*2;
|
||||||
int w,h;
|
int w,h;
|
||||||
|
|
||||||
|
// determine an initial window size, and resize
|
||||||
ideal_window_size(w2,h2, &w,&h, &x,&y);
|
ideal_window_size(w2,h2, &w,&h, &x,&y);
|
||||||
|
|
||||||
|
// if the size exactly matches, don't resize, just copy
|
||||||
if (w == source->x+FRAME*2 && h == source->y+FRAME*2) {
|
if (w == source->x+FRAME*2 && h == source->y+FRAME*2) {
|
||||||
display_error[0] = 0;
|
display_error[0] = 0;
|
||||||
cur = bmp_alloc(image_x + FRAME*2, image_y + FRAME*2);
|
cur = bmp_alloc(image_x + FRAME*2, image_y + FRAME*2);
|
||||||
@ -2086,25 +2220,26 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
}
|
}
|
||||||
cur_filename = strdup(filename);
|
cur_filename = strdup(filename);
|
||||||
|
|
||||||
wx = w;
|
// create the window
|
||||||
wy = h;
|
|
||||||
hWnd = CreateWindow(szAppName, displayName,
|
hWnd = CreateWindow(szAppName, displayName,
|
||||||
WS_POPUP,
|
WS_POPUP,
|
||||||
x,y, w, h,
|
x,y, w, h,
|
||||||
NULL, NULL, hInstance, NULL);
|
NULL, NULL, hInstance, NULL);
|
||||||
}
|
|
||||||
|
|
||||||
if (!hWnd)
|
if (!hWnd)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
} // open brace for defining some temporary variables
|
||||||
|
|
||||||
|
// display the window
|
||||||
ShowWindow(hWnd, nCmdShow);
|
ShowWindow(hWnd, nCmdShow);
|
||||||
UpdateWindow(hWnd);
|
UpdateWindow(hWnd);
|
||||||
InvalidateRect(hWnd, NULL, TRUE);
|
InvalidateRect(hWnd, NULL, TRUE);
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
// if we're not currently resizing, start a resize
|
// if we're not currently resizing, and there's a resize request
|
||||||
if (qs.w && pending_resize.size.w == 0) {
|
if (qs.w && pending_resize.size.w == 0) {
|
||||||
if (source) {
|
if (source) {
|
||||||
|
// is the image we're showing the image to resize, and does the size match?
|
||||||
if (cur_is_current() && (!cur || (qs.w == cur->x && qs.h >= cur->y) || (qs.h == cur->y && qs.w >= cur->x))) {
|
if (cur_is_current() && (!cur || (qs.w == cur->x && qs.h >= cur->y) || (qs.h == cur->y && qs.w >= cur->x))) {
|
||||||
// no resize necessary, just a variant of the current shape
|
// no resize necessary, just a variant of the current shape
|
||||||
MoveWindow(win, qs.x,qs.y,qs.w,qs.h, TRUE);
|
MoveWindow(win, qs.x,qs.y,qs.w,qs.h, TRUE);
|
||||||
@ -2118,38 +2253,61 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
qs.w = 0;
|
qs.w = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is currently done in a stupid way--we should have the resizer
|
||||||
|
// post us a message to wake us up, but I wrote this before I had
|
||||||
|
// that infrastructure worked out
|
||||||
|
|
||||||
if (!PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE)) {
|
if (!PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE)) {
|
||||||
// no messages, so check for pending activity
|
// no messages, so check for a resize completing
|
||||||
if (pending_resize.size.w) {
|
if (pending_resize.size.w) {
|
||||||
// there's a resize pending, so don't block
|
// there's a resize pending, so don't block
|
||||||
if (!pending_resize.image) {
|
if (!pending_resize.image) {
|
||||||
|
// resize isn't done, so sleep for a bit and try again
|
||||||
Sleep(10);
|
Sleep(10);
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
// resize is done
|
||||||
HDC hdc;
|
HDC hdc;
|
||||||
o(("Finished resize\n"));
|
o(("Finished resize\n"));
|
||||||
imfree(cur);
|
|
||||||
|
// reclaim ownership of the image from the resizer
|
||||||
pending_resize.image_c->status = LOAD_available;
|
pending_resize.image_c->status = LOAD_available;
|
||||||
|
|
||||||
|
// free the current image we're about to write over
|
||||||
|
imfree(cur);
|
||||||
cur = pending_resize.image;
|
cur = pending_resize.image;
|
||||||
display_error[0] = 0;
|
|
||||||
cur_filename = pending_resize.filename;
|
cur_filename = pending_resize.filename;
|
||||||
pending_resize.filename = NULL;
|
|
||||||
|
// clear error messages
|
||||||
|
display_error[0] = 0;
|
||||||
|
|
||||||
if (!show_frame) {
|
if (!show_frame) {
|
||||||
pending_resize.size.x += FRAME;
|
pending_resize.size.x += FRAME;
|
||||||
pending_resize.size.y += FRAME;
|
pending_resize.size.y += FRAME;
|
||||||
pending_resize.size.w -= FRAME*2;
|
pending_resize.size.w -= FRAME*2;
|
||||||
pending_resize.size.h -= FRAME*2;
|
pending_resize.size.h -= FRAME*2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resize the window
|
||||||
SetWindowPos(hWnd,NULL,pending_resize.size.x, pending_resize.size.y, pending_resize.size.w, pending_resize.size.h, SWP_NOZORDER);
|
SetWindowPos(hWnd,NULL,pending_resize.size.x, pending_resize.size.y, pending_resize.size.w, pending_resize.size.h, SWP_NOZORDER);
|
||||||
|
|
||||||
|
// clear the resize request info
|
||||||
|
pending_resize.filename = NULL;
|
||||||
barrier();
|
barrier();
|
||||||
pending_resize.size.w = 0;
|
pending_resize.size.w = 0;
|
||||||
|
|
||||||
|
// paint the window
|
||||||
hdc = GetDC(win);
|
hdc = GetDC(win);
|
||||||
display(hWnd, hdc);
|
display(hWnd, hdc);
|
||||||
ReleaseDC(win, hdc);
|
ReleaseDC(win, hdc);
|
||||||
}
|
|
||||||
|
// restart from the top
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can get rid of this with peek-with-remove, surely?
|
||||||
if (!GetMessage(&msg, NULL, 0, 0))
|
if (!GetMessage(&msg, NULL, 0, 0))
|
||||||
return msg.wParam;
|
return msg.wParam;
|
||||||
|
|
||||||
@ -2158,12 +2316,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
#define MAGIC (1.5 * (1 << 26) * (1 << 26))
|
//
|
||||||
double temp;
|
// Everything from here on down just does image resizing
|
||||||
#define FAST_FLOAT_TO_INT(x) ((q->temp = (x) + MAGIC), *(int *)&q->temp)
|
//
|
||||||
|
|
||||||
#define toint(x) ((int) (x)) // FAST_FLOAT_TO_INT(x)
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
short i;
|
short i;
|
||||||
|
BIN
imv.ico
BIN
imv.ico
Binary file not shown.
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 2.2 KiB |
15
notes.txt
15
notes.txt
@ -1,11 +1,22 @@
|
|||||||
Version 0.57
|
Version 0.91: Beta 2 (2007-07-01)
|
||||||
|
* feature: allow changing the label font size
|
||||||
|
* internal: various refactorings to clean up the code
|
||||||
|
* bugfix: finish commenting code (except resizer)
|
||||||
|
* bugfix: fix tiny leak closing the preferences dialog with the close button
|
||||||
|
|
||||||
|
Version 0.90: Beta 1 (2007-06-30)
|
||||||
|
* bugfix: user-friendlier error messages
|
||||||
|
* feature: save preferences to registry
|
||||||
|
* feature: preferences dialog
|
||||||
|
|
||||||
|
Version 0.57 (2007-06-29)
|
||||||
* feature: cubic image resampling
|
* feature: cubic image resampling
|
||||||
* bugfix: advancing to pre-loaded image then retreated to previous image
|
* bugfix: advancing to pre-loaded image then retreated to previous image
|
||||||
* bugfix: occasional error when advancing to image that was about to be decoded
|
* bugfix: occasional error when advancing to image that was about to be decoded
|
||||||
* bugfix: commented about 75% of code
|
* bugfix: commented about 75% of code
|
||||||
* bugfix: fix logic for fitting large images onscreen to not stop a few pixels short
|
* bugfix: fix logic for fitting large images onscreen to not stop a few pixels short
|
||||||
|
|
||||||
Version 0.56
|
Version 0.56 (2007-06-27)
|
||||||
* bugfix: stb_image wouldn't load jpegtran output (which is invalid JFIF)
|
* bugfix: stb_image wouldn't load jpegtran output (which is invalid JFIF)
|
||||||
|
|
||||||
Version 0.55 (2007-06-27)
|
Version 0.55 (2007-06-27)
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
= Release =
|
= Release =
|
||||||
|
|
||||||
This is an alpha version, targetting the eventual milestones:
|
This is a beta version, targetting the eventual milestones:
|
||||||
|
|
||||||
version 0.90 - beta
|
|
||||||
|
|
||||||
version 1.00 - stable release
|
version 1.00 - stable release
|
||||||
|
|
||||||
|
64
pref.rc
64
pref.rc
@ -52,15 +52,15 @@ END
|
|||||||
// Dialog
|
// Dialog
|
||||||
//
|
//
|
||||||
|
|
||||||
IDD_pref DIALOG DISCARDABLE 0, 0, 161, 141
|
IDD_pref DIALOG DISCARDABLE 0, 0, 161, 171
|
||||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
CAPTION "imv(stb) preferences"
|
CAPTION "imv(stb) preferences"
|
||||||
FONT 8, "MS Sans Serif"
|
FONT 8, "MS Sans Serif"
|
||||||
BEGIN
|
BEGIN
|
||||||
DEFPUSHBUTTON "OK",IDOK,23,119,50,14
|
DEFPUSHBUTTON "OK",IDOK,23,149,50,14
|
||||||
PUSHBUTTON "Cancel",IDCANCEL,83,119,50,14
|
PUSHBUTTON "Cancel",IDCANCEL,83,149,50,14
|
||||||
CONTROL "High-quality resizing",DIALOG_upsample,"Button",
|
CONTROL "High-quality resizing",DIALOG_upsample,"Button",
|
||||||
BS_AUTOCHECKBOX | WS_TABSTOP,24,86,76,10
|
BS_AUTOCHECKBOX | WS_TABSTOP,23,116,76,10
|
||||||
EDITTEXT DIALOG_r1,57,50,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
EDITTEXT DIALOG_r1,57,50,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
||||||
EDITTEXT DIALOG_g1,79,50,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
EDITTEXT DIALOG_g1,79,50,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
||||||
EDITTEXT DIALOG_b1,101,50,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
EDITTEXT DIALOG_b1,101,50,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
||||||
@ -71,10 +71,14 @@ BEGIN
|
|||||||
EDITTEXT DIALOG_r2,57,63,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
EDITTEXT DIALOG_r2,57,63,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
||||||
EDITTEXT DIALOG_g2,79,63,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
EDITTEXT DIALOG_g2,79,63,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
||||||
EDITTEXT DIALOG_b2,101,63,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
EDITTEXT DIALOG_b2,101,63,19,12,ES_RIGHT | ES_AUTOHSCROLL
|
||||||
EDITTEXT DIALOG_cachesize,11,100,21,13,ES_AUTOHSCROLL
|
EDITTEXT DIALOG_cachesize,10,130,21,13,ES_AUTOHSCROLL
|
||||||
LTEXT "Size of image cache in megabytes",IDC_STATIC,36,102,107,
|
LTEXT "Size of image cache in megabytes",IDC_STATIC,35,133,107,
|
||||||
8
|
8
|
||||||
LTEXT "",DIALOG_image,45,7,70,28
|
LTEXT "",DIALOG_image,45,7,70,28
|
||||||
|
EDITTEXT DIALOG_labelheight,11,99,20,12,ES_AUTOHSCROLL
|
||||||
|
LTEXT "Label font height",IDC_STATIC,35,101,104,10
|
||||||
|
CONTROL "Show filename label",DIALOG_showlabel,"Button",
|
||||||
|
BS_AUTOCHECKBOX | WS_TABSTOP,23,87,120,10
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|
||||||
@ -91,7 +95,7 @@ BEGIN
|
|||||||
LEFTMARGIN, 7
|
LEFTMARGIN, 7
|
||||||
RIGHTMARGIN, 154
|
RIGHTMARGIN, 154
|
||||||
TOPMARGIN, 7
|
TOPMARGIN, 7
|
||||||
BOTTOMMARGIN, 134
|
BOTTOMMARGIN, 164
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
#endif // APSTUDIO_INVOKED
|
#endif // APSTUDIO_INVOKED
|
||||||
@ -105,6 +109,52 @@ END
|
|||||||
// Icon with lowest ID value placed first to ensure application icon
|
// Icon with lowest ID value placed first to ensure application icon
|
||||||
// remains consistent on all systems.
|
// remains consistent on all systems.
|
||||||
IDI_ICON1 ICON DISCARDABLE "imv.ico"
|
IDI_ICON1 ICON DISCARDABLE "imv.ico"
|
||||||
|
|
||||||
|
#ifndef _MAC
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Version
|
||||||
|
//
|
||||||
|
|
||||||
|
VS_VERSION_INFO VERSIONINFO
|
||||||
|
FILEVERSION 1,0,0,1
|
||||||
|
PRODUCTVERSION 1,0,0,1
|
||||||
|
FILEFLAGSMASK 0x3fL
|
||||||
|
#ifdef _DEBUG
|
||||||
|
FILEFLAGS 0x1L
|
||||||
|
#else
|
||||||
|
FILEFLAGS 0x0L
|
||||||
|
#endif
|
||||||
|
FILEOS 0x40004L
|
||||||
|
FILETYPE 0x1L
|
||||||
|
FILESUBTYPE 0x0L
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904b0"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Comments", "\0"
|
||||||
|
VALUE "CompanyName", " Sean Barrett / nothings.org\0"
|
||||||
|
VALUE "FileDescription", "imv(stb) image viewer\0"
|
||||||
|
VALUE "FileVersion", "1, 0, 0, 1\0"
|
||||||
|
VALUE "InternalName", "stb_imv\0"
|
||||||
|
VALUE "LegalCopyright", "GPL; Copyright © 2007\0"
|
||||||
|
VALUE "LegalTrademarks", "\0"
|
||||||
|
VALUE "OriginalFilename", "stb_imv.exe\0"
|
||||||
|
VALUE "PrivateBuild", "\0"
|
||||||
|
VALUE "ProductName", "imv(stb)\0"
|
||||||
|
VALUE "ProductVersion", "1, 0, 0, 1\0"
|
||||||
|
VALUE "SpecialBuild", "\0"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x409, 1200
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // !_MAC
|
||||||
|
|
||||||
#endif // English (U.S.) resources
|
#endif // English (U.S.) resources
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ RSC=rc.exe
|
|||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
|
||||||
# ADD CPP /nologo /G6 /MD /Ze /W3 /GX /O2 /Oy /Ob1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
|
# ADD CPP /nologo /G6 /MD /W3 /GX /O2 /Ob0 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
|
||||||
# SUBTRACT CPP /YX
|
# SUBTRACT CPP /YX
|
||||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
|
@ -1 +1 @@
|
|||||||
set VERSION="0.57"
|
set VERSION="0.91"
|
||||||
|
Loading…
Reference in New Issue
Block a user