Wrapped some debugging output in #ifdef DEBUG. Cleaned up language of some debugging output. (Hey, it's a kids' program!)
15912 lines
392 KiB
C
15912 lines
392 KiB
C
/*
|
||
tuxpaint.c
|
||
|
||
Tux Paint - A simple drawing program for children.
|
||
|
||
Copyright (c) 2005 by Bill Kendrick and others; see AUTHORS.txt
|
||
bill@newbreedsoftware.com
|
||
http://www.newbreedsoftware.com/tuxpaint/
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
(See COPYING.txt)
|
||
|
||
June 14, 2002 - October 22, 2005
|
||
*/
|
||
|
||
|
||
/* (Note: VER_VERSION and VER_DATE are now handled by Makefile) */
|
||
|
||
|
||
/* Color depth for Tux Paint to run in, and store canvases in: */
|
||
|
||
//#define VIDEO_BPP 15 // saves memory
|
||
//#define VIDEO_BPP 16 // causes discoloration
|
||
//#define VIDEO_BPP 24 // compromise
|
||
#define VIDEO_BPP 32 // might be fastest, if conversion funcs removed
|
||
|
||
|
||
// plan to rip this out as soon as it is considered stable
|
||
//#define THREADED_FONTS
|
||
#define FORKED_FONTS
|
||
#ifdef WIN32
|
||
#undef FORKED_FONTS
|
||
#endif
|
||
|
||
/* Method for printing images: */
|
||
|
||
#define PRINTMETHOD_PS /* Direct to PostScript */
|
||
//#define PRINTMETHOD_PNM_PS /* Output PNM, assuming it gets printed */
|
||
//#define PRINTMETHOD_PNG_PNM_PS /* Output PNG, assuming it gets printed */
|
||
|
||
|
||
/* Default print and alt-print command, depending on the print method: */
|
||
|
||
#define DEFAULT_PRINTCOMMAND "lpr"
|
||
#define DEFAULT_ALTPRINTCOMMAND "kprinter"
|
||
|
||
#ifdef PRINTMETHOD_PNG_PNM_PS
|
||
#define PRINTCOMMAND "pngtopnm | pnmtops | " DEFAULT_PRINTCOMMAND
|
||
#elif defined(PRINTMETHOD_PNM_PS)
|
||
#define PRINTCOMMAND "pnmtops | " DEFAULT_PRINTCOMMAND
|
||
#elif defined(PRINTMETHOD_PS)
|
||
#define PRINTCOMMAND DEFAULT_PRINTCOMMAND
|
||
#else
|
||
#error No print method defined!
|
||
#endif
|
||
|
||
#ifdef PRINTMETHOD_PNG_PNM_PS
|
||
#define ALTPRINTCOMMAND "pngtopnm | pnmtops | " DEFAULT_ALTPRINTCOMMAND
|
||
#elif defined(PRINTMETHOD_PNM_PS)
|
||
#define ALTPRINTCOMMAND "pnmtops | " DEFAULT_ALTPRINTCOMMAND
|
||
#elif defined(PRINTMETHOD_PS)
|
||
#define ALTPRINTCOMMAND DEFAULT_ALTPRINTCOMMAND
|
||
#else
|
||
#error No print method defined!
|
||
#endif
|
||
|
||
|
||
/* Compile-time options: */
|
||
|
||
/* #define DEBUG */
|
||
/* #define DEBUG_MALLOC */
|
||
/* #define LOW_QUALITY_THUMBNAILS */
|
||
/* #define LOW_QUALITY_COLOR_SELECTOR */
|
||
/* #define LOW_QUALITY_STAMP_OUTLINE */
|
||
/* #define LOW_QUALITY_FLOOD_FILL */
|
||
/* #define NO_PROMPT_SHADOWS */
|
||
/* #define USE_HWSURFACE */
|
||
|
||
/* Disable fancy cursors in fullscreen mode, to avoid SDL bug: */
|
||
/* (This bug is still around, as of SDL 1.2.9, October 2005) */
|
||
#define LARGE_CURSOR_FULLSCREEN_BUG
|
||
|
||
// control the color selector
|
||
#define COLORSEL_DISABLE 0 // disable and draw the (greyed out) colors
|
||
#define COLORSEL_ENABLE 1 // enable and draw the colors
|
||
#define COLORSEL_CLOBBER 2 // colors get scribbled over
|
||
#define COLORSEL_REFRESH 4 // redraw the colors, either on or off
|
||
|
||
static unsigned draw_colors(unsigned action);
|
||
|
||
/////////////////////////////////////////////////////////////////////
|
||
// hide all scale-related values here
|
||
|
||
typedef struct scaleparams {
|
||
unsigned numer, denom;
|
||
} scaleparams;
|
||
static scaleparams scaletable[] = {
|
||
{ 1,256}, // 0.00390625
|
||
{ 3,512}, // 0.005859375
|
||
{ 1,128}, // 0.0078125
|
||
{ 3,256}, // 0.01171875
|
||
{ 1, 64}, // 0.015625
|
||
{ 3,128}, // 0.0234375
|
||
{ 1, 32}, // 0.03125
|
||
{ 3, 64}, // 0.046875
|
||
{ 1, 16}, // 0.0625
|
||
{ 3, 32}, // 0.09375
|
||
{ 1, 8}, // 0.125
|
||
{ 3, 16}, // 0.1875
|
||
{ 1, 4}, // 0.25
|
||
{ 3, 8}, // 0.375
|
||
{ 1, 2}, // 0.5
|
||
{ 3, 4}, // 0.75
|
||
{ 1, 1}, // 1
|
||
{ 3, 2}, // 1.5
|
||
{ 2, 1}, // 2
|
||
{ 3, 1}, // 3
|
||
{ 4, 1}, // 4
|
||
{ 6, 1}, // 6
|
||
{ 8, 1}, // 8
|
||
{ 12, 1}, // 12
|
||
{ 16, 1}, // 16
|
||
{ 24, 1}, // 24
|
||
{ 32, 1}, // 32
|
||
{ 48, 1}, // 48
|
||
};
|
||
|
||
|
||
/* Macros: */
|
||
|
||
#define HARD_MIN_STAMP_SIZE 0 // bottom of scaletable
|
||
#define HARD_MAX_STAMP_SIZE (sizeof scaletable / sizeof scaletable[0] - 1)
|
||
|
||
#define MIN_STAMP_SIZE (stamp_data[cur_stamp]->min)
|
||
#define MAX_STAMP_SIZE (stamp_data[cur_stamp]->max)
|
||
|
||
// to scale some offset, in pixels, like the current stamp is scaled
|
||
#define SCALE_LIKE_STAMP(x) ( ((x) * scaletable[stamp_data[cur_stamp]->size].numer + scaletable[stamp_data[cur_stamp]->size].denom-1) / scaletable[stamp_data[cur_stamp]->size].denom )
|
||
// pixel dimensions of the current stamp, as scaled
|
||
#define CUR_STAMP_W SCALE_LIKE_STAMP(active_stamp->w)
|
||
#define CUR_STAMP_H SCALE_LIKE_STAMP(active_stamp->h)
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
#define REPEAT_SPEED 300 /* Initial repeat speed for scrollbars */
|
||
#define CURSOR_BLINK_SPEED 500 /* Initial repeat speed for cursor */
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#define __USE_GNU /* for strcasestr() */
|
||
#include <string.h>
|
||
#include <ctype.h>
|
||
#include <time.h>
|
||
|
||
// math.h makes y1 an obscure function!
|
||
#define y1 evil_y1
|
||
#include <math.h>
|
||
#undef y1
|
||
|
||
#include <locale.h>
|
||
|
||
#ifndef OLD_UPPERCASE_CODE
|
||
#include <wctype.h>
|
||
#endif
|
||
|
||
#include <libintl.h>
|
||
#ifndef gettext_noop
|
||
#define gettext_noop(String) String
|
||
#endif
|
||
|
||
|
||
#ifdef DEBUG
|
||
#define gettext(String) debug_gettext(String)
|
||
#endif
|
||
|
||
|
||
#ifndef M_PI
|
||
#define M_PI 3.14159265358979323846
|
||
#endif
|
||
|
||
#include <sys/types.h>
|
||
#include <sys/stat.h>
|
||
|
||
#ifndef WIN32
|
||
#include <unistd.h>
|
||
#include <dirent.h>
|
||
#include <signal.h>
|
||
#ifdef __BEOS__
|
||
#include "BeOS_print.h"
|
||
// workaround dirent handling bug in TuxPaint code
|
||
typedef struct safer_dirent {
|
||
dev_t d_dev;
|
||
dev_t d_pdev;
|
||
ino_t d_ino;
|
||
ino_t d_pino;
|
||
unsigned short d_reclen;
|
||
char d_name[NAME_MAX];
|
||
} safer_dirent;
|
||
#define dirent safer_dirent
|
||
#endif
|
||
#ifdef __APPLE__
|
||
#include "macosx_print.h"
|
||
#include "wrapperdata.h"
|
||
extern WrapperData macosx;
|
||
#endif
|
||
#else
|
||
|
||
#include <unistd.h>
|
||
#include <dirent.h>
|
||
#include <malloc.h>
|
||
#include "win32_print.h"
|
||
#include <io.h>
|
||
#include <direct.h>
|
||
|
||
#define mkdir(path,access) _mkdir(path)
|
||
|
||
#endif /* WIN32 */
|
||
|
||
#include <errno.h>
|
||
#include <sys/stat.h>
|
||
|
||
#include "SDL.h"
|
||
#if !defined(_SDL_H)
|
||
#error "---------------------------------------------------"
|
||
#error "If you installed SDL from a package, be sure to get"
|
||
#error "the development package, as well!"
|
||
#error "(e.g., 'libsdl1.2-devel.rpm')"
|
||
#error "---------------------------------------------------"
|
||
#endif
|
||
|
||
#include "SDL_image.h"
|
||
#if !defined(_SDL_IMAGE_H) && !defined(_IMG_h)
|
||
#error "---------------------------------------------------"
|
||
#error "If you installed SDL_image from a package, be sure"
|
||
#error "to get the development package, as well!"
|
||
#error "(e.g., 'libsdl-image1.2-devel.rpm')"
|
||
#error "---------------------------------------------------"
|
||
#endif
|
||
|
||
#include "SDL_ttf.h"
|
||
#if !defined(_SDL_TTF_H) && !defined(_SDLttf_h)
|
||
#error "---------------------------------------------------"
|
||
#error "If you installed SDL_ttf from a package, be sure"
|
||
#error "to get the development package, as well!"
|
||
#error "(e.g., 'libsdl-ttf1.2-devel.rpm')"
|
||
#error "---------------------------------------------------"
|
||
#endif
|
||
|
||
#ifndef NOSOUND
|
||
#include "SDL_mixer.h"
|
||
#if !defined(_SDL_MIXER_H) && !defined(_MIXER_H_)
|
||
#error "---------------------------------------------------"
|
||
#error "If you installed SDL_mixer from a package, be sure"
|
||
#error "to get the development package, as well!"
|
||
#error "(e.g., 'libsdl-mixer1.2-devel.rpm')"
|
||
#error "---------------------------------------------------"
|
||
#endif
|
||
#endif
|
||
|
||
#ifndef SAVE_AS_BMP
|
||
#include <png.h>
|
||
#define FNAME_EXTENSION ".png"
|
||
#ifndef PNG_H
|
||
#error "---------------------------------------------------"
|
||
#error "If you installed the PNG libraries from a package,"
|
||
#error "be sure to get the development package, as well!"
|
||
#error "(e.g., 'libpng2-devel.rpm')"
|
||
#error "---------------------------------------------------"
|
||
#endif
|
||
#else
|
||
#define FNAME_EXTENSION ".bmp"
|
||
#endif
|
||
|
||
#if defined(THREADED_FONTS) || defined(THREADED_STAMPS)
|
||
#include "SDL_thread.h"
|
||
#include "SDL_mutex.h"
|
||
#else
|
||
#define SDL_CreateThread(fn,vp) (void*)(long)(fn(vp))
|
||
#define SDL_WaitThread(tid,rcp) do{(void)tid;(void)rcp;}while(0)
|
||
#define SDL_Thread int
|
||
#define SDL_mutex int
|
||
#define SDL_CreateMutex() 0 // creates in released state
|
||
#define SDL_DestroyMutex(lock)
|
||
#define SDL_mutexP(lock) // take lock
|
||
#define SDL_mutexV(lock) // release lock
|
||
#endif
|
||
|
||
#ifndef FORKED_FONTS
|
||
static SDL_Thread *font_thread;
|
||
#endif
|
||
static volatile long font_thread_done = 0;
|
||
static void run_font_scanner(void);
|
||
static int font_scanner_pid;
|
||
static int font_socket_fd;
|
||
|
||
#include "tools.h"
|
||
#include "titles.h"
|
||
#include "colors.h"
|
||
#include "shapes.h"
|
||
#include "magic.h"
|
||
#include "sounds.h"
|
||
#include "tip_tux.h"
|
||
#include "great.h"
|
||
|
||
|
||
#include "watch.xbm"
|
||
#include "watch-mask.xbm"
|
||
|
||
#include "hand.xbm"
|
||
#include "hand-mask.xbm"
|
||
|
||
#include "wand.xbm"
|
||
#include "wand-mask.xbm"
|
||
|
||
#include "insertion.xbm"
|
||
#include "insertion-mask.xbm"
|
||
|
||
#include "brush.xbm"
|
||
#include "brush-mask.xbm"
|
||
|
||
#include "crosshair.xbm"
|
||
#include "crosshair-mask.xbm"
|
||
|
||
#include "rotate.xbm"
|
||
#include "rotate-mask.xbm"
|
||
|
||
#include "up.xbm"
|
||
#include "up-mask.xbm"
|
||
|
||
#include "down.xbm"
|
||
#include "down-mask.xbm"
|
||
|
||
#include "tiny.xbm"
|
||
#include "tiny-mask.xbm"
|
||
|
||
#include "arrow.xbm"
|
||
#include "arrow-mask.xbm"
|
||
|
||
#ifdef DEBUG_MALLOC
|
||
#include "malloc.c"
|
||
#endif
|
||
|
||
#ifdef WIN32
|
||
/*
|
||
The SDL stderr redirection trick doesn't seem to work for perror().
|
||
This does pretty much the same thing.
|
||
*/
|
||
static void win32_perror(const char * const str)
|
||
{
|
||
if ( str && *str )
|
||
fprintf(stderr,"%s : ",str);
|
||
fprintf(stderr,
|
||
"%s [%d]\n",
|
||
(errno<_sys_nerr)?_sys_errlist[errno]:"unknown",errno );
|
||
}
|
||
#define perror win32_perror
|
||
|
||
/*
|
||
WIN32 and MINGW don't have strcasestr().
|
||
*/
|
||
#define NOMINMAX
|
||
#include "Shlwapi.h"
|
||
#define strcasestr StrStrI
|
||
#endif /* WIN32 */
|
||
|
||
|
||
#ifdef __GNUC__
|
||
// This version has strict type checking for safety.
|
||
// See the "unnecessary" pointer comparison. (from Linux)
|
||
#define min(x,y) ({ \
|
||
typeof(x) _x = (x); \
|
||
typeof(y) _y = (y); \
|
||
(void) (&_x == &_y); \
|
||
_x < _y ? _x : _y; })
|
||
#define max(x,y) ({ \
|
||
typeof(x) _x = (x); \
|
||
typeof(y) _y = (y); \
|
||
(void) (&_x == &_y); \
|
||
_x > _y ? _x : _y; })
|
||
#else
|
||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||
#endif
|
||
|
||
#define clamp(lo,value,hi) (min(max(value,lo),hi))
|
||
|
||
|
||
// since gcc-2.5
|
||
#ifdef __GNUC__
|
||
#define NORETURN __attribute__((__noreturn__))
|
||
#define FUNCTION __attribute__((__const__)) // no access to global mem, even via ptr, and no side effect
|
||
#else
|
||
#define NORETURN
|
||
#define FUNCTION
|
||
#endif
|
||
|
||
#if !defined(restrict) && __STDC_VERSION__ < 199901
|
||
#if __GNUC__ > 2 || __GNUC_MINOR__ >= 92
|
||
#define restrict __restrict__
|
||
#else
|
||
#warning No restrict keyword?
|
||
#define restrict
|
||
#endif
|
||
#endif
|
||
|
||
#if __GNUC__ > 2 || __GNUC_MINOR__ >= 96
|
||
// won't alias anything, and aligned enough for anything
|
||
#define MALLOC __attribute__ ((__malloc__))
|
||
// no side effect, may read globals
|
||
#define PURE __attribute__ ((__pure__))
|
||
// tell gcc what to expect: if(unlikely(err)) die(err);
|
||
#define likely(x) __builtin_expect(!!(x),1)
|
||
#define unlikely(x) __builtin_expect(!!(x),0)
|
||
#define expected(x,y) __builtin_expect((x),(y))
|
||
#else
|
||
#define MALLOC
|
||
#define PURE
|
||
#define likely(x) (x)
|
||
#define unlikely(x) (x)
|
||
#define expected(x,y) (x)
|
||
#endif
|
||
|
||
|
||
/* Unfortunately, there is a bug in SDL_ttf-2.0.6, the current version
|
||
that causes a segmentation fault if an attempt is made to call
|
||
TTF_OpenFont() with the filename of a font that doesn't exist. This
|
||
is an old and well documented bug that is fixed in CVS.
|
||
*/
|
||
static TTF_Font *BUGFIX_TTF_OpenFont206(const char * const file, int ptsize)
|
||
{
|
||
FILE *fp;
|
||
|
||
if ((fp = fopen(file, "rb")) == NULL) return NULL;
|
||
fclose(fp);
|
||
return TTF_OpenFont(file, ptsize);
|
||
}
|
||
#define TTF_OpenFont BUGFIX_TTF_OpenFont206
|
||
|
||
|
||
#if VIDEO_BPP==32
|
||
#ifdef __GNUC__
|
||
#define SDL_GetRGBA(p,f,rp,gp,bp,ap) ({ \
|
||
unsigned u_p = p; \
|
||
*(ap) = (u_p >> 24) & 0xff; \
|
||
*(rp) = (u_p >> 16) & 0xff; \
|
||
*(gp) = (u_p >> 8) & 0xff; \
|
||
*(bp) = (u_p >> 0) & 0xff; \
|
||
})
|
||
#define SDL_GetRGB(p,f,rp,gp,bp) ({ \
|
||
unsigned u_p = p; \
|
||
*(rp) = (u_p >> 16) & 0xff; \
|
||
*(gp) = (u_p >> 8) & 0xff; \
|
||
*(bp) = (u_p >> 0) & 0xff; \
|
||
})
|
||
#endif
|
||
#define SDL_MapRGBA(f,r,g,b,a) ( \
|
||
(((a) & 0xffu) << 24) \
|
||
| \
|
||
(((r) & 0xffu) << 16) \
|
||
| \
|
||
(((g) & 0xffu) << 8) \
|
||
| \
|
||
(((b) & 0xffu) << 0) \
|
||
)
|
||
#define SDL_MapRGB(f,r,g,b) ( \
|
||
(((r) & 0xffu) << 16) \
|
||
| \
|
||
(((g) & 0xffu) << 8) \
|
||
| \
|
||
(((b) & 0xffu) << 0) \
|
||
)
|
||
#endif
|
||
|
||
|
||
enum {
|
||
SAVE_OVER_PROMPT,
|
||
SAVE_OVER_ALWAYS,
|
||
SAVE_OVER_NO
|
||
};
|
||
|
||
enum {
|
||
ALTPRINT_MOD,
|
||
ALTPRINT_ALWAYS,
|
||
ALTPRINT_NEVER
|
||
};
|
||
|
||
|
||
enum {
|
||
STARTER_OUTLINE,
|
||
STARTER_SCENE
|
||
};
|
||
|
||
|
||
/* Show debugging stuff: */
|
||
|
||
static void debug(const char * const str)
|
||
{
|
||
#ifndef DEBUG
|
||
(void)str;
|
||
#else
|
||
fprintf(stderr, "DEBUG: %s\n", str);
|
||
fflush(stderr);
|
||
#endif
|
||
}
|
||
|
||
static const char *getfilename(const char* path)
|
||
{
|
||
char *p;
|
||
|
||
if ( (p = strrchr( path, '\\' )) != NULL )
|
||
return p+1;
|
||
if ( (p = strrchr( path, '/' )) != NULL )
|
||
return p+1;
|
||
return path;
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////
|
||
// Language stuff
|
||
|
||
/* Possible languages: */
|
||
|
||
enum {
|
||
LANG_AF, /* Afrikaans */
|
||
LANG_BE, /* Belarusian */
|
||
LANG_BG, /* Bulgarian */
|
||
LANG_BR, /* Breton */
|
||
LANG_CA, /* Catalan */
|
||
LANG_CS, /* Czech */
|
||
LANG_CY, /* Welsh */
|
||
LANG_DA, /* Danish */
|
||
LANG_DE, /* German */
|
||
LANG_EL, /* Greek */
|
||
LANG_EN, /* English (American) (DEFAULT) */
|
||
LANG_EN_GB, /* English (British) */
|
||
LANG_ES, /* Spanish */
|
||
LANG_ES_MX, /* Spanish (Mexican) */
|
||
LANG_ET, /* Estonian */
|
||
LANG_EU, /* Basque */
|
||
LANG_FI, /* Finnish */
|
||
LANG_FR, /* French */
|
||
LANG_GA, /* Gaelic */
|
||
LANG_GL, /* Galician */
|
||
LANG_GR, /* Gronings */
|
||
LANG_HE, /* Hebrew */
|
||
LANG_HI, /* Hindi */
|
||
LANG_HR, /* Croatian */
|
||
LANG_HU, /* Hungarian */
|
||
LANG_I_KLINGON_ROMANIZED, /* Klingon (Romanized) */
|
||
LANG_ID, /* Indonesian */
|
||
LANG_IS, /* Icelandic */
|
||
LANG_IT, /* Italian */
|
||
LANG_JA, /* Japanese */
|
||
LANG_KA, /* Georgian */
|
||
LANG_KO, /* Korean */
|
||
LANG_LT, /* Lithuanian */
|
||
LANG_MS, /* Malay */
|
||
LANG_NB, /* Norwegian Bokmal */
|
||
LANG_NL, /* Dutch */
|
||
LANG_NN, /* Norwegian Nynorsk */
|
||
LANG_PL, /* Polish */
|
||
LANG_PT_BR, /* Portuguese (Brazilian) */
|
||
LANG_PT_PT, /* Portuguese (Portugal) */
|
||
LANG_RO, /* Romanian */
|
||
LANG_RU, /* Russian */
|
||
LANG_RW, /* Kinyarwanda */
|
||
LANG_SK, /* Slovak */
|
||
LANG_SL, /* Slovenian */
|
||
LANG_SQ, /* Albanian */
|
||
LANG_SR, /* Serbian */
|
||
LANG_SV, /* Swedish */
|
||
LANG_SW, /* Swahili */
|
||
LANG_TA, /* Tamil */
|
||
LANG_TH, /* Thai */
|
||
LANG_TR, /* Turkish */
|
||
LANG_UK, /* Ukrainian */
|
||
LANG_VI, /* Vietnamese */
|
||
LANG_WA, /* Walloon */
|
||
LANG_ZH_CN, /* Chinese (Simplified) */
|
||
LANG_ZH_TW, /* Chinese (Traditional) */
|
||
NUM_LANGS
|
||
};
|
||
|
||
static const char * lang_prefixes[NUM_LANGS] = {
|
||
"af",
|
||
"be",
|
||
"bg",
|
||
"br",
|
||
"ca",
|
||
"cs",
|
||
"cy",
|
||
"da",
|
||
"de",
|
||
"el",
|
||
"en",
|
||
"en_gb",
|
||
"es",
|
||
"es_mx",
|
||
"et",
|
||
"eu",
|
||
"fi",
|
||
"fr",
|
||
"ga",
|
||
"gl",
|
||
"gos",
|
||
"he",
|
||
"hi",
|
||
"hr",
|
||
"hu",
|
||
"tlh",
|
||
"id",
|
||
"is",
|
||
"it",
|
||
"ja",
|
||
"ka",
|
||
"ko",
|
||
"lt",
|
||
"ms",
|
||
"nb",
|
||
"nl",
|
||
"nn",
|
||
"pl",
|
||
"pt_br",
|
||
"pt_pt",
|
||
"ro",
|
||
"ru",
|
||
"rw",
|
||
"sk",
|
||
"sl",
|
||
"sq",
|
||
"sr",
|
||
"sv",
|
||
"sw",
|
||
"ta",
|
||
"th",
|
||
"tr",
|
||
"uk",
|
||
"vi",
|
||
"wa",
|
||
"zh_cn",
|
||
"zh_tw"
|
||
};
|
||
|
||
|
||
// languages which don't use the default font
|
||
static int lang_use_own_font[] = {
|
||
// LANG_EL,
|
||
// LANG_HE,
|
||
LANG_HI,
|
||
LANG_JA,
|
||
LANG_KA,
|
||
LANG_KO,
|
||
LANG_TA,
|
||
LANG_TH,
|
||
LANG_ZH_CN,
|
||
LANG_ZH_TW,
|
||
-1
|
||
};
|
||
|
||
static int lang_use_right_to_left[] = {
|
||
LANG_HE,
|
||
-1
|
||
};
|
||
|
||
|
||
static int search_int_array(int l, int *array)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; array[i] != -1; i++)
|
||
{
|
||
if (array[i] == l)
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static char * langstr;
|
||
|
||
static void set_langstr(const char *s)
|
||
{
|
||
if (langstr)
|
||
free(langstr);
|
||
langstr = strdup(s);
|
||
}
|
||
|
||
static int need_own_font;
|
||
static int need_right_to_left;
|
||
static const char * lang_prefix;
|
||
|
||
/* Determine the current language/locale, and set the language string: */
|
||
|
||
static void set_current_language(void)
|
||
{
|
||
char * loc;
|
||
int lang, i, found;
|
||
|
||
bindtextdomain("tuxpaint", LOCALEDIR);
|
||
/* Old version of glibc does not have bind_textdomain_codeset() */
|
||
#if defined __GLIBC__ && __GLIBC__ == 2 && __GLIBC_MINOR__ >=2 || __GLIBC__ > 2
|
||
bind_textdomain_codeset("tuxpaint", "UTF-8");
|
||
#endif
|
||
textdomain("tuxpaint");
|
||
|
||
/* Default... */
|
||
|
||
lang = LANG_EN;
|
||
|
||
|
||
#ifndef WIN32
|
||
loc = setlocale(LC_MESSAGES, NULL);
|
||
if (loc != NULL)
|
||
{
|
||
if (strstr(loc, "LC_MESSAGES") != NULL)
|
||
loc = getenv("LANG");
|
||
}
|
||
#else
|
||
bind_textdomain_codeset("tuxpaint", "UTF-8");
|
||
loc = getenv("LANGUAGE");
|
||
if (!loc)
|
||
{
|
||
loc = _nl_locale_name(LC_MESSAGES, "");
|
||
if (loc)
|
||
{
|
||
char *s;
|
||
int len;
|
||
len = strlen("LANGUAGE=")+strlen(loc)+1;
|
||
s = malloc(len);
|
||
snprintf(s, len, "LANGUAGE=%s", loc);
|
||
putenv(s);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
debug(loc);
|
||
|
||
if (loc != NULL)
|
||
{
|
||
/* Which, if any, of the locales is it? */
|
||
|
||
found = 0;
|
||
|
||
for (i = 0; i < NUM_LANGS && found == 0; i++)
|
||
{
|
||
// Case-insensitive (both "pt_BR" and "pt_br" work, etc.)
|
||
if (strncasecmp(loc, lang_prefixes[i], strlen(lang_prefixes[i])) == 0)
|
||
{
|
||
lang = i;
|
||
found = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
lang_prefix = lang_prefixes[lang];
|
||
need_own_font = search_int_array(lang,lang_use_own_font);
|
||
need_right_to_left = search_int_array(lang,lang_use_right_to_left);
|
||
|
||
#ifdef DEBUG
|
||
fprintf(stderr,"DEBUG: Language is %s (%d)\n", lang_prefix, lang);
|
||
fflush(stderr);
|
||
#endif
|
||
|
||
}
|
||
|
||
/* FIXME: All this should REALLY be array-based!!! */
|
||
/* Show available languages: */
|
||
static void show_lang_usage(FILE * f, const char * const prg)
|
||
{
|
||
fprintf(f,
|
||
"\n"
|
||
"Usage: %s [--lang LANGUAGE]\n"
|
||
"\n"
|
||
"LANGUAGE may be one of:\n"
|
||
/* C */ " english american-english\n"
|
||
/* af */ " afrikaans\n"
|
||
/* sq */ " albanian\n"
|
||
/* eu */ " basque euskara\n"
|
||
/* be */ " belarusian bielaruskaja\n"
|
||
/* nb */ " bokmal\n"
|
||
/* pt_BR */ " brazilian brazilian-portuguese portugues-brazilian\n"
|
||
/* br */ " breton brezhoneg\n"
|
||
/* en_GB */ " british british-english\n"
|
||
/* bg_BG */ " bulgarian\n"
|
||
/* ca */ " catalan catala\n"
|
||
/* zh_CN */ " chinese simplified-chinese\n"
|
||
/* zh_TW */ " traditional-chinese\n"
|
||
/* hr */ " croatian hrvatski\n"
|
||
/* cs */ " czech cesky\n"
|
||
/* da */ " danish dansk\n"
|
||
/* nl */ " dutch nederlands\n"
|
||
/* et */ " estonian\n"
|
||
/* fi */ " finnish suomi\n"
|
||
/* fr */ " french francais\n"
|
||
/* ga */ " gaelic gaidhlig\n"
|
||
/* gl */ " galician galego\n"
|
||
/* ka */ " georgian\n"
|
||
/* de */ " german deutsch\n"
|
||
/* el */ " greek\n"
|
||
/* gos */ " gronings zudelk-veenkelonioals\n"
|
||
/* he */ " hebrew\n"
|
||
/* hi */ " hindi\n"
|
||
/* hu */ " hungarian magyar\n"
|
||
/* is */ " icelandic islenska\n"
|
||
/* id */ " indonesian bahasa-indonesia\n"
|
||
/* it */ " italian italiano\n"
|
||
/* ja */ " japanese\n"
|
||
/* rw */ " kinyarwanda\n"
|
||
/* tlh */ " klingon tlhIngan\n"
|
||
/* ko */ " korean\n"
|
||
/* lt */ " lithuanian lietuviu\n"
|
||
/* ms */ " malay\n"
|
||
/* es_MX */ " mexican mexican-spanish espanol-mejicano\n"
|
||
/* nn */ " norwegian nynorsk norsk\n"
|
||
/* pl */ " polish polski\n"
|
||
/* pt_PT */ " portuguese portugues\n"
|
||
/* ro */ " romanian\n"
|
||
/* ru */ " russian russkiy\n"
|
||
/* sr */ " serbian\n"
|
||
/* sk */ " slovak\n"
|
||
/* sl */ " slovenian slovensko\n"
|
||
/* es */ " spanish espanol\n"
|
||
/* sw */ " swahili\n"
|
||
/* sv */ " swedish svenska\n"
|
||
/* ta */ " tamil\n"
|
||
/* th */ " thai\n"
|
||
/* tr */ " turkish\n"
|
||
/* uk */ " ukrainian\n"
|
||
/* vi */ " vietnamese\n"
|
||
/* wa */ " walloon walon\n"
|
||
/* cy */ " welsh cymraeg\n"
|
||
"\n",
|
||
prg);
|
||
}
|
||
|
||
|
||
/* FIXME: Add accented characters to the descriptions */
|
||
/* Show available locales: */
|
||
static void show_locale_usage(FILE * f, const char * const prg)
|
||
{
|
||
fprintf(f,
|
||
"\n"
|
||
"Usage: %s [--locale LOCALE]\n"
|
||
"\n"
|
||
"LOCALE may be one of:\n"
|
||
" C (English American English)\n"
|
||
" af_ZA (Afrikaans)\n"
|
||
" eu_ES (Baque Euskara)\n"
|
||
" be_BY (Belarusian Bielaruskaja)\n"
|
||
" nb_NO (Bokmal)\n"
|
||
" pt_BR (Brazilian Brazilian Portuguese Portugues Brazilian)\n"
|
||
" br_FR (Breton Brezhoneg)\n"
|
||
" en_GB (British British English)\n"
|
||
" bg_BG (Bulgarian)\n"
|
||
" ca_ES (Catalan Catala)\n"
|
||
" zh_CN (Chinese-Simplified)\n"
|
||
" zh_TW (Chinese-Traditional)\n"
|
||
" cs_CZ (Czech Cesky)\n"
|
||
" da_DK (Danish Dansk)\n"
|
||
" nl_NL (Dutch)\n"
|
||
" fi_FI (Finnish Suomi)\n"
|
||
" fr_FR (French Francais)\n"
|
||
" ga_IE (Gaelic Gaidhlig)\n"
|
||
" gl_ES (Galician Galego)\n"
|
||
" gos_NL (Gronings Zudelk Veenkelonioals)\n"
|
||
" de_DE (German Deutsch)\n"
|
||
" et_EE (Estonian)\n"
|
||
" el_GR (Greek)\n"
|
||
" he_IL (Hebrew)\n"
|
||
" hi_IN (Hindi)\n"
|
||
" hr_HR (Croatian Hrvatski)\n"
|
||
" hu_HU (Hungarian Magyar)\n"
|
||
" tlh (Klingon tlhIngan)\n"
|
||
" is_IS (Icelandic Islenska)\n"
|
||
" id_ID (Indonesian Bahasa Indonesia)\n"
|
||
" it_IT (Italian Italiano)\n"
|
||
" ja_JP (Japanese)\n"
|
||
" ka_GE (Georgian)\n"
|
||
" ko_KR (Korean)\n"
|
||
" ms_MY (Malay)\n"
|
||
" lt_LT (Lithuanian Lietuviu)\n"
|
||
" nn_NO (Norwegian Nynorsk Norsk)\n"
|
||
" pl_PL (Polish Polski)\n"
|
||
" pt_PT (Portuguese Portugues)\n"
|
||
" ro_RO (Romanian)\n"
|
||
" ru_RU (Russian Russkiy)\n"
|
||
" rw_RW (Kinyarwanda)\n"
|
||
" sk_SK (Slovak)\n"
|
||
" sl_SI (Slovenian)\n"
|
||
" sq_AL (Albanian)\n"
|
||
" sr_YU (Serbian)\n"
|
||
" es_ES (Spanish Espanol)\n"
|
||
" es_MX (Mexican Mexican Spanish Espanol Mejicano)\n"
|
||
" sw_TZ (Swahili)\n"
|
||
" sv_SE (Swedish Svenska)\n"
|
||
" th_TH (Thai)\n"
|
||
" tr_TR (Turkish)\n"
|
||
" uk_UA (Ukrainian)\n"
|
||
" vi_VN (Vietnamese)\n"
|
||
" wa_BE (Walloon)\n"
|
||
" cy_GB (Welsh Cymraeg)\n"
|
||
"\n",
|
||
prg);
|
||
}
|
||
|
||
|
||
typedef struct language_to_locale_struct {
|
||
const char *language;
|
||
const char *locale;
|
||
} language_to_locale_struct;
|
||
|
||
static const language_to_locale_struct language_to_locale_array[] = {
|
||
{"english", "C"},
|
||
{"american-english", "C"},
|
||
{"croatian", "hr_HR.UTF-8"},
|
||
{"hrvatski", "hr_HR.UTF-8"},
|
||
{"catalan", "ca_ES.UTF-8"},
|
||
{"catala", "ca_ES.UTF-8"},
|
||
{"belarusian", "be_BY.UTF-8"},
|
||
{"bielaruskaja", "be_BY.UTF-8"},
|
||
{"czech", "cs_CZ.UTF-8"},
|
||
{"cesky", "cs_CZ.UTF-8"},
|
||
{"danish", "da_DK.UTF-8"},
|
||
{"dansk", "da_DK.UTF-8"},
|
||
{"german", "de_DE.UTF-8"},
|
||
{"deutsch", "de_DE.UTF-8"},
|
||
{"estonian", "et_EE.UTF-8"},
|
||
{"greek", "el_GR.UTF-8"},
|
||
{"gronings", "gos_NL.UTF-8"},
|
||
{"zudelk-veenkelonioals","gos_NL.UTF-8"},
|
||
{"british-english", "en_GB.UTF-8"},
|
||
{"british", "en_GB.UTF-8"},
|
||
{"spanish", "es_ES.UTF-8"},
|
||
{"mexican", "es_MX.UTF-8"},
|
||
{"mexican-spanish", "es_MX.UTF-8"},
|
||
{"espanol-mejicano", "es_MX.UTF-8"},
|
||
{"espanol", "es_ES.UTF-8"},
|
||
{"finnish", "fi_FI.UTF-8"},
|
||
{"suomi", "fi_FI.UTF-8"},
|
||
{"french", "fr_FR.UTF-8"},
|
||
{"francais", "fr_FR.UTF-8"},
|
||
{"gaelic", "ga_IE.UTF-8"},
|
||
{"gaidhlig", "ga_IE.UTF-8"},
|
||
{"galician", "gl_ES.UTF-8"},
|
||
{"galego", "gl_ES.UTF-8"},
|
||
{"hebrew", "he_IL.UTF-8"},
|
||
{"hindi", "hi_IN.UTF-8"},
|
||
{"hungarian", "hu_HU.UTF-8"},
|
||
{"magyar", "hu_HU.UTF-8"},
|
||
{"indonesian", "id_ID.UTF-8"},
|
||
{"bahasa-indonesia", "id_ID.UTF-8"},
|
||
{"icelandic", "is_IS.UTF-8"},
|
||
{"islenska", "is_IS.UTF-8"},
|
||
{"italian", "it_IT.UTF-8"},
|
||
{"italiano", "it_IT.UTF-8"},
|
||
{"japanese", "ja_JP.UTF-8"},
|
||
{"vietnamese", "vi_VN.UTF-8"},
|
||
{"afrikaans", "af_ZA.UTF-8"},
|
||
{"albanian", "sq_AL.UTF-8"},
|
||
{"breton", "br_FR.UTF-8"},
|
||
{"brezhoneg", "br_FR.UTF-8"},
|
||
{"bulgarian", "bg_BG.UTF-8"},
|
||
{"welsh", "cy_GB.UTF-8"},
|
||
{"cymraeg", "cy_GB.UTF-8"},
|
||
{"bokmal", "nb_NO.UTF-8"},
|
||
{"basque", "eu_ES.UTF-8"},
|
||
{"euskara", "eu_ES.UTF-8"},
|
||
{"georgian", "ka_GE"},
|
||
{"korean", "ko_KR.UTF-8"},
|
||
{"kinyarwanda", "rw_RW.UTF-8"},
|
||
{"klingon", "tlh.UTF-8"},
|
||
{"tlhIngan", "tlh.UTF-8"},
|
||
{"tlhingan", "tlh.UTF-8"},
|
||
{"tamil", "ta_IN.UTF-8"},
|
||
{"lithuanian", "lt_LT.UTF-8"},
|
||
{"lietuviu", "lt_LT.UTF-8"},
|
||
{"malay", "ms_MY.UTF-8"},
|
||
{"dutch", "nl_NL.UTF-8"},
|
||
{"nederlands", "nl_NL.UTF-8"},
|
||
{"norwegian", "nn_NO.UTF-8"},
|
||
{"nynorsk", "nn_NO.UTF-8"},
|
||
{"norsk", "nn_NO.UTF-8"},
|
||
{"polish", "pl_PL.UTF-8"},
|
||
{"polski", "pl_PL.UTF-8"},
|
||
{"brazilian-portuguese", "pt_BR.UTF-8"},
|
||
{"portugues-brazilian", "pt_BR.UTF-8"},
|
||
{"brazilian", "pt_BR.UTF-8"},
|
||
{"portuguese", "pt_PT.UTF-8"},
|
||
{"portugues", "pt_PT.UTF-8"},
|
||
{"romanian", "ro_RO.UTF-8"},
|
||
{"russian", "ru_RU.UTF-8"},
|
||
{"russkiy", "ru_RU.UTF-8"},
|
||
{"slovak", "sk_SK.UTF-8"},
|
||
{"slovenian", "sl_SI.UTF-8"},
|
||
{"slovensko", "sl_SI.UTF-8"},
|
||
{"serbian", "sr_YU.UTF-8"},
|
||
{"swedish", "sv_SE.UTF-8"},
|
||
{"svenska", "sv_SE.UTF-8"},
|
||
{"swahili", "sw_TZ.UTF-8"},
|
||
{"thai", "th_TH.UTF-8"},
|
||
{"turkish", "tr_TR.UTF-8"},
|
||
{"ukrainian", "uk_UA.UTF-8"},
|
||
{"walloon", "wa_BE.UTF-8"},
|
||
{"walon", "wa_BE.UTF-8"},
|
||
{"chinese", "zh_CN.UTF-8"},
|
||
{"simplified-chinese", "zh_CN.UTF-8"},
|
||
{"traditional-chinese", "zh_TW.UTF-8"},
|
||
};
|
||
|
||
|
||
static void setup_language(const char * const prg)
|
||
{
|
||
|
||
// Justify this or delete it. It seems to make Tux Paint
|
||
// violate the behavior documented by "man 7 locale".
|
||
#if 0
|
||
if (langstr == NULL && getenv("LANG"))
|
||
{
|
||
if(!strncasecmp(getenv("LANG"), "lt_LT", 5))
|
||
set_langstr("lithuanian");
|
||
if(!strncasecmp(getenv("LANG"), "pl_PL", 5))
|
||
set_langstr("polish");
|
||
}
|
||
#endif
|
||
|
||
if (langstr != NULL)
|
||
{
|
||
int i = sizeof language_to_locale_array / sizeof language_to_locale_array[0];
|
||
const char *locale = NULL;
|
||
|
||
while(i--)
|
||
{
|
||
if (strcmp(langstr, language_to_locale_array[i].language))
|
||
continue;
|
||
locale = language_to_locale_array[i].locale;
|
||
break;
|
||
}
|
||
|
||
if (!locale)
|
||
{
|
||
if (strcmp(langstr, "help") == 0 || strcmp(langstr, "list") == 0)
|
||
{
|
||
show_lang_usage(stdout, prg);
|
||
//free(langstr); // pointless
|
||
exit(0);
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr, "%s is an invalid language\n", langstr);
|
||
show_lang_usage(stderr, prg);
|
||
//free(langstr); // pointless
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
{
|
||
char *s;
|
||
ssize_t len;
|
||
|
||
len = strlen("LANGUAGE=")+strlen(locale)+1;
|
||
s = malloc(len);
|
||
snprintf(s, len, "LANGUAGE=%s", locale);
|
||
putenv(s);
|
||
|
||
len = strlen("LC_ALL=")+strlen(locale)+1;
|
||
s = malloc(len);
|
||
snprintf(s, len, "LC_ALL=%s", locale);
|
||
putenv(s);
|
||
}
|
||
|
||
setlocale(LC_ALL, "");
|
||
free(langstr);
|
||
}
|
||
|
||
set_current_language();
|
||
}
|
||
|
||
|
||
// handle --locale arg
|
||
static void do_locale_option(const char * const arg)
|
||
{
|
||
int len = strlen(arg) + 6;
|
||
char *str = malloc(len);
|
||
snprintf(str, len, "LANG=%s", arg);
|
||
putenv(str);
|
||
// We leak "str" because it can not be freed. It is now part
|
||
// of the environment. If it were local, the environment would
|
||
// get corrupted.
|
||
setlocale(LC_ALL, ""); /* use arg ? */
|
||
}
|
||
|
||
|
||
static TTF_Font *try_alternate_font(int size)
|
||
{
|
||
char str[128];
|
||
char prefix[64];
|
||
char *p;
|
||
|
||
strcpy(prefix, lang_prefix);
|
||
if ((p = strrchr(prefix, '_')) != NULL)
|
||
{
|
||
*p = 0;
|
||
snprintf(str, sizeof(str), "%sfonts/locale/%s.ttf",
|
||
DATA_PREFIX, prefix);
|
||
|
||
return TTF_OpenFont(str, size);
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
|
||
static TTF_Font *load_locale_font(TTF_Font *fallback, int size)
|
||
{
|
||
TTF_Font *ret = NULL;
|
||
if (need_own_font)
|
||
{
|
||
char str[128];
|
||
snprintf(str, sizeof(str), "%sfonts/locale/%s.ttf",
|
||
DATA_PREFIX, lang_prefix);
|
||
|
||
ret = TTF_OpenFont(str, size);
|
||
|
||
if (ret == NULL)
|
||
{
|
||
ret = try_alternate_font(size);
|
||
if (ret == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: Can't load font for this locale:\n"
|
||
"%s\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n"
|
||
"Will use default (American English) instead.\n\n",
|
||
str, SDL_GetError());
|
||
|
||
|
||
/* Revert to default: */
|
||
|
||
putenv((char *) "LANG=C");
|
||
putenv((char *) "OUTPUT_CHARSET=C");
|
||
setlocale(LC_ALL, "C");
|
||
|
||
set_current_language();
|
||
}
|
||
}
|
||
}
|
||
return ret ? ret : fallback;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////
|
||
// sizing
|
||
|
||
// The old Tux Paint:
|
||
// 640x480 screen
|
||
// 448x376 canvas
|
||
// 40x96 titles near the top
|
||
// 48x48 button tiles
|
||
// ??x56 tux area
|
||
// room for 2x7 button tile grids
|
||
|
||
typedef struct {
|
||
Uint8 rows, cols;
|
||
} grid_dims;
|
||
|
||
//static SDL_Rect r_screen; // was 640x480 @ 0,0 -- but this isn't so useful
|
||
static SDL_Rect r_canvas; // was 448x376 @ 96,0
|
||
static SDL_Rect r_tools; // was 96x336 @ 0,40
|
||
static SDL_Rect r_toolopt; // was 96x336 @ 544,40
|
||
static SDL_Rect r_colors; // was 544x48 @ 96,376
|
||
static SDL_Rect r_ttools; // was 96x40 @ 0,0 (title for tools, "Tools")
|
||
static SDL_Rect r_tcolors; // was 96x48 @ 0,376 (title for colors, "Colors")
|
||
static SDL_Rect r_ttoolopt; // was 96x40 @ 544,0 (title for tool options)
|
||
static SDL_Rect r_tuxarea; // was 640x56
|
||
|
||
static int button_w; // was 48
|
||
static int button_h; // was 48
|
||
|
||
static int color_button_w; // was 32
|
||
static int color_button_h; // was 48
|
||
|
||
// Define button grid dimensions. (in button units)
|
||
// These are the maximum slots -- some may be unused.
|
||
static grid_dims gd_tools; // was 2x7
|
||
static grid_dims gd_toolopt; // was 2x7
|
||
//static grid_dims gd_open; // was 4x4
|
||
static grid_dims gd_colors; // was 17x1
|
||
|
||
#define HEIGHTOFFSET (((WINDOW_HEIGHT - 480) / 48) * 48)
|
||
#define TOOLOFFSET (HEIGHTOFFSET / 48 * 2)
|
||
#define PROMPTOFFSETX (WINDOW_WIDTH - 640) / 2
|
||
#define PROMPTOFFSETY (HEIGHTOFFSET / 2)
|
||
|
||
#define THUMB_W ((WINDOW_WIDTH - 96 - 96) / 4)
|
||
#define THUMB_H (((48 * 7 + 40 + HEIGHTOFFSET) - 72) / 4)
|
||
|
||
static int WINDOW_WIDTH, WINDOW_HEIGHT;
|
||
|
||
|
||
static void setup_normal_screen_layout(void)
|
||
{
|
||
int buttons_tall;
|
||
|
||
button_w = 48;
|
||
button_h = 48;
|
||
|
||
gd_toolopt.cols = 2;
|
||
gd_tools.cols = 2;
|
||
|
||
r_ttools.x = 0;
|
||
r_ttools.y = 0;
|
||
r_ttools.w = gd_tools.cols * button_w;
|
||
r_ttools.h = 40;
|
||
|
||
r_ttoolopt.w = gd_toolopt.cols * button_w;
|
||
r_ttoolopt.h = 40;
|
||
r_ttoolopt.x = WINDOW_WIDTH - r_ttoolopt.w;
|
||
r_ttoolopt.y = 0;
|
||
|
||
gd_colors.rows = 1;
|
||
gd_colors.cols = (NUM_COLORS + gd_colors.rows - 1) / gd_colors.rows;
|
||
|
||
r_colors.h = 48;
|
||
color_button_h = r_colors.h / gd_colors.rows;
|
||
r_tcolors.h = r_colors.h;
|
||
|
||
r_tcolors.x = 0;
|
||
r_tcolors.w = gd_tools.cols * button_w;
|
||
r_colors.x = r_tcolors.w;
|
||
r_colors.w = WINDOW_WIDTH - r_tcolors.w;
|
||
|
||
color_button_w = r_colors.w / gd_colors.cols;
|
||
|
||
// This would make it contain _just_ the color spots,
|
||
// without any leftover bit on the end. Hmmm...
|
||
// r_colors.w = color_button_w * gd_colors.cols;
|
||
|
||
r_canvas.x = gd_tools.cols * button_w;
|
||
r_canvas.y = 0;
|
||
r_canvas.w = WINDOW_WIDTH - (gd_tools.cols+gd_toolopt.cols) * button_w;
|
||
|
||
r_tuxarea.x = 0;
|
||
r_tuxarea.w = WINDOW_WIDTH;
|
||
|
||
// need 56 minimum for the Tux area
|
||
buttons_tall = (WINDOW_HEIGHT - r_ttoolopt.h - 56 - r_colors.h) / button_h;
|
||
gd_tools.rows = buttons_tall;
|
||
gd_toolopt.rows = buttons_tall;
|
||
|
||
r_canvas.h = r_ttoolopt.h + buttons_tall * button_h;
|
||
|
||
r_colors.y = r_canvas.h + r_canvas.y;
|
||
r_tcolors.y = r_canvas.h + r_canvas.y;
|
||
|
||
r_tuxarea.y = r_colors.y + r_colors.h;
|
||
r_tuxarea.h = WINDOW_HEIGHT - r_tuxarea.y;
|
||
|
||
r_tools.x = 0;
|
||
r_tools.y = r_ttools.h + r_ttools.y;
|
||
r_tools.w = gd_tools.cols * button_w;
|
||
r_tools.h = gd_tools.rows * button_h;
|
||
|
||
r_toolopt.w = gd_toolopt.cols * button_w;
|
||
r_toolopt.h = gd_toolopt.rows * button_h;
|
||
r_toolopt.x = WINDOW_WIDTH - r_ttoolopt.w;
|
||
r_toolopt.y = r_ttoolopt.h + r_ttoolopt.y;
|
||
|
||
// TODO: dialog boxes
|
||
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
static void debug_rect(SDL_Rect *r, char *name)
|
||
{
|
||
printf("%-12s %dx%d @ %d,%d\n", name, r->w, r->h, r->x, r->y);
|
||
}
|
||
#define DR(x) debug_rect(&x, #x)
|
||
|
||
static void debug_dims(grid_dims *g, char *name)
|
||
{
|
||
printf("%-12s %dx%d\n", name, g->cols, g->rows);
|
||
}
|
||
#define DD(x) debug_dims(&x, #x)
|
||
|
||
static void print_layout(void)
|
||
{
|
||
printf("\n--- layout ---\n");
|
||
DR(r_canvas);
|
||
DR(r_tools);
|
||
DR(r_toolopt);
|
||
DR(r_colors);
|
||
DR(r_ttools);
|
||
DR(r_tcolors);
|
||
DR(r_ttoolopt);
|
||
DR(r_tuxarea);
|
||
DD(gd_tools);
|
||
DD(gd_toolopt);
|
||
DD(gd_colors);
|
||
printf("buttons are %dx%d\n", button_w,button_h);
|
||
printf("color buttons are %dx%d\n", color_button_w,color_button_h);
|
||
}
|
||
#undef DD
|
||
#undef DR
|
||
#endif
|
||
|
||
static void setup_screen_layout(void)
|
||
{
|
||
// can do right-to-left, colors at the top, extra tool option columns, etc.
|
||
setup_normal_screen_layout();
|
||
#ifdef DEBUG
|
||
print_layout();
|
||
#endif
|
||
}
|
||
|
||
static SDL_Surface * screen;
|
||
static SDL_Surface * canvas;
|
||
static SDL_Surface * img_starter, * img_starter_bkgd;
|
||
|
||
/* Update a rect. based on two x/y coords (not necessarly in order): */
|
||
static void update_screen(int x1, int y1, int x2, int y2)
|
||
{
|
||
int tmp;
|
||
|
||
if (x1 > x2)
|
||
{
|
||
tmp = x1;
|
||
x1 = x2;
|
||
x2 = tmp;
|
||
}
|
||
|
||
if (y1 > y2)
|
||
{
|
||
tmp = y1;
|
||
y1 = y2;
|
||
y2 = tmp;
|
||
}
|
||
|
||
x1 = x1 - 1;
|
||
x2 = x2 + 1;
|
||
y1 = y1 - 1;
|
||
y2 = y2 + 1;
|
||
|
||
|
||
if (x1 < 0)
|
||
x1 = 0;
|
||
if (x2 < 0)
|
||
x2 = 0;
|
||
if (y1 < 0)
|
||
y1 = 0;
|
||
if (y2 < 0)
|
||
y2 = 0;
|
||
|
||
if (x1 >= WINDOW_WIDTH)
|
||
x1 = WINDOW_WIDTH - 1;
|
||
if (x2 >= WINDOW_WIDTH)
|
||
x2 = WINDOW_WIDTH - 1;
|
||
if (y1 >= WINDOW_HEIGHT)
|
||
y1 = WINDOW_HEIGHT - 1;
|
||
if (y2 >= WINDOW_HEIGHT)
|
||
y2 = WINDOW_HEIGHT - 1;
|
||
|
||
SDL_UpdateRect(screen, x1, y1, x2-x1+1, y2-y1+1);
|
||
}
|
||
|
||
|
||
static void update_screen_rect(SDL_Rect *r)
|
||
{
|
||
SDL_UpdateRect(screen, r->x, r->y, r->w, r->h);
|
||
}
|
||
|
||
static int hit_test(const SDL_Rect * const r, unsigned x, unsigned y)
|
||
{
|
||
// note the use of unsigned math: no need to check for negative
|
||
return x - r->x < r->w && y - r->y < r->h ;
|
||
}
|
||
|
||
#define HIT(r) hit_test(&(r), event.button.x, event.button.y)
|
||
|
||
|
||
/* "#if"ing out, since unused; bjk 2005.01.09 */
|
||
|
||
#if 0
|
||
|
||
// x,y are pixel-wise screen-relative (mouse location), not grid-wise
|
||
// w,h are the size of a grid item
|
||
// Return the grid box.
|
||
// NOTE: grid items must fill full SDL_Rect width exactly
|
||
static int grid_hit_wh(const SDL_Rect * const r, unsigned x, unsigned y, unsigned w, unsigned h)
|
||
{
|
||
return (x - r->x) / w + (y - r->y) / h * (r->w/w) ;
|
||
}
|
||
|
||
// test an SDL_Rect r containing an array of WxH items for a grid location
|
||
#define GRIDHIT_WH(r,W,H) grid_hit_wh(&(r), event.button.x, event.button.y, W,H)
|
||
|
||
#endif
|
||
|
||
// test an SDL_Rect r containing an array of SDL_Surface surf for a grid location
|
||
#define GRIDHIT_SURF(r,surf) grid_hit_wh(&(r), event.button.x, event.button.y, (surf)->w, (surf)->h)
|
||
|
||
// x,y are pixel-wise screen-relative (mouse location), not grid-wise
|
||
// Return the grid box.
|
||
// NOTE: returns -1 if hit is below or to the right of the grid
|
||
static int grid_hit_gd(const SDL_Rect * const r, unsigned x, unsigned y, grid_dims *gd)
|
||
{
|
||
unsigned item_w = r->w / gd->cols;
|
||
unsigned item_h = r->h / gd->rows;
|
||
unsigned col = (x - r->x) / item_w;
|
||
unsigned row = (y - r->y) / item_h;
|
||
#ifdef DEBUG
|
||
printf("%d,%d resolves to %d,%d in a %dx%d grid, index is %d\n", x,y, col,row, gd->cols,gd->rows, col+row*gd->cols);
|
||
#endif
|
||
if (col >= gd->cols || row >= gd->rows)
|
||
return -1;
|
||
return col + row * gd->cols;
|
||
}
|
||
|
||
// test an SDL_Rect r for a grid location, based on a grid_dims gd
|
||
#define GRIDHIT_GD(r,gd) grid_hit_gd(&(r), event.button.x, event.button.y, &(gd))
|
||
|
||
/* Update the screen with the new canvas: */
|
||
static void update_canvas(int x1, int y1, int x2, int y2)
|
||
{
|
||
SDL_Rect src, dest;
|
||
|
||
if (img_starter != NULL)
|
||
{
|
||
/* If there was a starter, cover this part of the drawing with
|
||
the corresponding part of the starter's foreground! */
|
||
|
||
src.x = x1;
|
||
src.y = y1;
|
||
src.w = x2 - x1 + 1;
|
||
src.h = y2 - y1 + 1;
|
||
|
||
dest.x = x1;
|
||
dest.y = y1;
|
||
dest.w = src.w;
|
||
dest.h = src.h;
|
||
|
||
SDL_BlitSurface(img_starter, &dest, canvas, &dest);
|
||
}
|
||
|
||
SDL_BlitSurface(canvas, NULL, screen, &r_canvas);
|
||
update_screen(x1 + 96, y1, x2 + 96, y2);
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////
|
||
|
||
/* Globals: */
|
||
|
||
static int use_sound, fullscreen, disable_quit, simple_shapes,
|
||
disable_print, print_delay, only_uppercase, promptless_save, grab_input,
|
||
wheely, no_fancy_cursors, keymouse, mouse_x, mouse_y,
|
||
mousekey_up, mousekey_down, mousekey_left, mousekey_right,
|
||
dont_do_xor, use_print_config, dont_load_stamps, noshortcuts,
|
||
no_system_fonts, no_button_distinction,
|
||
mirrorstamps, disable_stamp_controls, disable_save, ok_to_use_lockfile,
|
||
alt_print_command_default;
|
||
static int want_alt_printcommand;
|
||
static int starter_mirrored, starter_flipped;
|
||
static int recording, playing;
|
||
static char * playfile;
|
||
static FILE * demofi;
|
||
static const char * printcommand = PRINTCOMMAND;
|
||
static const char * altprintcommand = ALTPRINTCOMMAND;
|
||
static int prog_bar_ctr;
|
||
|
||
enum {
|
||
UNDO_STARTER_NONE,
|
||
UNDO_STARTER_MIRRORED,
|
||
UNDO_STARTER_FLIPPED
|
||
};
|
||
|
||
#define NUM_UNDO_BUFS 20
|
||
static SDL_Surface * undo_bufs[NUM_UNDO_BUFS];
|
||
static int undo_starters[NUM_UNDO_BUFS];
|
||
static int cur_undo, oldest_undo, newest_undo;
|
||
|
||
static SDL_Surface * img_title, * img_title_credits, * img_title_tuxpaint;
|
||
static SDL_Surface * img_progress;
|
||
static SDL_Surface * img_btn_up, * img_btn_down, * img_btn_off;
|
||
static SDL_Surface * img_dead40x40;
|
||
static SDL_Surface * img_black, * img_grey;
|
||
static SDL_Surface * img_yes, * img_no;
|
||
static SDL_Surface * img_open, * img_erase, * img_back, * img_trash;
|
||
static SDL_Surface * img_printer, * img_printer_wait, * img_save_over;
|
||
static SDL_Surface * img_popup_arrow;
|
||
static SDL_Surface * img_cursor_up, * img_cursor_down;
|
||
static SDL_Surface * img_cursor_starter_up, * img_cursor_starter_down;
|
||
static SDL_Surface * img_scroll_up, * img_scroll_down;
|
||
static SDL_Surface * img_scroll_up_off, * img_scroll_down_off;
|
||
static SDL_Surface * img_grow, * img_shrink;
|
||
static SDL_Surface * img_bold, * img_italic;
|
||
|
||
static SDL_Surface * img_sparkles;
|
||
static SDL_Surface * img_grass;
|
||
|
||
static SDL_Surface * img_title_on, * img_title_off,
|
||
* img_title_large_on, * img_title_large_off;
|
||
static SDL_Surface * img_title_names[NUM_TITLES];
|
||
static SDL_Surface * img_tools[NUM_TOOLS], * img_tool_names[NUM_TOOLS];
|
||
|
||
static SDL_Surface * thumbnail(SDL_Surface * src, int max_x, int max_y,
|
||
int keep_aspect);
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// font stuff
|
||
|
||
// example from a Debian box with MS fonts:
|
||
// start with 232 files
|
||
// remove "Cursor", "Webdings", "Dingbats", "Standard Symbols L"
|
||
// split "Condensed" faces out into own family
|
||
// group by family
|
||
// end up with 34 user choices
|
||
|
||
static int text_state;
|
||
// nice progression (alternating 33% and 25%) 9 12 18 24 36 48 72 96 144 192
|
||
// commonly hinted sizes seem to be: 9, 10, 12, 14, 18, 20 (less so), 24
|
||
// reasonable: 9,12,18... and 10,14,18...
|
||
static int text_sizes[] = {9, 12, 18, 24, 36, 48,
|
||
56, 64, 96, 112, 128, 160}; // point sizes
|
||
#define MIN_TEXT_SIZE 0u
|
||
#define MAX_TEXT_SIZE (sizeof text_sizes / sizeof text_sizes[0] - 1)
|
||
static unsigned text_size = 4; // initial text size
|
||
|
||
|
||
// for sorting through the font files at startup
|
||
typedef struct style_info {
|
||
char *filename;
|
||
char *directory;
|
||
char *family; // name like "FooCorp Thunderstruck"
|
||
char *style; // junk like "Oblique Demi-Bold"
|
||
int italic;
|
||
int boldness;
|
||
int score;
|
||
int truetype; // Is it? (TrueType gets priority)
|
||
} style_info;
|
||
|
||
// user's notion of a font
|
||
typedef struct family_info {
|
||
char *directory;
|
||
char *family;
|
||
char *filename[4];
|
||
TTF_Font *handle;
|
||
int score;
|
||
} family_info;
|
||
|
||
static TTF_Font * medium_font, * small_font, * large_font, * locale_font;
|
||
|
||
static family_info **user_font_families;
|
||
static int num_font_families = 0;
|
||
static int num_font_families_max = 0;
|
||
|
||
static style_info **user_font_styles;
|
||
static int num_font_styles = 0;
|
||
static int num_font_styles_max = 0;
|
||
|
||
static void receive_some_font_info(void);
|
||
|
||
static TTF_Font *getfonthandle(int desire)
|
||
{
|
||
int missing = 0;
|
||
family_info *fi = user_font_families[desire];
|
||
char *name = fi->filename[text_state];
|
||
char *pathname;
|
||
|
||
if(fi->handle)
|
||
return fi->handle;
|
||
|
||
if(!name)
|
||
{
|
||
name = fi->filename[text_state ^ TTF_STYLE_ITALIC];
|
||
missing = text_state & TTF_STYLE_ITALIC;
|
||
}
|
||
if(!name)
|
||
{
|
||
name = fi->filename[text_state ^ TTF_STYLE_BOLD];
|
||
missing = text_state & TTF_STYLE_BOLD;
|
||
}
|
||
if(!name)
|
||
{
|
||
name = fi->filename[text_state ^ (TTF_STYLE_ITALIC|TTF_STYLE_BOLD)];
|
||
missing = text_state & (TTF_STYLE_ITALIC|TTF_STYLE_BOLD);
|
||
}
|
||
pathname = alloca(strlen(fi->directory) + 1 + strlen(name) + 1);
|
||
sprintf(pathname, "%s/%s", fi->directory, name);
|
||
|
||
fi->handle = TTF_OpenFont(pathname,text_sizes[text_size]);
|
||
// if the font doesn't load, we die -- it did load OK before though
|
||
TTF_SetFontStyle(fi->handle,missing);
|
||
return fi->handle;
|
||
}
|
||
|
||
|
||
static SDL_Surface *render_text(TTF_Font *restrict font, const char *restrict str, SDL_Color color)
|
||
{
|
||
SDL_Surface *ret;
|
||
int height;
|
||
|
||
ret = TTF_RenderUTF8_Blended(font, str, color);
|
||
if(ret)
|
||
return ret;
|
||
// Sometimes a font will be missing a character we need. Sometimes the library
|
||
// will substitute a rectangle without telling us. Sometimes it returns NULL.
|
||
// Probably we should use FreeType directly. For now though...
|
||
height = TTF_FontHeight(font);
|
||
if(height<2)
|
||
height = 2;
|
||
return thumbnail(img_title_large_off, height*strlen(str)/2, height, 0);
|
||
}
|
||
|
||
|
||
// void qsort(void *base, size_t nmemb, size_t size,
|
||
// int(*compar)(const void *, const void *));
|
||
|
||
|
||
// For qsort() and other use, to see if font files are groupable
|
||
static int compar_fontgroup(const void *v1, const void *v2)
|
||
{
|
||
const style_info *s1 = *(style_info**)v1;
|
||
const style_info *s2 = *(style_info**)v2;
|
||
int rc;
|
||
|
||
rc = strcmp(s1->directory,s2->directory);
|
||
if(rc)
|
||
return rc;
|
||
|
||
rc = s1->truetype - s2->truetype;
|
||
if(rc)
|
||
return rc;
|
||
|
||
return strcmp(s1->family,s2->family);
|
||
}
|
||
|
||
|
||
// For qsort() and other use, to see if font files are duplicates
|
||
static int compar_fontkiller(const void *v1, const void *v2)
|
||
{
|
||
const family_info *f1 = *(family_info**)v1;
|
||
const family_info *f2 = *(family_info**)v2;
|
||
int rc;
|
||
|
||
rc = strcmp(f1->family,f2->family);
|
||
if(rc)
|
||
return rc;
|
||
|
||
return f1->score - f2->score;
|
||
}
|
||
|
||
|
||
// For qsort() and other use, to order the worst ones last
|
||
static int compar_fontscore(const void *v1, const void *v2)
|
||
{
|
||
const family_info *f1 = *(family_info**)v1;
|
||
const family_info *f2 = *(family_info**)v2;
|
||
|
||
return f2->score - f1->score;
|
||
}
|
||
|
||
// Font style names are a mess that we must try to make
|
||
// sense of. For example...
|
||
//
|
||
// Cooper: Light, Medium, Light Bold, Black
|
||
// HoeflerText: (nil), Black
|
||
static void parse_font_style(style_info *si)
|
||
{
|
||
int have_light = 0;
|
||
int have_demi = 0;
|
||
int have_bold = 0;
|
||
int have_medium = 0;
|
||
int have_black = 0;
|
||
int have_heavy = 0;
|
||
|
||
int stumped = 0;
|
||
char *sp = si->style;
|
||
|
||
si->italic = 0;
|
||
|
||
|
||
while(*sp)
|
||
{
|
||
if(*sp==' ')
|
||
{
|
||
sp++;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Bold",strlen("Bold")))
|
||
{
|
||
sp += strlen("Bold");
|
||
have_bold = 1;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Regular",strlen("Regular")))
|
||
{
|
||
sp += strlen("Regular");
|
||
continue;
|
||
}
|
||
#if 0
|
||
if(!strncasecmp(sp,"Italic",strlen("Italic")))
|
||
{
|
||
sp += strlen("Italic");
|
||
si->italic = 1;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Oblique",strlen("Oblique")))
|
||
{
|
||
sp += strlen("Oblique");
|
||
si->italic = 1;
|
||
continue;
|
||
}
|
||
#endif
|
||
// move " Condensed" from style to family
|
||
if(!strncasecmp(sp,"Condensed",strlen("Condensed")))
|
||
{
|
||
size_t len = strlen(si->family);
|
||
char *name = malloc(len + strlen(" Condensed") + 1);
|
||
sp += strlen("Condensed");
|
||
memcpy(name,si->family,len);
|
||
strcpy(name+len," Condensed");
|
||
free(si->family);
|
||
si->family = name;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Light",strlen("Light")))
|
||
{
|
||
sp += strlen("Light");
|
||
have_light = 1;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Medium",strlen("Medium")))
|
||
{
|
||
sp += strlen("Medium");
|
||
have_medium = 1;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Demi",strlen("Demi")))
|
||
{
|
||
sp += strlen("Demi");
|
||
have_demi = 1;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Heavy",strlen("Heavy")))
|
||
{
|
||
sp += strlen("Heavy");
|
||
have_heavy = 1;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Normal",strlen("Normal")))
|
||
{
|
||
sp += strlen("Normal");
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Black",strlen("Black")))
|
||
{
|
||
sp += strlen("Black");
|
||
have_black = 1;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Roman",strlen("Roman")))
|
||
{
|
||
sp += strlen("Roman");
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Book",strlen("Book")))
|
||
{
|
||
sp += strlen("Book");
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Chancery",strlen("Chancery")))
|
||
{
|
||
sp += strlen("Chancery");
|
||
si->italic = 1;
|
||
continue;
|
||
}
|
||
if(!strncasecmp(sp,"Thin",strlen("Thin")))
|
||
{
|
||
sp += strlen("Thin");
|
||
have_light = 1;
|
||
continue;
|
||
}
|
||
if(!strncmp(sp,"LR",strlen("LR")))
|
||
{
|
||
sp += strlen("LR");
|
||
continue;
|
||
}
|
||
|
||
if(!stumped)
|
||
{
|
||
stumped=1;
|
||
#if 0
|
||
// THREADED_FONTS
|
||
printf("Font style parser stumped by \"%s\".\n", si->style);
|
||
#endif
|
||
}
|
||
sp++; // bad: an unknown character
|
||
}
|
||
|
||
if (have_demi || have_medium)
|
||
si->boldness = 2;
|
||
else if (have_bold || have_black || have_heavy) // TODO: split these
|
||
si->boldness = 3;
|
||
else if (have_light)
|
||
si->boldness = 0;
|
||
else
|
||
si->boldness = 1;
|
||
|
||
// we'll count both TrueType and OpenType
|
||
si->truetype = !!strcasestr(si->filename,".ttf") || !!strcasestr(si->filename,".otf");
|
||
}
|
||
|
||
static void groupfonts_range(style_info **base, int count)
|
||
{
|
||
int boldcounts[4] = {0,0,0,0};
|
||
int boldmap[4] = {-1,-1,-1,-1};
|
||
int i;
|
||
int boldmax;
|
||
int boldmin;
|
||
int bolduse;
|
||
int spot;
|
||
family_info *fi;
|
||
|
||
#if 0
|
||
// THREADED_FONTS
|
||
if(count<1 || count>4)
|
||
{
|
||
printf("\n::::::: %d styles in %s:\n",count, base[0]->family);
|
||
i = count;
|
||
while(i--)
|
||
{
|
||
printf(" %s\n", base[i]->style);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
i = count;
|
||
while(i--)
|
||
boldcounts[base[i]->boldness]++;
|
||
|
||
boldmax = base[0]->boldness;
|
||
boldmin = base[0]->boldness;
|
||
bolduse = 0;
|
||
|
||
i = 4;
|
||
while(i--)
|
||
{
|
||
if(!boldcounts[i])
|
||
continue;
|
||
if(i>boldmax)
|
||
boldmax = i;
|
||
if(i<boldmin)
|
||
boldmin = i;
|
||
bolduse++;
|
||
}
|
||
if(likely(bolduse<=2))
|
||
{
|
||
// in case they are same, we want non-bold,
|
||
// so that setting goes second
|
||
boldmap[boldmax] = 1;
|
||
boldmap[boldmin] = 0;
|
||
}
|
||
else if(count==3)
|
||
{
|
||
int boldmid;
|
||
int zmin = 0, zmid = 0, zmax = 0;
|
||
|
||
boldmap[boldmax] = 1;
|
||
boldmap[boldmin] = 0;
|
||
boldmid = boldcounts[boldmin+1] ? boldmin+1 : boldmin+2;
|
||
|
||
i = 3;
|
||
while(i--){
|
||
if(base[i]->boldness==boldmin)
|
||
zmin = base[i]->italic;
|
||
if(base[i]->boldness==boldmid)
|
||
zmid = base[i]->italic;
|
||
if(base[i]->boldness==boldmax)
|
||
zmax = base[i]->italic;
|
||
}
|
||
if(zmin!=zmid)
|
||
boldmap[boldmid] = 0;
|
||
else if(zmid!=zmax)
|
||
boldmap[boldmid] = 1;
|
||
else if(boldmin==0 && boldmid==1)
|
||
{
|
||
boldmap[0] = -1;
|
||
boldmap[1] = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int claimed_bold = boldcounts[3];
|
||
int claimed_norm = boldcounts[1];
|
||
|
||
// 3 or 4 boldness levels, 4 or more styles!
|
||
// This is going to be random hacks and hopes.
|
||
|
||
// bold is bold
|
||
boldmap[3] = 1;
|
||
|
||
// norm is norm
|
||
boldmap[1] = 0;
|
||
|
||
// classify demi-bold or medium
|
||
if(claimed_bold<2)
|
||
{
|
||
boldmap[2] = 1;
|
||
claimed_bold += boldcounts[2];
|
||
}
|
||
else if(claimed_norm<2)
|
||
{
|
||
boldmap[2] = 0;
|
||
claimed_norm += boldcounts[2];
|
||
}
|
||
|
||
// classify lightface
|
||
if(claimed_norm<2)
|
||
{
|
||
boldmap[0] = 0;
|
||
//claimed_norm += boldcounts[0];
|
||
}
|
||
}
|
||
|
||
if (num_font_families==num_font_families_max)
|
||
{
|
||
num_font_families_max = num_font_families_max * 5 / 4 + 30;
|
||
user_font_families = realloc(user_font_families, num_font_families_max * sizeof *user_font_families);
|
||
}
|
||
|
||
fi = calloc(1, sizeof *fi);
|
||
user_font_families[num_font_families++] = fi;
|
||
fi->directory = strdup(base[0]->directory);
|
||
fi->family = strdup(base[0]->family);
|
||
fi->score = base[0]->truetype + base[0]->score;
|
||
i = count;
|
||
while(i--)
|
||
{
|
||
int b = boldmap[base[i]->boldness];
|
||
if(b==-1)
|
||
{
|
||
#if 0
|
||
// THREADED_FONTS
|
||
printf("too many boldness levels, discarding: %s, %s\n", base[i]->family, base[i]->style);
|
||
#endif
|
||
continue;
|
||
}
|
||
spot = b ? TTF_STYLE_BOLD : 0;
|
||
spot += base[i]->italic ? TTF_STYLE_ITALIC : 0;
|
||
if(fi->filename[spot])
|
||
{
|
||
#if 0
|
||
// THREADED_FONTS
|
||
printf("duplicates, discarding: %s, %s\n", base[i]->family, base[i]->style);
|
||
printf("b %d, spot %d\n", b, spot);
|
||
printf("occupancy %p %p %p %p\n", fi->filename[0], fi->filename[1], fi->filename[2], fi->filename[3]);
|
||
#endif
|
||
continue;
|
||
}
|
||
fi->filename[spot] = strdup(base[i]->filename);
|
||
fi->score += 2;
|
||
}
|
||
if(!fi->filename[0] && !fi->filename[1])
|
||
{
|
||
fi->filename[0] = fi->filename[2];
|
||
fi->filename[2] = NULL;
|
||
fi->filename[1] = fi->filename[3];
|
||
fi->filename[3] = NULL;
|
||
}
|
||
if(!fi->filename[0] && !fi->filename[2])
|
||
{
|
||
fi->filename[0] = fi->filename[1];
|
||
fi->filename[1] = NULL;
|
||
fi->filename[2] = fi->filename[3];
|
||
fi->filename[3] = NULL;
|
||
}
|
||
if(!fi->filename[0])
|
||
{
|
||
fi->filename[0] = strdup(fi->filename[TTF_STYLE_BOLD]);
|
||
}
|
||
}
|
||
|
||
|
||
static void dupe_markdown_range(family_info **base, int count)
|
||
{
|
||
int bestscore = -999;
|
||
int bestslot = 0;
|
||
int i = count;
|
||
while(i--)
|
||
{
|
||
int score = base[i]->score;
|
||
if(score<=bestscore)
|
||
continue;
|
||
bestscore = score;
|
||
bestslot = i;
|
||
}
|
||
i = count;
|
||
while(i--)
|
||
{
|
||
if (i==bestslot)
|
||
continue;
|
||
base[i]->score = -999;
|
||
}
|
||
}
|
||
|
||
|
||
static void groupfonts(void)
|
||
{
|
||
char * * cpp;
|
||
int i = num_font_styles;
|
||
int low = 0;
|
||
|
||
while(i--)
|
||
parse_font_style(user_font_styles[i]);
|
||
|
||
qsort(user_font_styles, num_font_styles, sizeof user_font_styles[0], compar_fontgroup);
|
||
//printf("groupfonts() qsort(user_font_styles...)\n");
|
||
//fflush(stdout);
|
||
|
||
for(;;)
|
||
{
|
||
int high = low;
|
||
if(low >= num_font_styles)
|
||
break;
|
||
for(;;)
|
||
{
|
||
if(++high >= num_font_styles)
|
||
break;
|
||
if(compar_fontgroup(user_font_styles+low, user_font_styles+high))
|
||
break;
|
||
}
|
||
groupfonts_range(user_font_styles+low, high-low);
|
||
low = high;
|
||
}
|
||
|
||
i = num_font_styles;
|
||
while(i--)
|
||
{
|
||
free(user_font_styles[i]->filename);
|
||
free(user_font_styles[i]->directory);
|
||
free(user_font_styles[i]->family);
|
||
free(user_font_styles[i]->style);
|
||
free(user_font_styles[i]);
|
||
}
|
||
free(user_font_styles);
|
||
user_font_styles = NULL; // just to catch bugs
|
||
|
||
qsort(user_font_families, num_font_families, sizeof user_font_families[0], compar_fontkiller);
|
||
//printf(stderr, "groupfonts() qsort(user_font_families 1...)\n");
|
||
//fflush(stdout);
|
||
low = 0;
|
||
for(;;)
|
||
{
|
||
int high = low;
|
||
if(low >= num_font_families)
|
||
break;
|
||
for(;;)
|
||
{
|
||
if(++high >= num_font_families)
|
||
break;
|
||
if(strcmp(user_font_families[low]->family,user_font_families[high]->family))
|
||
break;
|
||
}
|
||
dupe_markdown_range(user_font_families+low,high-low);
|
||
low = high;
|
||
}
|
||
qsort(user_font_families, num_font_families, sizeof user_font_families[0], compar_fontscore);
|
||
//printf("groupfonts() qsort(user_font_families 2...)\n");
|
||
//fflush(stdout);
|
||
if(user_font_families[0]->score < 0)
|
||
printf("sorted the wrong way, or all fonts were unusable\n");
|
||
#if 0
|
||
// THREADED_FONTS
|
||
printf("Trim starting with %d families\n", num_font_families);
|
||
#endif
|
||
while(num_font_families>1 && user_font_families[num_font_families-1]->score < 0)
|
||
{
|
||
i = --num_font_families;
|
||
free(user_font_families[i]->directory);
|
||
free(user_font_families[i]->family);
|
||
cpp = user_font_families[i]->filename;
|
||
if(cpp[0])
|
||
free(cpp[0]);
|
||
if(cpp[1])
|
||
free(cpp[1]);
|
||
if(cpp[2])
|
||
free(cpp[2]);
|
||
if(cpp[3])
|
||
free(cpp[3]);
|
||
free(user_font_families[i]);
|
||
user_font_families[i] = NULL;
|
||
}
|
||
#if 0
|
||
// THREADED_FONTS
|
||
printf("Trim ending with %d families\n", num_font_families);
|
||
#endif
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////
|
||
|
||
typedef struct stamp_type {
|
||
char *stampname;
|
||
char *stxt;
|
||
#ifndef NOSOUND
|
||
Mix_Chunk *ssnd;
|
||
#endif
|
||
|
||
SDL_Surface *thumbnail;
|
||
unsigned thumb_mirrored : 1;
|
||
unsigned thumb_flipped : 1;
|
||
unsigned no_premirror : 1;
|
||
|
||
unsigned processed : 1; // got *.dat, computed size limits, etc.
|
||
|
||
unsigned no_sound : 1;
|
||
unsigned no_txt : 1;
|
||
// unsigned no_local_sound : 1; // to remember, if code written to discard sound
|
||
|
||
unsigned tinter : 3;
|
||
unsigned colorable : 1;
|
||
unsigned tintable : 1;
|
||
unsigned mirrorable : 1;
|
||
unsigned flipable : 1;
|
||
|
||
unsigned mirrored : 1;
|
||
unsigned flipped : 1;
|
||
unsigned min : 5;
|
||
unsigned size : 5;
|
||
unsigned max : 5;
|
||
} stamp_type;
|
||
|
||
static int num_stamps;
|
||
static int max_stamps;
|
||
static stamp_type **stamp_data;
|
||
|
||
static SDL_Surface *active_stamp;
|
||
|
||
/* Returns whether a particular stamp can be colored: */
|
||
static int stamp_colorable(int stamp)
|
||
{
|
||
return stamp_data[stamp]->colorable;
|
||
}
|
||
|
||
/* Returns whether a particular stamp can be tinted: */
|
||
static int stamp_tintable(int stamp)
|
||
{
|
||
return stamp_data[stamp]->tintable;
|
||
}
|
||
|
||
|
||
|
||
static int num_brushes, num_brushes_max;
|
||
static SDL_Surface **img_brushes;
|
||
|
||
static SDL_Surface * img_shapes[NUM_SHAPES], * img_shape_names[NUM_SHAPES];
|
||
static SDL_Surface * img_magics[NUM_MAGICS], * img_magic_names[NUM_MAGICS];
|
||
static SDL_Surface * img_openlabels_open, * img_openlabels_erase,
|
||
* img_openlabels_back;
|
||
|
||
static SDL_Surface * img_tux[NUM_TIP_TUX];
|
||
|
||
static SDL_Surface * img_mouse, * img_mouse_click;
|
||
|
||
#ifdef LOW_QUALITY_COLOR_SELECTOR
|
||
static SDL_Surface * img_paintcan;
|
||
#else
|
||
static SDL_Surface * img_color_btns[NUM_COLORS*2];
|
||
static SDL_Surface * img_color_btn_off;
|
||
#endif
|
||
|
||
static int colors_are_selectable;
|
||
|
||
static SDL_Surface * img_cur_brush;
|
||
static int brush_counter, rainbow_color;
|
||
|
||
#ifndef NOSOUND
|
||
static Mix_Chunk * sounds[NUM_SOUNDS];
|
||
#endif
|
||
|
||
#define NUM_ERASERS 6 /* How many sizes of erasers (from ERASER_MIN to _MAX) */
|
||
#define ERASER_MIN 13
|
||
#define ERASER_MAX 128
|
||
|
||
|
||
static SDL_Cursor * cursor_hand, * cursor_arrow, * cursor_watch,
|
||
* cursor_up, * cursor_down, * cursor_tiny, * cursor_crosshair,
|
||
* cursor_brush, * cursor_wand, * cursor_insertion, * cursor_rotate;
|
||
|
||
|
||
static unsigned cur_color;
|
||
static int cur_tool, cur_brush, cur_stamp, cur_shape, cur_magic;
|
||
static int cur_font, cur_eraser;
|
||
static int cursor_left, cursor_x, cursor_y, cursor_textwidth; // canvas-relative
|
||
static int been_saved;
|
||
static char file_id[32];
|
||
static char starter_id[32];
|
||
static int brush_scroll, stamp_scroll, font_scroll, magic_scroll;
|
||
static int eraser_scroll, shape_scroll; // dummy variables for now
|
||
|
||
static int eraser_sound;
|
||
|
||
static char texttool_str[256];
|
||
static unsigned int texttool_len;
|
||
|
||
static int tool_avail[NUM_TOOLS], tool_avail_bak[NUM_TOOLS];
|
||
|
||
typedef struct edge_type {
|
||
int y_upper;
|
||
float x_intersect, dx_per_scan;
|
||
struct edge_type * next;
|
||
} edge;
|
||
|
||
|
||
typedef struct point_type {
|
||
int x, y;
|
||
} point_type;
|
||
|
||
typedef struct fpoint_type {
|
||
float x, y;
|
||
} fpoint_type;
|
||
|
||
typedef enum { Left, Right, Bottom, Top } an_edge;
|
||
#define NUM_EDGES 4
|
||
|
||
static SDL_Event scrolltimer_event;
|
||
|
||
int non_left_click_count = 0;
|
||
|
||
|
||
static char * savedir;
|
||
|
||
typedef struct dirent2 {
|
||
struct dirent f;
|
||
int place;
|
||
} dirent2;
|
||
|
||
|
||
/* Local function prototypes: */
|
||
|
||
static void mainloop(void);
|
||
static void brush_draw(int x1, int y1, int x2, int y2, int update);
|
||
static void blit_brush(int x, int y);
|
||
static void magic_draw(int x1, int y1, int x2, int y2, int button_down);
|
||
static void blit_magic(int x, int y, int button_down);
|
||
static void stamp_draw(int x, int y);
|
||
static void rec_undo_buffer(void);
|
||
static void show_usage(FILE * f, char * prg);
|
||
static void setup(int argc, char * argv[]);
|
||
void signal_handler(int sig);
|
||
static SDL_Cursor * get_cursor(unsigned char * bits, unsigned char * mask_bits,
|
||
unsigned int w, unsigned int h,
|
||
unsigned int x, unsigned int y);
|
||
static void seticon(void);
|
||
static SDL_Surface * loadimage(const char * const fname);
|
||
static SDL_Surface * do_loadimage(const char * const fname, int abort_on_error);
|
||
static void draw_toolbar(void);
|
||
static void draw_magic(void);
|
||
static void draw_brushes(void);
|
||
static void draw_stamps(void);
|
||
static void draw_shapes(void);
|
||
static void draw_erasers(void);
|
||
static void draw_fonts(void);
|
||
static void draw_none(void);
|
||
|
||
static void putpixel8(SDL_Surface * surface, int x, int y, Uint32 pixel);
|
||
static void putpixel16(SDL_Surface * surface, int x, int y, Uint32 pixel);
|
||
static void putpixel24(SDL_Surface * surface, int x, int y, Uint32 pixel);
|
||
static void putpixel32(SDL_Surface * surface, int x, int y, Uint32 pixel);
|
||
|
||
static void (*putpixels[])(SDL_Surface *, int, int, Uint32) = {
|
||
putpixel8, putpixel8, putpixel16, putpixel24, putpixel32 };
|
||
|
||
static Uint32 getpixel8(SDL_Surface * surface, int x, int y);
|
||
static Uint32 getpixel16(SDL_Surface * surface, int x, int y);
|
||
static Uint32 getpixel24(SDL_Surface * surface, int x, int y);
|
||
static Uint32 getpixel32(SDL_Surface * surface, int x, int y);
|
||
|
||
static Uint32 (*getpixels[])(SDL_Surface *, int, int) = {
|
||
getpixel8, getpixel8, getpixel16, getpixel24, getpixel32 };
|
||
|
||
static void do_undo(void);
|
||
static void do_redo(void);
|
||
static void render_brush(void);
|
||
static void playsound(int chan, int s, int override);
|
||
static void line_xor(int x1, int y1, int x2, int y2);
|
||
static void rect_xor(int x1, int y1, int x2, int y2);
|
||
|
||
#ifdef LOW_QUALITY_STAMP_OUTLINE
|
||
#define stamp_xor(x,y) rect_xor( \
|
||
(x) - (CUR_STAMP_W+1)/2, \
|
||
(y) - (CUR_STAMP_H+1)/2, \
|
||
(x) + (CUR_STAMP_W+1)/2, \
|
||
(y) + (CUR_STAMP_H+1)/2 \
|
||
)
|
||
#define update_stamp_xor()
|
||
#else
|
||
static void stamp_xor(int x1, int y1);
|
||
static void update_stamp_xor(void);
|
||
#endif
|
||
|
||
static void set_active_stamp(void);
|
||
|
||
static void do_eraser(int x, int y);
|
||
static void disable_avail_tools(void);
|
||
static void enable_avail_tools(void);
|
||
static void reset_avail_tools(void);
|
||
static int compare_dirent2s(struct dirent2 * f1, struct dirent2 * f2);
|
||
static void draw_tux_text(int which_tux, const char * const str,
|
||
int want_right_to_left);
|
||
static void wordwrap_text(const char * const str, SDL_Color color,
|
||
int left, int top, int right,
|
||
int want_right_to_left);
|
||
static char * loaddesc(const char * const fname);
|
||
static double loadinfo(const char * const fname, stamp_type *inf);
|
||
#ifndef NOSOUND
|
||
static Mix_Chunk * loadsound(const char * const fname);
|
||
#endif
|
||
static void do_wait(int counter);
|
||
static void load_current(void);
|
||
static void save_current(void);
|
||
static char * get_fname(const char * const name);
|
||
static int do_prompt_image_flash(const char * const text, const char * const btn_yes, const char * const btn_no, SDL_Surface * img1, SDL_Surface * img2, SDL_Surface * img3, int animate);
|
||
static int do_prompt_image_flash_snd(const char * const text, const char * const btn_yes, const char * const btn_no, SDL_Surface * img1, SDL_Surface * img2, SDL_Surface * img3, int animate, int snd);
|
||
static int do_prompt_image(const char * const text, const char * const btn_yes, const char * const btn_no, SDL_Surface * img1, SDL_Surface * img2, SDL_Surface * img3);
|
||
static int do_prompt_image_snd(const char * const text, const char * const btn_yes, const char * const btn_no, SDL_Surface * img1, SDL_Surface * img2, SDL_Surface * img3, int snd);
|
||
static int do_prompt(const char * const text, const char * const btn_yes, const char * const btn_no);
|
||
static int do_prompt_snd(const char * const text, const char * const btn_yes, const char * const btn_no, int snd);
|
||
static void cleanup(void);
|
||
static void free_cursor(SDL_Cursor ** cursor);
|
||
static void free_surface(SDL_Surface **surface_array);
|
||
static void free_surface_array(SDL_Surface *surface_array[], int count);
|
||
//static void update_shape(int cx, int ox1, int ox2, int cy, int oy1, int oy2,
|
||
// int fixed);
|
||
static void do_shape(int cx, int cy, int ox, int oy, int rotn, int use_brush);
|
||
static int rotation(int ctr_x, int ctr_y, int ox, int oy);
|
||
static int do_save(void);
|
||
static int do_png_save(FILE * fi, const char * const fname, SDL_Surface * surf);
|
||
static void get_new_file_id(void);
|
||
static int do_quit(void);
|
||
void do_open(void);
|
||
static void wait_for_sfx(void);
|
||
static void rgbtohsv(Uint8 r8, Uint8 g8, Uint8 b8, float *h, float *s, float *v);
|
||
static void hsvtorgb(float h, float s, float v, Uint8 *r8, Uint8 *g8, Uint8 *b8);
|
||
|
||
static void show_progress_bar(void);
|
||
static int progress_bar_disabled;
|
||
|
||
SDL_Surface * flip_surface(SDL_Surface *s);
|
||
SDL_Surface * mirror_surface(SDL_Surface *s);
|
||
|
||
static void do_print(void);
|
||
static void strip_trailing_whitespace(char * buf);
|
||
static void do_render_cur_text(int do_blit);
|
||
static void loadfonts(const char * const dir);
|
||
static char * uppercase(char * str);
|
||
static char * textdir(const char * const str);
|
||
static SDL_Surface * do_render_button_label(const char * const label);
|
||
static void create_button_labels(void);
|
||
static int colors_close(Uint32 c1, Uint32 c2);
|
||
static void do_flood_fill(int x, int y, Uint32 cur_colr, Uint32 old_colr);
|
||
static Uint32 scrolltimer_callback(Uint32 interval, void *param);
|
||
static Uint32 drawtext_callback(Uint32 interval, void *param);
|
||
static void control_drawtext_timer(Uint32 interval, const char * const text);
|
||
static void parse_options(FILE * fi);
|
||
static void do_setcursor(SDL_Cursor * c);
|
||
static const char * great_str(void);
|
||
static void draw_image_title(int t, SDL_Rect dest);
|
||
static void handle_keymouse(SDLKey key, Uint8 updown);
|
||
static void handle_active(SDL_Event * event);
|
||
static char * remove_slash(char * path);
|
||
static void anti_carriage_return(int left, int right, int cur_top, int new_top,
|
||
int cur_bot, int line_width);
|
||
static int mySDL_WaitEvent(SDL_Event *event);
|
||
static int mySDL_PollEvent(SDL_Event *event);
|
||
static void load_starter_id(char * saved_id);
|
||
static void load_starter(char * img_id);
|
||
static SDL_Surface * duplicate_surface(SDL_Surface * orig);
|
||
static void mirror_starter(void);
|
||
static void flip_starter(void);
|
||
int valid_click(Uint8 button);
|
||
|
||
#ifdef DEBUG
|
||
static char * debug_gettext(const char * str);
|
||
static int charsize(char c);
|
||
#endif
|
||
|
||
|
||
#define MAX_UTF8_CHAR_LENGTH 6
|
||
|
||
#define USEREVENT_TEXT_UPDATE 1
|
||
|
||
static int bypass_splash_wait;
|
||
|
||
// Wait for a keypress or mouse click
|
||
// counter is in 1/10 second units
|
||
static void do_wait(int counter)
|
||
{
|
||
SDL_Event event;
|
||
int done;
|
||
|
||
if(bypass_splash_wait)
|
||
return;
|
||
|
||
done = 0;
|
||
|
||
do
|
||
{
|
||
while (mySDL_PollEvent(&event))
|
||
{
|
||
if (event.type == SDL_QUIT)
|
||
{
|
||
done = 1;
|
||
|
||
/* FIXME: Handle SDL_Quit better */
|
||
}
|
||
else if (event.type == SDL_ACTIVEEVENT)
|
||
{
|
||
handle_active(&event);
|
||
}
|
||
else if (event.type == SDL_KEYDOWN)
|
||
{
|
||
done = 1;
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
||
valid_click(event.button.button))
|
||
{
|
||
done = 1;
|
||
}
|
||
}
|
||
|
||
counter--;
|
||
SDL_Delay(100);
|
||
}
|
||
while (!done && counter > 0);
|
||
}
|
||
|
||
|
||
// This lets us exit quickly; perhaps the system is swapping to death
|
||
// or the user started Tux Paint by accident. It also lets the user
|
||
// more easily bypass the splash screen wait.
|
||
static void eat_sdl_events(void)
|
||
{
|
||
SDL_Event event;
|
||
while (SDL_PollEvent(&event))
|
||
{
|
||
if (event.type == SDL_QUIT)
|
||
{
|
||
SDL_Quit();
|
||
exit(0); // can't safely use do_quit during start-up
|
||
}
|
||
else if (event.type == SDL_ACTIVEEVENT)
|
||
handle_active(&event);
|
||
else if (event.type == SDL_KEYDOWN)
|
||
{
|
||
SDLKey key = event.key.keysym.sym;
|
||
SDLMod ctrl = event.key.keysym.mod & KMOD_CTRL;
|
||
SDLMod alt = event.key.keysym.mod & KMOD_ALT;
|
||
if (/* key==SDLK_ESCAPE || */ (key==SDLK_c && ctrl) || (key==SDLK_F4 && alt))
|
||
{
|
||
SDL_Quit();
|
||
exit(0);
|
||
}
|
||
else if (key==SDLK_ESCAPE)
|
||
{
|
||
/* FIXME: Abort font loading! */
|
||
}
|
||
else
|
||
bypass_splash_wait = 1;
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONDOWN)
|
||
bypass_splash_wait = 1;
|
||
}
|
||
}
|
||
|
||
|
||
static void show_progress_bar(void)
|
||
{
|
||
SDL_Rect dest, src;
|
||
int x;
|
||
static Uint32 oldtime;
|
||
Uint32 newtime;
|
||
|
||
if(progress_bar_disabled)
|
||
return;
|
||
|
||
newtime = SDL_GetTicks();
|
||
if(newtime > oldtime+15) // trying not to eat some serious CPU time!
|
||
{
|
||
for (x = 0; x < WINDOW_WIDTH; x = x + 65)
|
||
{
|
||
src.x = 65 - (prog_bar_ctr % 65);
|
||
src.y = 0;
|
||
src.w = 65;
|
||
src.h = 24;
|
||
|
||
dest.x = x;
|
||
dest.y = WINDOW_HEIGHT - 24;
|
||
|
||
SDL_BlitSurface(img_progress, &src, screen, &dest);
|
||
}
|
||
|
||
prog_bar_ctr++;
|
||
|
||
SDL_UpdateRect(screen, 0, WINDOW_HEIGHT - 24, WINDOW_WIDTH, 24);
|
||
}
|
||
oldtime = newtime;
|
||
|
||
eat_sdl_events();
|
||
}
|
||
|
||
|
||
|
||
#ifdef __powerpc__
|
||
// Ticks at 1/4 the memory bus clock (24.907667 MHz on Albert's Mac Cube)
|
||
// This is good for 80-second diff or 160-second total.
|
||
#define CLOCK_ASM(tbl) asm volatile("mftb %0" : "=r" (tbl))
|
||
#define CLOCK_TYPE unsigned long
|
||
#ifndef CLOCK_SPEED
|
||
#warning Benchmark times are based on a 99.63 MHz memory bus.
|
||
#define CLOCK_SPEED 24907667.0
|
||
#endif
|
||
#endif
|
||
|
||
#ifdef __i386__
|
||
#define CLOCK_ASM(tbl) asm volatile("rdtsc" : "=A" (tbl))
|
||
#define CLOCK_TYPE unsigned long long
|
||
#ifndef CLOCK_SPEED
|
||
#warning Benchmark times are based on a 450 MHz CPU.
|
||
#define CLOCK_SPEED 450000000.0
|
||
#endif
|
||
#endif
|
||
|
||
#ifndef CLOCK_ASM
|
||
#warning No idea how to read CPU cycles for you, sorry.
|
||
#define CLOCK_ASM(tbl)
|
||
#define CLOCK_TYPE unsigned long
|
||
#define CLOCK_SPEED 1000000000.0
|
||
#endif
|
||
|
||
#ifdef NO_ASM
|
||
#undef CLOCK_ASM
|
||
#define CLOCK_ASM(x) x=42
|
||
#endif
|
||
|
||
/* --- MAIN --- */
|
||
|
||
int main(int argc, char * argv[])
|
||
{
|
||
CLOCK_TYPE time1;
|
||
CLOCK_TYPE time2;
|
||
SDL_Rect dest;
|
||
SDL_Rect src;
|
||
|
||
#ifdef FORKED_FONTS
|
||
run_font_scanner();
|
||
#endif
|
||
|
||
/* Set up locale support */
|
||
setlocale(LC_ALL, "");
|
||
|
||
CLOCK_ASM(time1);
|
||
|
||
/* Set up! */
|
||
setup(argc, argv);
|
||
|
||
|
||
|
||
#if 0
|
||
while(!font_thread_done)
|
||
{
|
||
// FIXME: should respond to quit events
|
||
// FIXME: should have a read-depends memory barrier around here
|
||
show_progress_bar();
|
||
SDL_Delay(20);
|
||
}
|
||
SDL_WaitThread(font_thread, NULL);
|
||
#endif
|
||
|
||
CLOCK_ASM(time2);
|
||
|
||
#ifdef DEBUG
|
||
printf("Start-up time: %.3f\n", (double)(time2-time1)/CLOCK_SPEED);
|
||
#endif
|
||
|
||
// Let the user know we're (nearly) ready now
|
||
|
||
dest.x = 0;
|
||
dest.y = WINDOW_HEIGHT - img_progress->h;
|
||
dest.h = img_progress->h;
|
||
dest.w = WINDOW_WIDTH;
|
||
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
|
||
src.h = img_progress->h;
|
||
src.w = img_title->w;
|
||
src.x = 0;
|
||
src.y = img_title->h - img_progress->h;
|
||
dest.x = ((WINDOW_WIDTH - img_title->w - (img_title_tuxpaint->w / 2)) / 2) + (img_title_tuxpaint->w / 2) + 20;
|
||
SDL_BlitSurface(img_title, &src, screen, &dest);
|
||
|
||
SDL_FreeSurface(img_title);
|
||
SDL_FreeSurface(img_title_credits);
|
||
SDL_FreeSurface(img_title_tuxpaint);
|
||
|
||
dest.x = 0;
|
||
dest.w = WINDOW_WIDTH; // SDL mangles this! So, do repairs.
|
||
update_screen_rect(&dest);
|
||
|
||
do_setcursor(cursor_arrow);
|
||
playsound(0, SND_HARP, 1);
|
||
do_wait(50); // about 5 seconds
|
||
|
||
|
||
/* Set defaults! */
|
||
|
||
cur_undo = 0;
|
||
oldest_undo = 0;
|
||
newest_undo = 0;
|
||
|
||
cur_tool = TOOL_BRUSH;
|
||
cur_color = COLOR_BLACK;
|
||
colors_are_selectable = 1;
|
||
cur_brush = 0;
|
||
cur_stamp = 0;
|
||
cur_shape = SHAPE_SQUARE;
|
||
cur_magic = 0;
|
||
cur_font = 0;
|
||
cur_eraser = 0;
|
||
cursor_left = -1;
|
||
cursor_x = -1;
|
||
cursor_y = -1;
|
||
cursor_textwidth = 0;
|
||
|
||
mouse_x = WINDOW_WIDTH / 2;
|
||
mouse_y = WINDOW_HEIGHT / 2;
|
||
SDL_WarpMouse(mouse_x, mouse_y);
|
||
|
||
mousekey_up = SDL_KEYUP;
|
||
mousekey_down = SDL_KEYUP;
|
||
mousekey_left = SDL_KEYUP;
|
||
mousekey_right = SDL_KEYUP;
|
||
|
||
eraser_sound = 0;
|
||
|
||
img_cur_brush = NULL;
|
||
render_brush();
|
||
|
||
brush_scroll = 0;
|
||
stamp_scroll = 0;
|
||
font_scroll = 0;
|
||
magic_scroll = 0;
|
||
|
||
|
||
reset_avail_tools();
|
||
|
||
|
||
/* Load current image (if any): */
|
||
|
||
load_current();
|
||
been_saved = 1;
|
||
tool_avail[TOOL_SAVE] = 0;
|
||
|
||
|
||
/* Draw the screen! */
|
||
|
||
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
|
||
|
||
draw_toolbar();
|
||
draw_colors(COLORSEL_REFRESH);
|
||
draw_brushes();
|
||
update_canvas(0, 0, WINDOW_WIDTH - 96, (48 * 7) + 40 + HEIGHTOFFSET);
|
||
|
||
SDL_Flip(screen);
|
||
|
||
draw_tux_text(tool_tux[cur_tool], tool_tips[cur_tool], 1);
|
||
|
||
/* Main loop! */
|
||
mainloop();
|
||
|
||
|
||
/* Close and quit! */
|
||
|
||
save_current();
|
||
|
||
wait_for_sfx();
|
||
|
||
|
||
cleanup();
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* FIXME: Move elsewhere!!! */
|
||
|
||
#define PROMPT_QUIT_TXT gettext_noop("Do you really want to quit?")
|
||
#define PROMPT_QUIT_YES gettext_noop("Yes")
|
||
#define PROMPT_QUIT_NO gettext_noop("No")
|
||
|
||
#define PROMPT_QUIT_SAVE_TXT gettext_noop("If you quit, you’ll lose your picture! Save it?")
|
||
#define PROMPT_QUIT_SAVE_YES gettext_noop("Yes")
|
||
#define PROMPT_QUIT_SAVE_NO gettext_noop("No")
|
||
|
||
#define PROMPT_OPEN_SAVE_TXT gettext_noop("Save your picture first?")
|
||
#define PROMPT_OPEN_SAVE_YES gettext_noop("Yes")
|
||
#define PROMPT_OPEN_SAVE_NO gettext_noop("No")
|
||
|
||
#define PROMPT_OPEN_UNOPENABLE_TXT gettext_noop("Can’t open that picture!")
|
||
#define PROMPT_OPEN_UNOPENABLE_YES gettext_noop("OK")
|
||
|
||
#define PROMPT_NEW_TXT gettext_noop("Starting a new picture will erase the current one!")
|
||
#define PROMPT_NEW_YES gettext_noop("That’s OK!")
|
||
#define PROMPT_NEW_NO gettext_noop("Never mind!")
|
||
|
||
#define PROMPT_OPEN_NOFILES_TXT gettext_noop("There are no saved files!")
|
||
#define PROMPT_OPEN_NOFILES_YES gettext_noop("OK")
|
||
|
||
#define PROMPT_PRINT_NOW_TXT gettext_noop("Print your picture now?")
|
||
#define PROMPT_PRINT_NOW_YES gettext_noop("Yes")
|
||
#define PROMPT_PRINT_NOW_NO gettext_noop("No")
|
||
|
||
#define PROMPT_PRINT_TXT gettext_noop("Your picture has been printed!")
|
||
#define PROMPT_PRINT_YES gettext_noop("OK")
|
||
|
||
#define PROMPT_PRINT_TOO_SOON_TXT gettext_noop("You can’t print yet!")
|
||
#define PROMPT_PRINT_TOO_SOON_YES gettext_noop("OK")
|
||
|
||
#define PROMPT_ERASE_TXT gettext_noop("Erase this picture?")
|
||
#define PROMPT_ERASE_YES gettext_noop("Yes")
|
||
#define PROMPT_ERASE_NO gettext_noop("No")
|
||
|
||
#define PROMPT_TIP_LEFTCLICK_TXT gettext_noop("Remember to use the left mouse button!")
|
||
#define PROMPT_TIP_LEFTCLICK_YES gettext_noop("OK")
|
||
|
||
|
||
enum {
|
||
SHAPE_TOOL_MODE_STRETCH,
|
||
SHAPE_TOOL_MODE_ROTATE,
|
||
SHAPE_TOOL_MODE_DONE
|
||
};
|
||
|
||
|
||
/* --- MAIN LOOP! --- */
|
||
|
||
static void mainloop(void)
|
||
{
|
||
int done, which, button_down, old_x, old_y, new_x, new_y,
|
||
line_start_x, line_start_y, shape_tool_mode,
|
||
shape_ctr_x, shape_ctr_y, shape_outer_x, shape_outer_y;
|
||
int num_things;
|
||
int *thing_scroll;
|
||
int cur_thing, do_draw, old_tool,
|
||
tmp_int, max;
|
||
int cur_time, last_print_time, scrolling, ignoring_motion;
|
||
SDL_TimerID scrolltimer;
|
||
SDL_Event event;
|
||
SDLKey key, key_down;
|
||
Uint16 key_unicode;
|
||
SDLMod mod;
|
||
Uint32 last_cursor_blink, cur_cursor_blink,
|
||
pre_event_time, current_event_time;
|
||
|
||
|
||
num_things = num_brushes;
|
||
thing_scroll = &brush_scroll;
|
||
cur_thing = 0;
|
||
do_draw = 0;
|
||
old_x = 0;
|
||
old_y = 0;
|
||
new_x = 0;
|
||
new_y = 0;
|
||
line_start_x = 0;
|
||
line_start_y = 0;
|
||
shape_ctr_x = 0;
|
||
shape_ctr_y = 0;
|
||
shape_outer_x = 0;
|
||
shape_outer_y =0;
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
button_down = 0;
|
||
last_print_time = -print_delay;
|
||
last_cursor_blink = 0;
|
||
texttool_len = 0;
|
||
scrolling = 0;
|
||
scrolltimer = 0;
|
||
key_down = SDLK_LAST;
|
||
key_unicode = 0;
|
||
|
||
|
||
done = 0;
|
||
|
||
do
|
||
{
|
||
ignoring_motion = 0;
|
||
|
||
pre_event_time = SDL_GetTicks();
|
||
|
||
|
||
while (mySDL_PollEvent(&event))
|
||
{
|
||
current_event_time = SDL_GetTicks();
|
||
|
||
if (current_event_time > pre_event_time + 250)
|
||
ignoring_motion = 1;
|
||
|
||
|
||
if (event.type == SDL_QUIT)
|
||
{
|
||
done = do_quit();
|
||
}
|
||
else if (event.type == SDL_ACTIVEEVENT)
|
||
{
|
||
handle_active(&event);
|
||
}
|
||
else if (event.type == SDL_KEYUP)
|
||
{
|
||
key = event.key.keysym.sym;
|
||
|
||
handle_keymouse(key, SDL_KEYUP);
|
||
}
|
||
else if (event.type == SDL_KEYDOWN)
|
||
{
|
||
key = event.key.keysym.sym;
|
||
mod = event.key.keysym.mod;
|
||
|
||
handle_keymouse(key, SDL_KEYDOWN);
|
||
|
||
if (key == SDLK_ESCAPE && !disable_quit)
|
||
{
|
||
done = do_quit();
|
||
}
|
||
else if (key == SDLK_ESCAPE &&
|
||
(mod & KMOD_SHIFT) && (mod & KMOD_CTRL))
|
||
{
|
||
done = do_quit();
|
||
}
|
||
#ifdef WIN32
|
||
else if (key == SDLK_F4 && (mod & KMOD_ALT))
|
||
{
|
||
done = do_quit();
|
||
}
|
||
#endif
|
||
else if (key==SDLK_z && (mod & KMOD_CTRL) && !noshortcuts)
|
||
{
|
||
/* Ctrl-Z - Undo */
|
||
|
||
if (tool_avail[TOOL_UNDO])
|
||
{
|
||
if (cur_undo == newest_undo)
|
||
{
|
||
rec_undo_buffer();
|
||
do_undo();
|
||
}
|
||
do_undo();
|
||
update_screen_rect(&r_tools);
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
}
|
||
}
|
||
else if (key==SDLK_r && (mod & KMOD_CTRL) && !noshortcuts)
|
||
{
|
||
/* Ctrl-R - Redo */
|
||
|
||
if (tool_avail[TOOL_REDO])
|
||
{
|
||
do_redo();
|
||
update_screen_rect(&r_tools);
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
}
|
||
}
|
||
else if (key==SDLK_o && (mod & KMOD_CTRL) && !noshortcuts)
|
||
{
|
||
/* Ctrl-O - Open */
|
||
|
||
disable_avail_tools();
|
||
draw_toolbar();
|
||
draw_colors(COLORSEL_CLOBBER);
|
||
draw_none();
|
||
|
||
do_open();
|
||
|
||
enable_avail_tools();
|
||
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
draw_colors(COLORSEL_REFRESH);
|
||
|
||
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
||
draw_brushes();
|
||
else if (cur_tool == TOOL_MAGIC)
|
||
draw_magic();
|
||
else if (cur_tool == TOOL_STAMP)
|
||
draw_stamps();
|
||
else if (cur_tool == TOOL_TEXT)
|
||
draw_fonts();
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
draw_shapes();
|
||
else if (cur_tool == TOOL_ERASER)
|
||
draw_erasers();
|
||
|
||
draw_tux_text(TUX_GREAT, tool_tips[cur_tool], 1);
|
||
|
||
/* FIXME: Make delay configurable: */
|
||
control_drawtext_timer(1000, tool_tips[cur_tool]);
|
||
}
|
||
else if ( (key==SDLK_n && (mod & KMOD_CTRL)) && !noshortcuts)
|
||
{
|
||
/* Ctrl-N - New */
|
||
|
||
if (do_prompt_snd(PROMPT_NEW_TXT,
|
||
PROMPT_NEW_YES,
|
||
PROMPT_NEW_NO,
|
||
SND_AREYOUSURE))
|
||
{
|
||
free_surface(&img_starter);
|
||
free_surface(&img_starter_bkgd);
|
||
starter_mirrored = 0;
|
||
starter_flipped = 0;
|
||
|
||
SDL_FillRect(canvas, NULL,
|
||
SDL_MapRGB(canvas->format, 255, 255, 255));
|
||
update_canvas(0, 0,
|
||
WINDOW_WIDTH - 96,
|
||
(48 * 7) + 40 + HEIGHTOFFSET);
|
||
|
||
cur_undo = 0;
|
||
oldest_undo = 0;
|
||
newest_undo = 0;
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
|
||
been_saved = 1;
|
||
reset_avail_tools();
|
||
|
||
file_id[0] = '\0';
|
||
starter_id[0] = '\0';
|
||
|
||
playsound(1, SND_HARP, 1);
|
||
}
|
||
else
|
||
{
|
||
draw_tux_text(tool_tux[TUX_DEFAULT], TIP_NEW_ABORT,
|
||
1);
|
||
}
|
||
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
else if (key==SDLK_s && (mod & KMOD_CTRL) && !noshortcuts)
|
||
{
|
||
/* Ctrl-S - Save */
|
||
|
||
if (do_save())
|
||
{
|
||
/* Only think it's been saved if it HAS been saved :^) */
|
||
|
||
been_saved = 1;
|
||
tool_avail[TOOL_SAVE] = 0;
|
||
}
|
||
|
||
/* cur_tool = old_tool; */
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
else
|
||
{
|
||
/* Handle key in text tool: */
|
||
|
||
if (cur_tool == TOOL_TEXT &&
|
||
cursor_x != -1 && cursor_y != -1)
|
||
{
|
||
key_down = key;
|
||
key_unicode = event.key.keysym.unicode;
|
||
|
||
#ifdef DEBUG
|
||
printf("charsize(%c) = %d\n", event.key.keysym.unicode,
|
||
charsize(event.key.keysym.unicode));
|
||
#endif
|
||
|
||
if (key_down == SDLK_BACKSPACE)
|
||
{
|
||
|
||
if (texttool_len > 0)
|
||
{
|
||
texttool_len--;
|
||
texttool_str[texttool_len] = '\0';
|
||
playsound(0, SND_KEYCLICK, 0);
|
||
|
||
do_render_cur_text(0);
|
||
}
|
||
}
|
||
else if (key_down == SDLK_RETURN)
|
||
{
|
||
int font_height;
|
||
if (texttool_len > 0)
|
||
{
|
||
rec_undo_buffer();
|
||
do_render_cur_text(1);
|
||
texttool_len = 0;
|
||
cursor_textwidth = 0;
|
||
}
|
||
font_height = TTF_FontHeight(getfonthandle(cur_font));
|
||
|
||
cursor_x = cursor_left;
|
||
cursor_y = min(cursor_y+font_height, canvas->h-font_height);
|
||
|
||
playsound(0, SND_RETURN, 1);
|
||
}
|
||
else if (key_down == SDLK_TAB)
|
||
{
|
||
if (texttool_len > 0)
|
||
{
|
||
rec_undo_buffer();
|
||
do_render_cur_text(1);
|
||
cursor_x = min(cursor_x + cursor_textwidth,
|
||
canvas->w);
|
||
texttool_len = 0;
|
||
cursor_textwidth = 0;
|
||
}
|
||
}
|
||
else if (isprint(key_unicode))
|
||
{
|
||
if (texttool_len < sizeof(texttool_str) - MAX_UTF8_CHAR_LENGTH)
|
||
{
|
||
int old_cursor_textwidth = cursor_textwidth;
|
||
#ifdef DEBUG
|
||
printf(" key = %c\n"
|
||
"unicode = %c (%d)\n\n",
|
||
key_down, key_unicode, key_unicode);
|
||
#endif
|
||
|
||
texttool_str[texttool_len++] = key_unicode;
|
||
|
||
texttool_str[texttool_len] = '\0';
|
||
do_render_cur_text(0);
|
||
|
||
|
||
if (cursor_x + old_cursor_textwidth <= canvas->w - 50 &&
|
||
cursor_x + cursor_textwidth > canvas->w - 50)
|
||
{
|
||
playsound(0, SND_KEYCLICKRING, 1);
|
||
}
|
||
else
|
||
{
|
||
playsound(0, SND_KEYCLICK, 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
||
valid_click(event.button.button))
|
||
{
|
||
if (HIT(r_tools))
|
||
{
|
||
/* A tool on the left has been pressed! */
|
||
which = GRIDHIT_GD(r_tools,gd_tools);
|
||
|
||
if (which < NUM_TOOLS && tool_avail[which])
|
||
{
|
||
/* Render any current text: */
|
||
|
||
if (cur_tool == TOOL_TEXT && which != TOOL_TEXT &&
|
||
texttool_len > 0)
|
||
{
|
||
if (cursor_x != -1 && cursor_y != -1)
|
||
{
|
||
if (texttool_len > 0)
|
||
{
|
||
rec_undo_buffer();
|
||
do_render_cur_text(1);
|
||
texttool_len = 0;
|
||
cursor_textwidth = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
old_tool = cur_tool;
|
||
cur_tool = which;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
|
||
playsound(1, SND_CLICK, 0);
|
||
|
||
// FIXME: this "if" is just plain gross
|
||
if(cur_tool != TOOL_TEXT)
|
||
draw_tux_text(tool_tux[cur_tool], tool_tips[cur_tool], 1);
|
||
|
||
/* Draw items for this tool: */
|
||
|
||
if (cur_tool == TOOL_BRUSH)
|
||
{
|
||
cur_thing = cur_brush;
|
||
num_things = num_brushes;
|
||
thing_scroll = &brush_scroll;
|
||
draw_brushes();
|
||
draw_colors(COLORSEL_ENABLE);
|
||
}
|
||
else if (cur_tool == TOOL_STAMP)
|
||
{
|
||
cur_thing = cur_stamp;
|
||
num_things = num_stamps;
|
||
thing_scroll = &stamp_scroll;
|
||
draw_stamps();
|
||
draw_colors(stamp_colorable(cur_stamp) ||
|
||
stamp_tintable(cur_stamp));
|
||
set_active_stamp();
|
||
update_stamp_xor();
|
||
}
|
||
else if (cur_tool == TOOL_LINES)
|
||
{
|
||
cur_thing = cur_brush;
|
||
num_things = num_brushes;
|
||
thing_scroll = &brush_scroll;
|
||
draw_brushes();
|
||
draw_colors(COLORSEL_ENABLE);
|
||
}
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
{
|
||
cur_thing = cur_shape;
|
||
num_things = NUM_SHAPES;
|
||
thing_scroll = &shape_scroll;
|
||
draw_shapes();
|
||
draw_colors(COLORSEL_ENABLE);
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
}
|
||
else if (cur_tool == TOOL_TEXT)
|
||
{
|
||
if(!font_thread_done)
|
||
{
|
||
draw_colors(COLORSEL_DISABLE);
|
||
draw_none();
|
||
update_screen_rect(&r_toolopt);
|
||
update_screen_rect(&r_ttoolopt);
|
||
do_setcursor(cursor_watch);
|
||
draw_tux_text(TUX_WAIT, gettext("Please wait..."), 1);
|
||
#ifdef FORKED_FONTS
|
||
receive_some_font_info();
|
||
#else
|
||
while(!font_thread_done)
|
||
{
|
||
// FIXME: should have a read-depends memory barrier around here
|
||
show_progress_bar();
|
||
SDL_Delay(20);
|
||
}
|
||
// FIXME: should kill this in any case
|
||
SDL_WaitThread(font_thread, NULL);
|
||
#endif
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
draw_tux_text(tool_tux[cur_tool], tool_tips[cur_tool], 1);
|
||
|
||
if (num_font_families > 0)
|
||
{
|
||
cur_thing = cur_font;
|
||
num_things = num_font_families;
|
||
thing_scroll = &font_scroll;
|
||
draw_fonts();
|
||
draw_colors(COLORSEL_ENABLE);
|
||
}
|
||
else
|
||
{
|
||
/* Problem using fonts! */
|
||
|
||
cur_tool = old_tool;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
}
|
||
else if (cur_tool == TOOL_MAGIC)
|
||
{
|
||
cur_thing = cur_magic;
|
||
num_things = NUM_MAGICS;
|
||
thing_scroll = &magic_scroll;
|
||
rainbow_color = 0;
|
||
draw_magic();
|
||
draw_colors(magic_colors[cur_magic]);
|
||
}
|
||
else if (cur_tool == TOOL_ERASER)
|
||
{
|
||
cur_thing = cur_eraser;
|
||
num_things = NUM_ERASERS;
|
||
thing_scroll = &eraser_scroll;
|
||
draw_erasers();
|
||
draw_colors(COLORSEL_DISABLE);
|
||
}
|
||
else if (cur_tool == TOOL_UNDO)
|
||
{
|
||
if (cur_undo == newest_undo)
|
||
{
|
||
rec_undo_buffer();
|
||
do_undo();
|
||
}
|
||
do_undo();
|
||
|
||
been_saved = 0;
|
||
|
||
if (!disable_save)
|
||
tool_avail[TOOL_SAVE] = 1;
|
||
|
||
cur_tool = old_tool;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
}
|
||
else if (cur_tool == TOOL_REDO)
|
||
{
|
||
do_redo();
|
||
|
||
been_saved = 0;
|
||
|
||
if (!disable_save)
|
||
tool_avail[TOOL_SAVE] = 1;
|
||
|
||
cur_tool = old_tool;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
}
|
||
else if (cur_tool == TOOL_OPEN)
|
||
{
|
||
disable_avail_tools();
|
||
draw_toolbar();
|
||
draw_colors(COLORSEL_CLOBBER);
|
||
draw_none();
|
||
|
||
do_open();
|
||
|
||
enable_avail_tools();
|
||
|
||
cur_tool = old_tool;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
|
||
draw_tux_text(TUX_GREAT, tool_tips[cur_tool], 1);
|
||
|
||
draw_colors(COLORSEL_REFRESH);
|
||
|
||
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
||
draw_brushes();
|
||
else if (cur_tool == TOOL_MAGIC)
|
||
draw_magic();
|
||
else if (cur_tool == TOOL_STAMP)
|
||
draw_stamps();
|
||
else if (cur_tool == TOOL_TEXT)
|
||
draw_fonts();
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
draw_shapes();
|
||
else if (cur_tool == TOOL_ERASER)
|
||
draw_erasers();
|
||
}
|
||
else if (cur_tool == TOOL_SAVE)
|
||
{
|
||
if (do_save())
|
||
{
|
||
been_saved = 1;
|
||
tool_avail[TOOL_SAVE] = 0;
|
||
}
|
||
|
||
cur_tool = old_tool;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
else if (cur_tool == TOOL_NEW)
|
||
{
|
||
if (do_prompt_snd(PROMPT_NEW_TXT,
|
||
PROMPT_NEW_YES,
|
||
PROMPT_NEW_NO,
|
||
SND_AREYOUSURE))
|
||
{
|
||
free_surface(&img_starter);
|
||
free_surface(&img_starter_bkgd);
|
||
starter_mirrored = 0;
|
||
starter_flipped = 0;
|
||
|
||
SDL_FillRect(canvas, NULL,
|
||
SDL_MapRGB(canvas->format,
|
||
255, 255, 255));
|
||
update_canvas(0, 0,
|
||
WINDOW_WIDTH - 96,
|
||
(48 * 7) + 40 + HEIGHTOFFSET);
|
||
|
||
cur_undo = 0;
|
||
oldest_undo = 0;
|
||
newest_undo = 0;
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
|
||
been_saved = 1;
|
||
reset_avail_tools();
|
||
|
||
file_id[0] = '\0';
|
||
starter_id[0] = '\0';
|
||
|
||
playsound(1, SND_HARP, 1);
|
||
}
|
||
else
|
||
{
|
||
draw_tux_text(tool_tux[TUX_DEFAULT],
|
||
TIP_NEW_ABORT, 1);
|
||
}
|
||
|
||
cur_tool = old_tool;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
else if (cur_tool == TOOL_PRINT)
|
||
{
|
||
cur_time = SDL_GetTicks() / 1000;
|
||
|
||
#ifdef DEBUG
|
||
printf("Current time = %d\n", cur_time);
|
||
#endif
|
||
|
||
if (cur_time >= last_print_time + print_delay)
|
||
{
|
||
if (alt_print_command_default == ALTPRINT_ALWAYS)
|
||
want_alt_printcommand = 1;
|
||
else if (alt_print_command_default == ALTPRINT_NEVER)
|
||
want_alt_printcommand = 0;
|
||
else /* ALTPRINT_MOD */
|
||
want_alt_printcommand = (SDL_GetModState() & KMOD_ALT);
|
||
|
||
if (do_prompt_image_snd(PROMPT_PRINT_NOW_TXT,
|
||
PROMPT_PRINT_NOW_YES,
|
||
PROMPT_PRINT_NOW_NO,
|
||
img_printer, NULL, NULL,
|
||
SND_AREYOUSURE))
|
||
{
|
||
do_print();
|
||
|
||
last_print_time = cur_time;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
do_prompt_image_snd(PROMPT_PRINT_TOO_SOON_TXT,
|
||
PROMPT_PRINT_TOO_SOON_YES,
|
||
"",
|
||
img_printer_wait, NULL, NULL,
|
||
SND_YOUCANNOT);
|
||
}
|
||
|
||
cur_tool = old_tool;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
else if (cur_tool == TOOL_QUIT)
|
||
{
|
||
done = do_quit();
|
||
cur_tool = old_tool;
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
update_screen_rect(&r_toolopt);
|
||
update_screen_rect(&r_ttoolopt);
|
||
}
|
||
}
|
||
else if (HIT(r_toolopt))
|
||
{
|
||
// Options on the right
|
||
// WARNING: this must be kept in sync with the mouse-move
|
||
// code (for cursor changes) and mouse-scroll code.
|
||
|
||
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_STAMP ||
|
||
cur_tool == TOOL_SHAPES || cur_tool == TOOL_LINES ||
|
||
cur_tool == TOOL_MAGIC || cur_tool == TOOL_TEXT ||
|
||
cur_tool == TOOL_ERASER)
|
||
{
|
||
int old_thing;
|
||
int num_rows_needed;
|
||
SDL_Rect r_controls;
|
||
SDL_Rect r_notcontrols;
|
||
SDL_Rect r_items;// = r_notcontrols;
|
||
int toolopt_changed;
|
||
|
||
grid_dims gd_controls = {0,0}; // might become 2-by-2
|
||
grid_dims gd_items = {2,2}; // generally becoming 2-by-whatever
|
||
|
||
/* Note set of things we're dealing with */
|
||
/* (stamps, brushes, etc.) */
|
||
|
||
if (cur_tool == TOOL_STAMP)
|
||
{
|
||
if(!disable_stamp_controls)
|
||
gd_controls = (grid_dims){2,2};
|
||
}
|
||
else if (cur_tool == TOOL_TEXT)
|
||
{
|
||
if(!disable_stamp_controls)
|
||
gd_controls = (grid_dims){2,2};
|
||
}
|
||
|
||
// number of whole or partial rows that will be needed
|
||
// (can make this per-tool if variable columns needed)
|
||
num_rows_needed = (num_things+gd_items.cols-1)/gd_items.cols;
|
||
|
||
do_draw = 0;
|
||
|
||
r_controls.w = r_toolopt.w;
|
||
r_controls.h = gd_controls.rows * button_h;
|
||
r_controls.x = r_toolopt.x;
|
||
r_controls.y = r_toolopt.y + r_toolopt.h - r_controls.h;
|
||
|
||
r_notcontrols.w = r_toolopt.w;
|
||
r_notcontrols.h = r_toolopt.h - r_controls.h;
|
||
r_notcontrols.x = r_toolopt.x;
|
||
r_notcontrols.y = r_toolopt.y;
|
||
|
||
r_items.x = r_notcontrols.x;
|
||
r_items.y = r_notcontrols.y;
|
||
r_items.w = r_notcontrols.w;
|
||
r_items.h = r_notcontrols.h;
|
||
|
||
if(num_rows_needed * button_h > r_items.h)
|
||
{
|
||
// too many; we'll need scroll buttons
|
||
r_items.h -= button_h;
|
||
r_items.y += button_h/2;
|
||
}
|
||
gd_items.rows = r_items.h / button_h;
|
||
|
||
toolopt_changed = 0;
|
||
|
||
if(HIT(r_items))
|
||
{
|
||
which = GRIDHIT_GD(r_items,gd_items) + *thing_scroll;
|
||
|
||
if (which < num_things)
|
||
{
|
||
toolopt_changed = 1;
|
||
#ifndef NOSOUND
|
||
if (cur_tool != TOOL_STAMP || stamp_data[which]->ssnd == NULL)
|
||
{
|
||
playsound(1, SND_BLEEP, 0);
|
||
}
|
||
#endif
|
||
old_thing = cur_thing;
|
||
cur_thing = which;
|
||
do_draw = 1;
|
||
}
|
||
}
|
||
else if(HIT(r_controls))
|
||
{
|
||
which = GRIDHIT_GD(r_controls,gd_controls);
|
||
if (cur_tool == TOOL_STAMP)
|
||
{
|
||
/* Stamp controls! */
|
||
int control_sound = -1;
|
||
if (which&2)
|
||
{
|
||
/* One of the bottom buttons: */
|
||
if (which&1)
|
||
{
|
||
/* Bottom right button: Grow: */
|
||
if (stamp_data[cur_stamp]->size < MAX_STAMP_SIZE)
|
||
{
|
||
stamp_data[cur_stamp]->size++;
|
||
control_sound = SND_GROW;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Bottom left button: Shrink: */
|
||
if (stamp_data[cur_stamp]->size > MIN_STAMP_SIZE)
|
||
{
|
||
stamp_data[cur_stamp]->size--;
|
||
control_sound = SND_SHRINK;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* One of the top buttons: */
|
||
if (which&1)
|
||
{
|
||
/* Top right button: Flip: */
|
||
if (stamp_data[cur_stamp]->flipable)
|
||
{
|
||
stamp_data[cur_stamp]->flipped =
|
||
!stamp_data[cur_stamp]->flipped;
|
||
control_sound = SND_FLIP;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Top left button: Mirror: */
|
||
if (stamp_data[cur_stamp]->mirrorable)
|
||
{
|
||
stamp_data[cur_stamp]->mirrored =
|
||
!stamp_data[cur_stamp]->mirrored;
|
||
control_sound = SND_MIRROR;
|
||
}
|
||
}
|
||
}
|
||
if (control_sound != -1)
|
||
{
|
||
playsound(0, control_sound, 0);
|
||
draw_stamps();
|
||
update_screen_rect(&r_toolopt);
|
||
set_active_stamp();
|
||
update_stamp_xor();
|
||
}
|
||
}
|
||
else // not TOOL_STAMP, so must be TOOL_TEXT
|
||
{
|
||
/* Text controls! */
|
||
int control_sound = -1;
|
||
if (which&2)
|
||
{
|
||
/* One of the bottom buttons: */
|
||
if (which&1)
|
||
{
|
||
/* Bottom right button: Grow: */
|
||
if (text_size < MAX_TEXT_SIZE)
|
||
{
|
||
text_size++;
|
||
control_sound = SND_GROW;
|
||
toolopt_changed = 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Bottom left button: Shrink: */
|
||
if (text_size > MIN_TEXT_SIZE)
|
||
{
|
||
text_size--;
|
||
control_sound = SND_SHRINK;
|
||
toolopt_changed = 1;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* One of the top buttons: */
|
||
if (which&1)
|
||
{
|
||
/* Top right button: Italic: */
|
||
if (text_state & TTF_STYLE_ITALIC)
|
||
{
|
||
text_state &= ~TTF_STYLE_ITALIC;
|
||
control_sound = SND_ITALIC_ON;
|
||
}
|
||
else
|
||
{
|
||
text_state |= TTF_STYLE_ITALIC;
|
||
control_sound = SND_ITALIC_OFF;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Top left button: Bold: */
|
||
if (text_state & TTF_STYLE_BOLD)
|
||
{
|
||
text_state &= ~TTF_STYLE_BOLD;
|
||
control_sound = SND_THIN;
|
||
}
|
||
else
|
||
{
|
||
text_state |= TTF_STYLE_BOLD;
|
||
control_sound = SND_THICK;
|
||
}
|
||
}
|
||
toolopt_changed = 1;
|
||
}
|
||
|
||
|
||
if (control_sound != -1)
|
||
{
|
||
playsound(0, control_sound, 0);
|
||
|
||
|
||
if (cur_tool == TOOL_TEXT) // Huh? It had better be!
|
||
{
|
||
// need to invalidate all the cached user fonts, causing reload on demand
|
||
int i;
|
||
for (i = 0; i < num_font_families; i++)
|
||
{
|
||
if (user_font_families[i] && user_font_families[i]->handle)
|
||
{
|
||
TTF_CloseFont(user_font_families[i]->handle);
|
||
user_font_families[i]->handle = NULL;
|
||
}
|
||
}
|
||
// FIXME: is setting do_draw enough?
|
||
draw_fonts();
|
||
update_screen_rect(&r_toolopt);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// scroll button
|
||
int is_upper = event.button.y < r_toolopt.y + button_h/2;
|
||
if (
|
||
(is_upper && *thing_scroll > 0) // upper arrow
|
||
||
|
||
(!is_upper && *thing_scroll/gd_items.cols < num_rows_needed-gd_items.rows) // lower arrow
|
||
)
|
||
{
|
||
*thing_scroll += is_upper ? -gd_items.cols : gd_items.cols;
|
||
do_draw = 1;
|
||
playsound(1, SND_SCROLL, 1);
|
||
if (!scrolling)
|
||
{
|
||
memcpy(&scrolltimer_event, &event, sizeof(SDL_Event));
|
||
/* FIXME: Make delay value changable: */
|
||
scrolltimer = SDL_AddTimer(REPEAT_SPEED, scrolltimer_callback, (void*) &scrolltimer_event);
|
||
scrolling = 1;
|
||
}
|
||
else
|
||
{
|
||
SDL_RemoveTimer(scrolltimer);
|
||
scrolltimer = SDL_AddTimer(REPEAT_SPEED / 3, scrolltimer_callback, (void*) &scrolltimer_event);
|
||
}
|
||
if (*thing_scroll == 0)
|
||
{
|
||
do_setcursor(cursor_arrow);
|
||
if (scrolling)
|
||
{
|
||
SDL_RemoveTimer(scrolltimer);
|
||
scrolling = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Assign the change(s), if any / redraw, if needed: */
|
||
|
||
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
||
{
|
||
cur_brush = cur_thing;
|
||
render_brush();
|
||
|
||
if (do_draw)
|
||
draw_brushes();
|
||
}
|
||
else if (cur_tool == TOOL_ERASER)
|
||
{
|
||
cur_eraser = cur_thing;
|
||
|
||
if (do_draw)
|
||
draw_erasers();
|
||
}
|
||
else if (cur_tool == TOOL_TEXT)
|
||
{
|
||
char font_tux_text[512];
|
||
|
||
cur_font = cur_thing;
|
||
|
||
snprintf(font_tux_text, sizeof font_tux_text, "%s (%s).", TTF_FontFaceFamilyName(getfonthandle(cur_font)), TTF_FontFaceStyleName(getfonthandle(cur_font)));
|
||
// printf("font change:%s\n", font_tux_text);
|
||
draw_tux_text(TUX_GREAT, font_tux_text, 1);
|
||
|
||
if (do_draw)
|
||
draw_fonts();
|
||
|
||
|
||
// Only rerender when picking a different font
|
||
if (toolopt_changed)
|
||
do_render_cur_text(0);
|
||
}
|
||
else if (cur_tool == TOOL_STAMP)
|
||
{
|
||
#ifndef NOSOUND
|
||
if (toolopt_changed)
|
||
{
|
||
// Only play when picking a different stamp
|
||
if (stamp_data[cur_thing]->ssnd != NULL)
|
||
Mix_PlayChannel(2, stamp_data[cur_thing]->ssnd, 0);
|
||
}
|
||
#endif
|
||
|
||
if (cur_thing != cur_stamp)
|
||
{
|
||
cur_stamp = cur_thing;
|
||
set_active_stamp();
|
||
update_stamp_xor();
|
||
}
|
||
|
||
if (do_draw)
|
||
draw_stamps();
|
||
|
||
if (stamp_data[cur_stamp]->stxt != NULL)
|
||
{
|
||
#ifdef DEBUG
|
||
printf("stamp_data[cur_stamp]->stxt = %s\n",
|
||
stamp_data[cur_stamp]->stxt);
|
||
#endif
|
||
|
||
draw_tux_text(TUX_GREAT,
|
||
stamp_data[cur_stamp]->stxt, 1);
|
||
}
|
||
else
|
||
draw_tux_text(TUX_GREAT, "", 0);
|
||
|
||
/* Enable or disable color selector: */
|
||
draw_colors(stamp_colorable(cur_stamp) || stamp_tintable(cur_stamp));
|
||
}
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
{
|
||
cur_shape = cur_thing;
|
||
|
||
draw_tux_text(TUX_GREAT, shape_tips[cur_shape], 1);
|
||
|
||
if (do_draw)
|
||
draw_shapes();
|
||
}
|
||
else if (cur_tool == TOOL_MAGIC)
|
||
{
|
||
if (cur_thing != cur_magic)
|
||
{
|
||
cur_magic = cur_thing;
|
||
draw_colors(magic_colors[cur_magic]);
|
||
}
|
||
|
||
draw_tux_text(TUX_GREAT, magic_tips[cur_magic], 1);
|
||
|
||
if (do_draw)
|
||
draw_magic();
|
||
}
|
||
|
||
/* Update the screen: */
|
||
if (do_draw)
|
||
update_screen_rect(&r_toolopt);
|
||
}
|
||
}
|
||
else if (HIT(r_colors) && colors_are_selectable)
|
||
{
|
||
/* Color! */
|
||
which = GRIDHIT_GD(r_colors,gd_colors);
|
||
|
||
if (which < NUM_COLORS)
|
||
{
|
||
cur_color = which;
|
||
playsound(1, SND_BUBBLE, 1);
|
||
draw_colors(COLORSEL_REFRESH);
|
||
render_brush();
|
||
draw_tux_text(TUX_KISS, color_names[cur_color], 1);
|
||
|
||
if (cur_tool == TOOL_TEXT)
|
||
do_render_cur_text(0);
|
||
}
|
||
}
|
||
else if (HIT(r_canvas))
|
||
{
|
||
/* Draw something! */
|
||
|
||
old_x = event.button.x - r_canvas.x;
|
||
old_y = event.button.y - r_canvas.y;
|
||
|
||
if (been_saved)
|
||
{
|
||
been_saved = 0;
|
||
|
||
if (!disable_save)
|
||
tool_avail[TOOL_SAVE] = 1;
|
||
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
|
||
if (cur_tool == TOOL_BRUSH)
|
||
{
|
||
/* Start painting! */
|
||
|
||
rec_undo_buffer();
|
||
|
||
/* (Arbitrarily large, so we draw once now) */
|
||
brush_counter = 999;
|
||
|
||
brush_draw(old_x, old_y, old_x, old_y, 1);
|
||
playsound(0, SND_PAINT1 + (img_cur_brush->w) / 12, 1);
|
||
}
|
||
else if (cur_tool == TOOL_STAMP)
|
||
{
|
||
/* Draw a stamp! */
|
||
|
||
rec_undo_buffer();
|
||
|
||
stamp_draw(old_x, old_y);
|
||
stamp_xor(old_x, old_y);
|
||
playsound(1, SND_STAMP, 1);
|
||
|
||
draw_tux_text(TUX_GREAT, great_str(), 1);
|
||
|
||
/* FIXME: Make delay configurable: */
|
||
|
||
control_drawtext_timer(1000, stamp_data[cur_stamp]->stxt);
|
||
}
|
||
else if (cur_tool == TOOL_LINES)
|
||
{
|
||
/* Start a line! */
|
||
|
||
rec_undo_buffer();
|
||
|
||
line_start_x = old_x;
|
||
line_start_y = old_y;
|
||
|
||
/* (Arbitrarily large, so we draw once now) */
|
||
brush_counter = 999;
|
||
|
||
brush_draw(old_x, old_y, old_x, old_y, 1);
|
||
|
||
playsound(1, SND_LINE_START, 1);
|
||
draw_tux_text(TUX_BORED, TIP_LINE_START, 1);
|
||
}
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
{
|
||
if (shape_tool_mode == SHAPE_TOOL_MODE_DONE)
|
||
{
|
||
/* Start drawing a shape! */
|
||
|
||
rec_undo_buffer();
|
||
|
||
shape_ctr_x = old_x;
|
||
shape_ctr_y = old_y;
|
||
|
||
shape_tool_mode = SHAPE_TOOL_MODE_STRETCH;
|
||
|
||
playsound(1, SND_LINE_START, 1);
|
||
draw_tux_text(TUX_BORED, TIP_SHAPE_START, 1);
|
||
}
|
||
else if (shape_tool_mode == SHAPE_TOOL_MODE_ROTATE)
|
||
{
|
||
/* Draw the shape with the brush! */
|
||
|
||
/* (Arbitrarily large...) */
|
||
brush_counter = 999;
|
||
|
||
playsound(1, SND_LINE_END, 1);
|
||
do_shape(shape_ctr_x, shape_ctr_y,
|
||
shape_outer_x, shape_outer_y,
|
||
rotation(shape_ctr_x, shape_ctr_y,
|
||
event.button.x - r_canvas.x,
|
||
event.button.y - r_canvas.y),
|
||
1);
|
||
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
draw_tux_text(TUX_GREAT, tool_tips[TOOL_SHAPES], 1);
|
||
}
|
||
}
|
||
else if (cur_tool == TOOL_MAGIC)
|
||
{
|
||
/* Start doing magic! */
|
||
|
||
tmp_int = cur_undo;
|
||
rec_undo_buffer();
|
||
|
||
|
||
/* Mirror or flip, make a note so we record it for
|
||
the starters, too! */
|
||
|
||
if (cur_magic == MAGIC_MIRROR)
|
||
undo_starters[tmp_int] = UNDO_STARTER_MIRRORED;
|
||
else if (cur_magic == MAGIC_FLIP)
|
||
undo_starters[tmp_int] = UNDO_STARTER_FLIPPED;
|
||
|
||
|
||
/* (Arbitrarily large, so we draw once now) */
|
||
brush_counter = 999;
|
||
|
||
if (cur_magic != MAGIC_FILL)
|
||
{
|
||
magic_draw(old_x, old_y, old_x, old_y, button_down);
|
||
}
|
||
else
|
||
{
|
||
do_flood_fill(old_x, old_y,
|
||
SDL_MapRGB(canvas->format,
|
||
color_hexes[cur_color][0],
|
||
color_hexes[cur_color][1],
|
||
color_hexes[cur_color][2]),
|
||
getpixels[canvas->format->BytesPerPixel](canvas, old_x, old_y));
|
||
|
||
draw_tux_text(TUX_GREAT, magic_tips[MAGIC_FILL], 1);
|
||
}
|
||
|
||
if (cur_magic == MAGIC_FLIP ||
|
||
cur_magic == MAGIC_MIRROR ||
|
||
cur_magic == MAGIC_FILL)
|
||
{
|
||
update_canvas(0, 0, canvas->w, canvas->h);
|
||
}
|
||
}
|
||
else if (cur_tool == TOOL_ERASER)
|
||
{
|
||
/* Erase! */
|
||
|
||
rec_undo_buffer();
|
||
|
||
do_eraser(old_x, old_y);
|
||
}
|
||
else if (cur_tool == TOOL_TEXT)
|
||
{
|
||
/* Text Tool! */
|
||
|
||
if (cursor_x != -1 && cursor_y != -1)
|
||
{
|
||
/*
|
||
if (texttool_len > 0)
|
||
{
|
||
rec_undo_buffer();
|
||
do_render_cur_text(1);
|
||
texttool_len = 0;
|
||
}
|
||
*/
|
||
}
|
||
|
||
cursor_x = old_x;
|
||
cursor_y = old_y;
|
||
cursor_left = old_x;
|
||
|
||
do_render_cur_text(0);
|
||
}
|
||
|
||
button_down = 1;
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
||
wheely &&
|
||
event.button.button >= 4 &&
|
||
event.button.button <= 5)
|
||
{
|
||
int num_rows_needed;
|
||
SDL_Rect r_controls;
|
||
SDL_Rect r_notcontrols;
|
||
SDL_Rect r_items;// = r_notcontrols;
|
||
|
||
// Scroll wheel code.
|
||
// WARNING: this must be kept in sync with the mouse-move
|
||
// code (for cursor changes) and mouse-click code.
|
||
|
||
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_STAMP ||
|
||
cur_tool == TOOL_SHAPES || cur_tool == TOOL_LINES ||
|
||
cur_tool == TOOL_MAGIC || cur_tool == TOOL_TEXT ||
|
||
cur_tool == TOOL_ERASER)
|
||
{
|
||
grid_dims gd_controls = {0,0}; // might become 2-by-2
|
||
grid_dims gd_items = {2,2}; // generally becoming 2-by-whatever
|
||
|
||
/* Note set of things we're dealing with */
|
||
/* (stamps, brushes, etc.) */
|
||
|
||
if (cur_tool == TOOL_STAMP)
|
||
{
|
||
if(!disable_stamp_controls)
|
||
gd_controls = (grid_dims){2,2};
|
||
}
|
||
else if (cur_tool == TOOL_TEXT)
|
||
{
|
||
if(!disable_stamp_controls)
|
||
gd_controls = (grid_dims){2,2};
|
||
}
|
||
|
||
// number of whole or partial rows that will be needed
|
||
// (can make this per-tool if variable columns needed)
|
||
num_rows_needed = (num_things+gd_items.cols-1)/gd_items.cols;
|
||
|
||
do_draw = 0;
|
||
|
||
r_controls.w = r_toolopt.w;
|
||
r_controls.h = gd_controls.rows * button_h;
|
||
r_controls.x = r_toolopt.x;
|
||
r_controls.y = r_toolopt.y + r_toolopt.h - r_controls.h;
|
||
|
||
r_notcontrols.w = r_toolopt.w;
|
||
r_notcontrols.h = r_toolopt.h - r_controls.h;
|
||
r_notcontrols.x = r_toolopt.x;
|
||
r_notcontrols.y = r_toolopt.y;
|
||
|
||
r_items.x = r_notcontrols.x;
|
||
r_items.y = r_notcontrols.y;
|
||
r_items.w = r_notcontrols.w;
|
||
r_items.h = r_notcontrols.h;
|
||
|
||
if(num_rows_needed * button_h > r_items.h)
|
||
{
|
||
// too many; we'll need scroll buttons
|
||
r_items.h -= button_h;
|
||
r_items.y += button_h/2;
|
||
}
|
||
gd_items.rows = r_items.h / button_h;
|
||
|
||
if(0)
|
||
{
|
||
}
|
||
else
|
||
{
|
||
// scroll button
|
||
int is_upper = (event.button.button == 4);
|
||
if (
|
||
(is_upper && *thing_scroll > 0) // upper arrow
|
||
||
|
||
(!is_upper && *thing_scroll/gd_items.cols < num_rows_needed-gd_items.rows) // lower arrow
|
||
)
|
||
{
|
||
*thing_scroll += is_upper ? -gd_items.cols : gd_items.cols;
|
||
do_draw = 1;
|
||
playsound(1, SND_SCROLL, 1);
|
||
#if 0
|
||
if (!scrolling)
|
||
{
|
||
memcpy(&scrolltimer_event, &event, sizeof(SDL_Event));
|
||
/* FIXME: Make delay value changable: */
|
||
scrolltimer = SDL_AddTimer(REPEAT_SPEED, scrolltimer_callback, (void*) &scrolltimer_event);
|
||
scrolling = 1;
|
||
}
|
||
else
|
||
{
|
||
SDL_RemoveTimer(scrolltimer);
|
||
scrolltimer = SDL_AddTimer(REPEAT_SPEED / 3, scrolltimer_callback, (void*) &scrolltimer_event);
|
||
}
|
||
#endif
|
||
if (*thing_scroll == 0)
|
||
{
|
||
do_setcursor(cursor_arrow);
|
||
#if 0
|
||
if (scrolling)
|
||
{
|
||
SDL_RemoveTimer(scrolltimer);
|
||
scrolling = 0;
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Assign the change(s), if any / redraw, if needed: */
|
||
|
||
if (cur_tool == TOOL_BRUSH || cur_tool == TOOL_LINES)
|
||
{
|
||
if (do_draw)
|
||
draw_brushes();
|
||
}
|
||
else if (cur_tool == TOOL_ERASER)
|
||
{
|
||
if (do_draw)
|
||
draw_erasers();
|
||
}
|
||
else if (cur_tool == TOOL_TEXT)
|
||
{
|
||
if (do_draw)
|
||
draw_fonts();
|
||
}
|
||
else if (cur_tool == TOOL_STAMP)
|
||
{
|
||
if (do_draw)
|
||
draw_stamps();
|
||
}
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
{
|
||
if (do_draw)
|
||
draw_shapes();
|
||
}
|
||
else if (cur_tool == TOOL_MAGIC)
|
||
{
|
||
if (do_draw)
|
||
draw_magic();
|
||
}
|
||
|
||
/* Update the screen: */
|
||
if (do_draw)
|
||
update_screen_rect(&r_toolopt);
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
||
event.button.button >= 2 &&
|
||
event.button.button <= 3)
|
||
{
|
||
/* They're using the middle or right mouse buttons! */
|
||
|
||
non_left_click_count++;
|
||
|
||
|
||
if (non_left_click_count == 10 ||
|
||
non_left_click_count == 20 ||
|
||
(non_left_click_count % 50) == 0)
|
||
{
|
||
/* Pop up an informative animation: */
|
||
|
||
do_prompt_image_flash(PROMPT_TIP_LEFTCLICK_TXT,
|
||
PROMPT_TIP_LEFTCLICK_YES,
|
||
"",
|
||
img_mouse, img_mouse_click, NULL, 1);
|
||
}
|
||
}
|
||
else if (event.type == SDL_USEREVENT)
|
||
{
|
||
if (event.user.code == USEREVENT_TEXT_UPDATE)
|
||
{
|
||
/* Time to replace "Great!" with old tip text: */
|
||
|
||
if (event.user.data1 != NULL)
|
||
{
|
||
if (((unsigned char *) event.user.data1)[0] == '=')
|
||
{
|
||
draw_tux_text(TUX_GREAT,
|
||
(char *) event.user.data1 + 1, 1);
|
||
}
|
||
else
|
||
{
|
||
draw_tux_text(TUX_GREAT,
|
||
(char *) event.user.data1, 0);
|
||
}
|
||
}
|
||
else
|
||
draw_tux_text(TUX_GREAT, "", 1);
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONUP)
|
||
{
|
||
if (scrolling)
|
||
{
|
||
SDL_RemoveTimer(scrolltimer);
|
||
scrolling = 0;
|
||
}
|
||
|
||
if (button_down)
|
||
{
|
||
if (cur_tool == TOOL_LINES)
|
||
{
|
||
/* (Arbitrarily large, so we draw once now) */
|
||
brush_counter = 999;
|
||
|
||
brush_draw(line_start_x, line_start_y,
|
||
event.button.x-r_canvas.x, event.button.y-r_canvas.y, 1);
|
||
brush_draw(event.button.x-r_canvas.x, event.button.y-r_canvas.y,
|
||
event.button.x-r_canvas.x, event.button.y-r_canvas.y, 1);
|
||
|
||
playsound(1, SND_LINE_END, 1);
|
||
draw_tux_text(TUX_GREAT, tool_tips[TOOL_LINES], 1);
|
||
}
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
{
|
||
if (shape_tool_mode == SHAPE_TOOL_MODE_STRETCH)
|
||
{
|
||
/* Now we can rotate the shape... */
|
||
|
||
shape_outer_x = event.button.x-r_canvas.x;
|
||
shape_outer_y = event.button.y-r_canvas.y;
|
||
|
||
if (!simple_shapes && !shape_no_rotate[cur_shape])
|
||
{
|
||
shape_tool_mode = SHAPE_TOOL_MODE_ROTATE;
|
||
|
||
SDL_WarpMouse(shape_outer_x + 96, shape_ctr_y);
|
||
do_setcursor(cursor_rotate);
|
||
|
||
|
||
/* Erase stretchy XOR: */
|
||
|
||
do_shape(shape_ctr_x, shape_ctr_y, old_x, old_y,
|
||
0, 0);
|
||
|
||
/* Make an initial rotation XOR to be erased: */
|
||
|
||
do_shape(shape_ctr_x, shape_ctr_y,
|
||
shape_outer_x, shape_outer_y,
|
||
rotation(shape_ctr_x, shape_ctr_y,
|
||
shape_outer_x, shape_outer_y),
|
||
0);
|
||
|
||
playsound(1, SND_LINE_START, 1);
|
||
draw_tux_text(TUX_BORED, TIP_SHAPE_NEXT, 1);
|
||
|
||
|
||
/* FIXME: Do something less intensive! */
|
||
|
||
SDL_Flip(screen);
|
||
}
|
||
else
|
||
{
|
||
brush_counter = 999; /* arbitrarily large... */
|
||
|
||
|
||
playsound(1, SND_LINE_END, 1);
|
||
do_shape(shape_ctr_x, shape_ctr_y,
|
||
shape_outer_x, shape_outer_y,
|
||
0, 1);
|
||
|
||
SDL_Flip(screen);
|
||
|
||
shape_tool_mode = SHAPE_TOOL_MODE_DONE;
|
||
draw_tux_text(TUX_GREAT,
|
||
tool_tips[TOOL_SHAPES], 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
button_down = 0;
|
||
}
|
||
else if (event.type == SDL_MOUSEMOTION && !ignoring_motion)
|
||
{
|
||
new_x = event.button.x-r_canvas.x;
|
||
new_y = event.button.y-r_canvas.y;
|
||
|
||
|
||
/* FIXME: Is doing this every event too intensive? */
|
||
/* Should I check current cursor first? */
|
||
|
||
if (HIT(r_tools))
|
||
{
|
||
/* Tools: */
|
||
|
||
if (tool_avail[((event.button.x - r_tools.x) / button_w) +
|
||
((event.button.y - r_tools.y) / button_h) * gd_tools.cols])
|
||
{
|
||
do_setcursor(cursor_hand);
|
||
}
|
||
else
|
||
{
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
}
|
||
else if (HIT(r_colors))
|
||
{
|
||
/* Color picker: */
|
||
|
||
if (colors_are_selectable)
|
||
do_setcursor(cursor_hand);
|
||
else
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
else if (HIT(r_toolopt))
|
||
{
|
||
// mouse cursor code
|
||
// WARNING: this must be kept in sync with the mouse-click
|
||
// and mouse-click code. (it isn't, currently!)
|
||
|
||
/* Note set of things we're dealing with */
|
||
/* (stamps, brushes, etc.) */
|
||
|
||
if (cur_tool == TOOL_STAMP)
|
||
{
|
||
}
|
||
else if (cur_tool == TOOL_TEXT)
|
||
{
|
||
}
|
||
|
||
max = 14;
|
||
if (cur_tool == TOOL_STAMP && !disable_stamp_controls)
|
||
max = 10;
|
||
if (cur_tool == TOOL_TEXT && !disable_stamp_controls)
|
||
max = 10;
|
||
|
||
|
||
if (num_things > max + TOOLOFFSET)
|
||
{
|
||
/* Are there scroll buttons? */
|
||
|
||
if (event.button.y < 40 + 24)
|
||
{
|
||
/* Up button; is it available? */
|
||
|
||
if (*thing_scroll > 0)
|
||
do_setcursor(cursor_up);
|
||
else
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
else if (event.button.y > (48 * ((max - 2) / 2 + TOOLOFFSET / 2)) + 40 + 24 &&
|
||
event.button.y <= (48 * ((max - 2) / 2 + TOOLOFFSET / 2)) + 40 + 24 + 24)
|
||
{
|
||
/* Down button; is it available? */
|
||
|
||
if (*thing_scroll < num_things - (max - 2))
|
||
do_setcursor(cursor_down);
|
||
else
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
else
|
||
{
|
||
/* One of the selectors: */
|
||
|
||
which = ((event.button.y - 40 - 24) / 48) * 2 +
|
||
(event.button.x - (WINDOW_WIDTH - 96)) / 48;
|
||
|
||
if (which < num_things)
|
||
do_setcursor(cursor_hand);
|
||
else
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* No scroll buttons - must be a selector: */
|
||
|
||
which = ((event.button.y - 40) / 48) * 2 +
|
||
(event.button.x - (WINDOW_WIDTH - 96)) / 48;
|
||
|
||
if (which < num_things)
|
||
do_setcursor(cursor_hand);
|
||
else
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
}
|
||
else if (HIT(r_canvas))
|
||
{
|
||
/* Canvas: */
|
||
|
||
if (cur_tool == TOOL_BRUSH)
|
||
do_setcursor(cursor_brush);
|
||
else if (cur_tool == TOOL_STAMP)
|
||
do_setcursor(cursor_tiny);
|
||
else if (cur_tool == TOOL_LINES)
|
||
do_setcursor(cursor_crosshair);
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
{
|
||
if (shape_tool_mode != SHAPE_TOOL_MODE_ROTATE)
|
||
do_setcursor(cursor_crosshair);
|
||
else
|
||
do_setcursor(cursor_rotate);
|
||
}
|
||
else if (cur_tool == TOOL_TEXT)
|
||
do_setcursor(cursor_insertion);
|
||
else if (cur_tool == TOOL_MAGIC)
|
||
do_setcursor(cursor_wand);
|
||
else if (cur_tool == TOOL_ERASER)
|
||
do_setcursor(cursor_tiny);
|
||
}
|
||
else
|
||
{
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
|
||
|
||
if (button_down)
|
||
{
|
||
if (cur_tool == TOOL_BRUSH)
|
||
{
|
||
/* Pushing button and moving: Draw with the brush: */
|
||
|
||
brush_draw(old_x, old_y, new_x, new_y, 1);
|
||
|
||
playsound(0, SND_PAINT1 + (img_cur_brush->w) / 12, 0);
|
||
}
|
||
else if (cur_tool == TOOL_LINES)
|
||
{
|
||
/* Still pushing button, while moving:
|
||
Draw XOR where line will go: */
|
||
|
||
line_xor(line_start_x, line_start_y, old_x, old_y);
|
||
|
||
line_xor(line_start_x, line_start_y, new_x, new_y);
|
||
|
||
update_screen(line_start_x + r_canvas.x, line_start_y + r_canvas.y,
|
||
old_x + r_canvas.x, old_y + r_canvas.y);
|
||
update_screen(line_start_x + r_canvas.x, line_start_y + r_canvas.y,
|
||
new_x + r_canvas.x, new_y + r_canvas.y);
|
||
}
|
||
else if (cur_tool == TOOL_SHAPES)
|
||
{
|
||
/* Still pushing button, while moving:
|
||
Draw XOR where shape will go: */
|
||
|
||
if (shape_tool_mode == SHAPE_TOOL_MODE_STRETCH)
|
||
{
|
||
do_shape(shape_ctr_x, shape_ctr_y, old_x, old_y,
|
||
0, 0);
|
||
|
||
do_shape(shape_ctr_x, shape_ctr_y,
|
||
new_x, new_y,
|
||
0, 0);
|
||
|
||
|
||
/* FIXME: Fix update shape function! */
|
||
|
||
/* update_shape(shape_ctr_x, old_x, new_x,
|
||
shape_ctr_y, old_y, new_y,
|
||
shape_locked[cur_shape]); */
|
||
|
||
SDL_Flip(screen);
|
||
}
|
||
}
|
||
else if (cur_tool == TOOL_MAGIC)
|
||
{
|
||
/* Pushing button and moving: Do the magic: */
|
||
|
||
if (cur_magic != MAGIC_FLIP &&
|
||
cur_magic != MAGIC_MIRROR &&
|
||
cur_magic != MAGIC_FILL)
|
||
{
|
||
magic_draw(old_x, old_y, new_x, new_y, button_down);
|
||
}
|
||
}
|
||
else if (cur_tool == TOOL_ERASER)
|
||
{
|
||
/* Still pushing, and moving - Erase! */
|
||
|
||
do_eraser(new_x, new_y);
|
||
}
|
||
}
|
||
|
||
|
||
if (cur_tool == TOOL_STAMP ||
|
||
(cur_tool == TOOL_ERASER && !button_down))
|
||
{
|
||
int w, h;
|
||
/* Moving: Draw XOR where stamp/eraser will apply: */
|
||
|
||
if (cur_tool == TOOL_STAMP)
|
||
{
|
||
w = active_stamp->w;
|
||
h = active_stamp->h;
|
||
}
|
||
else
|
||
{
|
||
w = (ERASER_MIN +
|
||
((NUM_ERASERS - cur_eraser - 1) *
|
||
((ERASER_MAX - ERASER_MIN) / (NUM_ERASERS - 1))));
|
||
|
||
h = w;
|
||
}
|
||
|
||
if (old_x >= 0 && old_x < r_canvas.w &&
|
||
old_y >= 0 && old_y < r_canvas.h)
|
||
{
|
||
if (cur_tool == TOOL_STAMP)
|
||
{
|
||
stamp_xor(old_x, old_y);
|
||
|
||
update_screen(old_x - (CUR_STAMP_W+1)/2 + r_canvas.x,
|
||
old_y - (CUR_STAMP_H+1)/2 + r_canvas.y,
|
||
old_x + (CUR_STAMP_W+1)/2 + r_canvas.x,
|
||
old_y + (CUR_STAMP_H+1)/2 + r_canvas.y);
|
||
}
|
||
else
|
||
{
|
||
rect_xor(old_x - w / 2, old_y - h / 2,
|
||
old_x + w / 2, old_y + h / 2);
|
||
|
||
update_screen(old_x - w / 2 + r_canvas.x, old_y - h / 2 + r_canvas.y,
|
||
old_x + w / 2 + r_canvas.x, old_y + h / 2 + r_canvas.y);
|
||
}
|
||
}
|
||
|
||
if (new_x >= 0 && new_x < r_canvas.w &&
|
||
new_y >= 0 && new_y < r_canvas.h)
|
||
{
|
||
if (cur_tool == TOOL_STAMP)
|
||
{
|
||
stamp_xor(new_x, new_y);
|
||
|
||
update_screen(old_x - (CUR_STAMP_W+1)/2 + r_canvas.x,
|
||
old_y - (CUR_STAMP_H+1)/2 + r_canvas.y,
|
||
old_x + (CUR_STAMP_W+1)/2 + r_canvas.x,
|
||
old_y + (CUR_STAMP_H+1)/2 + r_canvas.y);
|
||
}
|
||
else
|
||
{
|
||
rect_xor(new_x - w / 2, new_y - h / 2,
|
||
new_x + w / 2, new_y + h / 2);
|
||
|
||
update_screen(new_x - w / 2 + r_canvas.x, new_y - h / 2 + r_canvas.y,
|
||
new_x + w / 2 + r_canvas.x, new_y + h / 2 + r_canvas.y);
|
||
}
|
||
}
|
||
}
|
||
else if (cur_tool == TOOL_SHAPES &&
|
||
shape_tool_mode == SHAPE_TOOL_MODE_ROTATE)
|
||
{
|
||
do_shape(shape_ctr_x, shape_ctr_y,
|
||
shape_outer_x, shape_outer_y,
|
||
rotation(shape_ctr_x, shape_ctr_y,
|
||
old_x, old_y), 0);
|
||
|
||
|
||
do_shape(shape_ctr_x, shape_ctr_y,
|
||
shape_outer_x, shape_outer_y,
|
||
rotation(shape_ctr_x, shape_ctr_y,
|
||
new_x, new_y), 0);
|
||
|
||
|
||
/* FIXME: Do something less intensive! */
|
||
SDL_Flip(screen);
|
||
}
|
||
|
||
old_x = new_x;
|
||
old_y = new_y;
|
||
}
|
||
}
|
||
|
||
|
||
SDL_Delay(10);
|
||
|
||
cur_cursor_blink = SDL_GetTicks();
|
||
|
||
|
||
if (cur_tool == TOOL_TEXT && cursor_x != -1 && cursor_y != -1 &&
|
||
cur_cursor_blink > last_cursor_blink + CURSOR_BLINK_SPEED)
|
||
{
|
||
last_cursor_blink = SDL_GetTicks();
|
||
|
||
line_xor(cursor_x + cursor_textwidth, cursor_y,
|
||
cursor_x + cursor_textwidth,
|
||
cursor_y + TTF_FontHeight(getfonthandle(cur_font)));
|
||
|
||
update_screen(cursor_x + r_canvas.x + cursor_textwidth,
|
||
cursor_y + r_canvas.y,
|
||
cursor_x + r_canvas.x + cursor_textwidth,
|
||
cursor_y + r_canvas.y + TTF_FontHeight(getfonthandle(cur_font)));
|
||
}
|
||
}
|
||
while (!done);
|
||
}
|
||
|
||
|
||
/* Draw using the current brush: */
|
||
|
||
static void brush_draw(int x1, int y1, int x2, int y2, int update)
|
||
{
|
||
int dx, dy, y;
|
||
int orig_x1, orig_y1, orig_x2, orig_y2, tmp;
|
||
float m, b;
|
||
|
||
orig_x1 = x1;
|
||
orig_y1 = y1;
|
||
|
||
orig_x2 = x2;
|
||
orig_y2 = y2;
|
||
|
||
|
||
x1 = x1 - (img_brushes[cur_brush]->w / 2);
|
||
y1 = y1 - (img_brushes[cur_brush]->h / 2);
|
||
|
||
x2 = x2 - (img_brushes[cur_brush]->w / 2);
|
||
y2 = y2 - (img_brushes[cur_brush]->h / 2);
|
||
|
||
|
||
dx = x2 - x1;
|
||
dy = y2 - y1;
|
||
|
||
if (dx != 0)
|
||
{
|
||
m = ((float) dy) / ((float) dx);
|
||
b = y1 - m * x1;
|
||
|
||
if (x2 >= x1)
|
||
dx = 1;
|
||
else
|
||
dx = -1;
|
||
|
||
|
||
while (x1 != x2)
|
||
{
|
||
y1 = m * x1 + b;
|
||
y2 = m * (x1 + dx) + b;
|
||
|
||
if (y1 > y2)
|
||
{
|
||
y = y1;
|
||
y1 = y2;
|
||
y2 = y;
|
||
}
|
||
|
||
for (y = y1; y <= y2; y++)
|
||
blit_brush(x1, y);
|
||
|
||
x1 = x1 + dx;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (y1 > y2)
|
||
{
|
||
y = y1;
|
||
y1 = y2;
|
||
y2 = y;
|
||
}
|
||
|
||
for (y = y1; y <= y2; y++)
|
||
blit_brush(x1, y);
|
||
}
|
||
|
||
if (orig_x1 > orig_x2)
|
||
{
|
||
tmp = orig_x1;
|
||
orig_x1 = orig_x2;
|
||
orig_x2 = tmp;
|
||
}
|
||
|
||
if (orig_y1 > orig_y2)
|
||
{
|
||
tmp = orig_y1;
|
||
orig_y1 = orig_y2;
|
||
orig_y2 = tmp;
|
||
}
|
||
|
||
|
||
if (update)
|
||
{
|
||
update_canvas(orig_x1 - (img_brushes[cur_brush]->w / 2),
|
||
orig_y1 - (img_brushes[cur_brush]->h / 2),
|
||
orig_x2 + (img_brushes[cur_brush]->w / 2),
|
||
orig_y2 + (img_brushes[cur_brush]->h / 2));
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw the current brush in the current color: */
|
||
|
||
static void blit_brush(int x, int y)
|
||
{
|
||
SDL_Rect dest;
|
||
|
||
brush_counter++;
|
||
|
||
if (brush_counter >= (img_cur_brush->h / 4))
|
||
{
|
||
brush_counter = 0;
|
||
|
||
dest.x = x;
|
||
dest.y = y;
|
||
|
||
SDL_BlitSurface(img_cur_brush, NULL, canvas, &dest);
|
||
}
|
||
}
|
||
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
// stamp tinter
|
||
|
||
#define TINTER_ANYHUE 0 // like normal, but remaps all hues in the stamp
|
||
#define TINTER_NARROW 1 // like normal, but narrow hue angle
|
||
#define TINTER_NORMAL 2 // normal
|
||
#define TINTER_VECTOR 3 // map black->white to black->destination
|
||
|
||
// This goes from 8-bit sRGB (range 0 to 255) to linear (range 0 to 1).
|
||
// The math to produce a table entry:
|
||
// tmp = oldvalue / 255.0;
|
||
// result = (tmp<=0.03928) ? tmp/12.92 : pow((tmp+0.055)/1.055,2.4);
|
||
static const float sRGB_to_linear_table[256] = {
|
||
0.000000, 0.000304, 0.000607, 0.000911, 0.001214, 0.001518, 0.001821,
|
||
0.002125, 0.002428, 0.002732, 0.003035, 0.003347, 0.003677, 0.004025,
|
||
0.004391, 0.004777, 0.005182, 0.005605, 0.006049, 0.006512, 0.006995,
|
||
0.007499, 0.008023, 0.008568, 0.009134, 0.009721, 0.010330, 0.010960,
|
||
0.011612, 0.012286, 0.012983, 0.013702, 0.014444, 0.015209, 0.015996,
|
||
0.016807, 0.017642, 0.018500, 0.019382, 0.020289, 0.021219, 0.022174,
|
||
0.023153, 0.024158, 0.025187, 0.026241, 0.027321, 0.028426, 0.029557,
|
||
0.030713, 0.031896, 0.033105, 0.034340, 0.035601, 0.036889, 0.038204,
|
||
0.039546, 0.040915, 0.042311, 0.043735, 0.045186, 0.046665, 0.048172,
|
||
0.049707, 0.051269, 0.052861, 0.054480, 0.056128, 0.057805, 0.059511,
|
||
0.061246, 0.063010, 0.064803, 0.066626, 0.068478, 0.070360, 0.072272,
|
||
0.074214, 0.076185, 0.078187, 0.080220, 0.082283, 0.084376, 0.086500,
|
||
0.088656, 0.090842, 0.093059, 0.095307, 0.097587, 0.099899, 0.102242,
|
||
0.104616, 0.107023, 0.109462, 0.111932, 0.114435, 0.116971, 0.119538,
|
||
0.122139, 0.124772, 0.127438, 0.130136, 0.132868, 0.135633, 0.138432,
|
||
0.141263, 0.144128, 0.147027, 0.149960, 0.152926, 0.155926, 0.158961,
|
||
0.162029, 0.165132, 0.168269, 0.171441, 0.174647, 0.177888, 0.181164,
|
||
0.184475, 0.187821, 0.191202, 0.194618, 0.198069, 0.201556, 0.205079,
|
||
0.208637, 0.212231, 0.215861, 0.219526, 0.223228, 0.226966, 0.230740,
|
||
0.234551, 0.238398, 0.242281, 0.246201, 0.250158, 0.254152, 0.258183,
|
||
0.262251, 0.266356, 0.270498, 0.274677, 0.278894, 0.283149, 0.287441,
|
||
0.291771, 0.296138, 0.300544, 0.304987, 0.309469, 0.313989, 0.318547,
|
||
0.323143, 0.327778, 0.332452, 0.337164, 0.341914, 0.346704, 0.351533,
|
||
0.356400, 0.361307, 0.366253, 0.371238, 0.376262, 0.381326, 0.386429,
|
||
0.391572, 0.396755, 0.401978, 0.407240, 0.412543, 0.417885, 0.423268,
|
||
0.428690, 0.434154, 0.439657, 0.445201, 0.450786, 0.456411, 0.462077,
|
||
0.467784, 0.473531, 0.479320, 0.485150, 0.491021, 0.496933, 0.502886,
|
||
0.508881, 0.514918, 0.520996, 0.527115, 0.533276, 0.539479, 0.545724,
|
||
0.552011, 0.558340, 0.564712, 0.571125, 0.577580, 0.584078, 0.590619,
|
||
0.597202, 0.603827, 0.610496, 0.617207, 0.623960, 0.630757, 0.637597,
|
||
0.644480, 0.651406, 0.658375, 0.665387, 0.672443, 0.679542, 0.686685,
|
||
0.693872, 0.701102, 0.708376, 0.715694, 0.723055, 0.730461, 0.737910,
|
||
0.745404, 0.752942, 0.760525, 0.768151, 0.775822, 0.783538, 0.791298,
|
||
0.799103, 0.806952, 0.814847, 0.822786, 0.830770, 0.838799, 0.846873,
|
||
0.854993, 0.863157, 0.871367, 0.879622, 0.887923, 0.896269, 0.904661,
|
||
0.913099, 0.921582, 0.930111, 0.938686, 0.947307, 0.955973, 0.964686,
|
||
0.973445, 0.982251, 0.991102, 1.000000
|
||
};
|
||
|
||
// this goes the other way; range checking will be required
|
||
static const unsigned char linear_to_sRGB_table[4096] =
|
||
"\x00\x01\x02\x03\x03\x04\x05\x06\x07\x08\x08\x09\x0a\x0b\x0b\x0c\x0d\x0d"
|
||
"\x0e\x0f\x10\x10\x11\x11\x12\x12\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17"
|
||
"\x18\x18\x18\x19\x19\x1a\x1a\x1a\x1b\x1b\x1c\x1c\x1c\x1d\x1d\x1d\x1e\x1e"
|
||
"\x1e\x1f\x1f\x1f\x20\x20\x20\x21\x21\x21\x22\x22\x22\x23\x23\x23\x23\x24"
|
||
"\x24\x24\x25\x25\x25\x25\x26\x26\x26\x26\x27\x27\x27\x28\x28\x28\x28\x29"
|
||
"\x29\x29\x29\x2a\x2a\x2a\x2a\x2b\x2b\x2b\x2b\x2c\x2c\x2c\x2c\x2c\x2d\x2d"
|
||
"\x2d\x2d\x2e\x2e\x2e\x2e\x2f\x2f\x2f\x2f\x2f\x30\x30\x30\x30\x30\x31\x31"
|
||
"\x31\x31\x31\x32\x32\x32\x32\x33\x33\x33\x33\x33\x34\x34\x34\x34\x34\x35"
|
||
"\x35\x35\x35\x35\x35\x36\x36\x36\x36\x36\x37\x37\x37\x37\x37\x38\x38\x38"
|
||
"\x38\x38\x38\x39\x39\x39\x39\x39\x39\x3a\x3a\x3a\x3a\x3a\x3a\x3b\x3b\x3b"
|
||
"\x3b\x3b\x3c\x3c\x3c\x3c\x3c\x3c\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3e\x3e\x3e"
|
||
"\x3e\x3e\x3e\x3f\x3f\x3f\x3f\x3f\x3f\x40\x40\x40\x40\x40\x40\x41\x41\x41"
|
||
"\x41\x41\x41\x41\x42\x42\x42\x42\x42\x42\x42\x43\x43\x43\x43\x43\x43\x44"
|
||
"\x44\x44\x44\x44\x44\x44\x45\x45\x45\x45\x45\x45\x45\x46\x46\x46\x46\x46"
|
||
"\x46\x46\x46\x47\x47\x47\x47\x47\x47\x47\x48\x48\x48\x48\x48\x48\x48\x48"
|
||
"\x49\x49\x49\x49\x49\x49\x49\x4a\x4a\x4a\x4a\x4a\x4a\x4a\x4a\x4b\x4b\x4b"
|
||
"\x4b\x4b\x4b\x4b\x4c\x4c\x4c\x4c\x4c\x4c\x4c\x4c\x4d\x4d\x4d\x4d\x4d\x4d"
|
||
"\x4d\x4d\x4e\x4e\x4e\x4e\x4e\x4e\x4e\x4e\x4f\x4f\x4f\x4f\x4f\x4f\x4f\x4f"
|
||
"\x50\x50\x50\x50\x50\x50\x50\x50\x50\x51\x51\x51\x51\x51\x51\x51\x51\x51"
|
||
"\x52\x52\x52\x52\x52\x52\x52\x52\x53\x53\x53\x53\x53\x53\x53\x53\x53\x54"
|
||
"\x54\x54\x54\x54\x54\x54\x54\x54\x55\x55\x55\x55\x55\x55\x55\x55\x55\x56"
|
||
"\x56\x56\x56\x56\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x58"
|
||
"\x58\x58\x58\x58\x58\x58\x58\x58\x58\x59\x59\x59\x59\x59\x59\x59\x59\x59"
|
||
"\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5b\x5b\x5b\x5b\x5b\x5b\x5b\x5b"
|
||
"\x5b\x5b\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5d\x5d\x5d\x5d\x5d\x5d"
|
||
"\x5d\x5d\x5d\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5e\x5f\x5f\x5f\x5f"
|
||
"\x5f\x5f\x5f\x5f\x5f\x5f\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x61"
|
||
"\x61\x61\x61\x61\x61\x61\x61\x61\x61\x62\x62\x62\x62\x62\x62\x62\x62\x62"
|
||
"\x62\x62\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x64\x64\x64\x64\x64"
|
||
"\x64\x64\x64\x64\x64\x64\x65\x65\x65\x65\x65\x65\x65\x65\x65\x65\x65\x66"
|
||
"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x67\x67\x67\x67\x67\x67\x67\x67"
|
||
"\x67\x67\x67\x67\x68\x68\x68\x68\x68\x68\x68\x68\x68\x68\x68\x69\x69\x69"
|
||
"\x69\x69\x69\x69\x69\x69\x69\x69\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a\x6a"
|
||
"\x6a\x6a\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6b\x6c\x6c\x6c\x6c"
|
||
"\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6c\x6d\x6d\x6d\x6d\x6d\x6d\x6d\x6d\x6d"
|
||
"\x6d\x6d\x6d\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6e\x6f\x6f\x6f"
|
||
"\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x70\x70\x70\x70\x70\x70\x70\x70\x70"
|
||
"\x70\x70\x70\x70\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x72"
|
||
"\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x72\x73\x73\x73\x73\x73\x73"
|
||
"\x73\x73\x73\x73\x73\x73\x73\x74\x74\x74\x74\x74\x74\x74\x74\x74\x74\x74"
|
||
"\x74\x74\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x75\x76\x76"
|
||
"\x76\x76\x76\x76\x76\x76\x76\x76\x76\x76\x76\x77\x77\x77\x77\x77\x77\x77"
|
||
"\x77\x77\x77\x77\x77\x77\x77\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78\x78"
|
||
"\x78\x78\x78\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x79\x7a"
|
||
"\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7a\x7b\x7b\x7b\x7b\x7b\x7b"
|
||
"\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7b\x7c\x7c\x7c\x7c\x7c\x7c\x7c\x7c\x7c"
|
||
"\x7c\x7c\x7c\x7c\x7c\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d\x7d"
|
||
"\x7d\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x7f\x7f"
|
||
"\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x80\x80\x80\x80\x80"
|
||
"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x81\x81\x81\x81\x81\x81\x81\x81"
|
||
"\x81\x81\x81\x81\x81\x81\x81\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82"
|
||
"\x82\x82\x82\x82\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83\x83"
|
||
"\x83\x83\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x85"
|
||
"\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x85\x86\x86\x86"
|
||
"\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x86\x87\x87\x87\x87\x87"
|
||
"\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x87\x88\x88\x88\x88\x88\x88\x88"
|
||
"\x88\x88\x88\x88\x88\x88\x88\x88\x88\x89\x89\x89\x89\x89\x89\x89\x89\x89"
|
||
"\x89\x89\x89\x89\x89\x89\x89\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a\x8a"
|
||
"\x8a\x8a\x8a\x8a\x8a\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b\x8b"
|
||
"\x8b\x8b\x8b\x8b\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c\x8c"
|
||
"\x8c\x8c\x8c\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d\x8d"
|
||
"\x8d\x8d\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e\x8e"
|
||
"\x8e\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f\x8f"
|
||
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x91"
|
||
"\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x91\x92"
|
||
"\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x92\x93"
|
||
"\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x93\x94\x94"
|
||
"\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x94\x95\x95"
|
||
"\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x95\x96\x96"
|
||
"\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x96\x97"
|
||
"\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x98"
|
||
"\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98"
|
||
"\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99"
|
||
"\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a"
|
||
"\x9a\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b\x9b"
|
||
"\x9b\x9b\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c\x9c"
|
||
"\x9c\x9c\x9c\x9c\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d\x9d"
|
||
"\x9d\x9d\x9d\x9d\x9d\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e"
|
||
"\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f"
|
||
"\x9f\x9f\x9f\x9f\x9f\x9f\x9f\x9f\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0"
|
||
"\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1"
|
||
"\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa1\xa2\xa2\xa2\xa2\xa2\xa2"
|
||
"\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa2\xa3\xa3\xa3\xa3"
|
||
"\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa3\xa4"
|
||
"\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4\xa4"
|
||
"\xa4\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5"
|
||
"\xa5\xa5\xa5\xa5\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
|
||
"\xa6\xa6\xa6\xa6\xa6\xa6\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7"
|
||
"\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa7\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8"
|
||
"\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa8\xa9\xa9\xa9\xa9\xa9"
|
||
"\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xa9\xaa\xaa"
|
||
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
|
||
"\xaa\xaa\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab"
|
||
"\xab\xab\xab\xab\xab\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac"
|
||
"\xac\xac\xac\xac\xac\xac\xac\xac\xac\xad\xad\xad\xad\xad\xad\xad\xad\xad"
|
||
"\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xad\xae\xae\xae\xae\xae"
|
||
"\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xae\xaf"
|
||
"\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf\xaf"
|
||
"\xaf\xaf\xaf\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0"
|
||
"\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
|
||
"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xb2\xb2"
|
||
"\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb2\xb3"
|
||
"\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3\xb3"
|
||
"\xb3\xb3\xb3\xb3\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4"
|
||
"\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb4\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5"
|
||
"\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb5\xb6\xb6\xb6"
|
||
"\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6\xb6"
|
||
"\xb6\xb6\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7"
|
||
"\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8"
|
||
"\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb8\xb9\xb9\xb9\xb9"
|
||
"\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9\xb9"
|
||
"\xb9\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba\xba"
|
||
"\xba\xba\xba\xba\xba\xba\xba\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb"
|
||
"\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbc\xbc\xbc\xbc"
|
||
"\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc\xbc"
|
||
"\xbc\xbc\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd"
|
||
"\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe"
|
||
"\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbf\xbf"
|
||
"\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf\xbf"
|
||
"\xbf\xbf\xbf\xbf\xbf\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0"
|
||
"\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc1\xc1\xc1\xc1\xc1\xc1"
|
||
"\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
|
||
"\xc1\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2"
|
||
"\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc2\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3"
|
||
"\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc3\xc4\xc4"
|
||
"\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4"
|
||
"\xc4\xc4\xc4\xc4\xc4\xc4\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5"
|
||
"\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc5\xc6\xc6\xc6\xc6"
|
||
"\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6"
|
||
"\xc6\xc6\xc6\xc6\xc6\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7"
|
||
"\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc7\xc8\xc8\xc8\xc8\xc8"
|
||
"\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8\xc8"
|
||
"\xc8\xc8\xc8\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9"
|
||
"\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xc9\xca\xca\xca\xca\xca\xca"
|
||
"\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca\xca"
|
||
"\xca\xca\xca\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb"
|
||
"\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcb\xcc\xcc\xcc\xcc\xcc\xcc"
|
||
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
|
||
"\xcc\xcc\xcc\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
|
||
"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xce\xce\xce\xce\xce"
|
||
"\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce\xce"
|
||
"\xce\xce\xce\xce\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf"
|
||
"\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xcf\xd0\xd0\xd0\xd0"
|
||
"\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0\xd0"
|
||
"\xd0\xd0\xd0\xd0\xd0\xd0\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1"
|
||
"\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd1\xd2\xd2"
|
||
"\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2"
|
||
"\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd2\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3"
|
||
"\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3\xd3"
|
||
"\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4"
|
||
"\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd4\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
|
||
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
|
||
"\xd5\xd5\xd5\xd5\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6"
|
||
"\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd6\xd7\xd7\xd7"
|
||
"\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7"
|
||
"\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd7\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8"
|
||
"\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8"
|
||
"\xd8\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9"
|
||
"\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xda\xda\xda\xda\xda"
|
||
"\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda\xda"
|
||
"\xda\xda\xda\xda\xda\xda\xda\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb"
|
||
"\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb"
|
||
"\xdb\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc"
|
||
"\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdc\xdd\xdd\xdd\xdd\xdd"
|
||
"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
|
||
"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde"
|
||
"\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde\xde"
|
||
"\xde\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf"
|
||
"\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xdf\xe0\xe0\xe0\xe0"
|
||
"\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0"
|
||
"\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1"
|
||
"\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1\xe1"
|
||
"\xe1\xe1\xe1\xe1\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2"
|
||
"\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe3"
|
||
"\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3"
|
||
"\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe4\xe4\xe4\xe4\xe4\xe4"
|
||
"\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4"
|
||
"\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe4\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5"
|
||
"\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5"
|
||
"\xe5\xe5\xe5\xe5\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6"
|
||
"\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe6\xe7"
|
||
"\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7"
|
||
"\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe8\xe8\xe8\xe8\xe8"
|
||
"\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8"
|
||
"\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe8\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9"
|
||
"\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9"
|
||
"\xe9\xe9\xe9\xe9\xe9\xe9\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea"
|
||
"\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea\xea"
|
||
"\xea\xea\xea\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb"
|
||
"\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xeb\xec"
|
||
"\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec"
|
||
"\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xec\xed\xed\xed"
|
||
"\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed"
|
||
"\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xed\xee\xee\xee\xee\xee\xee"
|
||
"\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee"
|
||
"\xee\xee\xee\xee\xee\xee\xee\xee\xee\xef\xef\xef\xef\xef\xef\xef\xef\xef"
|
||
"\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef\xef"
|
||
"\xef\xef\xef\xef\xef\xef\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0"
|
||
"\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0"
|
||
"\xf0\xf0\xf0\xf0\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1"
|
||
"\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1\xf1"
|
||
"\xf1\xf1\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2"
|
||
"\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2"
|
||
"\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3"
|
||
"\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf4\xf4"
|
||
"\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4"
|
||
"\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf5\xf5\xf5"
|
||
"\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5"
|
||
"\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf6\xf6\xf6\xf6"
|
||
"\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6"
|
||
"\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf6\xf7\xf7\xf7\xf7\xf7\xf7"
|
||
"\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7"
|
||
"\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf7\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
||
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
||
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf9\xf9\xf9\xf9\xf9\xf9\xf9"
|
||
"\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9"
|
||
"\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xf9\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa"
|
||
"\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa"
|
||
"\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb"
|
||
"\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb"
|
||
"\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfb\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc"
|
||
"\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc"
|
||
"\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd"
|
||
"\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd"
|
||
"\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe"
|
||
"\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe"
|
||
"\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff"
|
||
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
||
;
|
||
|
||
static unsigned char linear_to_sRGB (float linear) FUNCTION;
|
||
static unsigned char linear_to_sRGB (float linear)
|
||
{
|
||
unsigned slot;
|
||
slot = linear*4096.0 + 0.5;
|
||
if(slot>4095)
|
||
{
|
||
if(linear>0.5)
|
||
slot = 4095;
|
||
else
|
||
slot = 0;
|
||
}
|
||
return linear_to_sRGB_table[slot];
|
||
}
|
||
|
||
typedef struct multichan {
|
||
double L,hue,sat; // L,a,b would be better -- 2-way formula unknown
|
||
unsigned char or,og,ob,alpha; // old 8-bit values
|
||
} multichan;
|
||
|
||
#define X0 ((double)0.9505)
|
||
#define Y0 ((double)1.0000)
|
||
#define Z0 ((double)1.0890)
|
||
#define u0_prime ( (4.0 * X0) / (X0 + 15.0*Y0 + 3.0*Z0) )
|
||
#define v0_prime ( (9.0 * Y0) / (X0 + 15.0*Y0 + 3.0*Z0) )
|
||
|
||
|
||
static void fill_multichan(multichan *mc, double *up, double *vp)
|
||
{
|
||
double X,Y,Z,u,v;
|
||
double u_prime, v_prime; /* temp, part of official formula */
|
||
double Y_norm, fract; /* severely temp */
|
||
|
||
double r = sRGB_to_linear_table[mc->or];
|
||
double g = sRGB_to_linear_table[mc->og];
|
||
double b = sRGB_to_linear_table[mc->ob];
|
||
|
||
// coordinate change, RGB --> XYZ
|
||
X = 0.4124*r + 0.3576*g + 0.1805*b;
|
||
Y = 0.2126*r + 0.7152*g + 0.0722*b;
|
||
Z = 0.0193*r + 0.1192*g + 0.9505*b;
|
||
|
||
// XYZ --> Luv
|
||
Y_norm = Y/Y0;
|
||
fract = 1.0 / (X + 15.0*Y + 3.0*Z);
|
||
u_prime = 4.0*X*fract;
|
||
v_prime = 9.0*Y*fract;
|
||
mc->L = (Y_norm>0.008856) ? 116.0*pow(Y_norm,1.0/3.0)-16.0 : 903.3*Y_norm;
|
||
u = 13.0*mc->L*(u_prime - u0_prime);
|
||
v = 13.0*mc->L*(v_prime - v0_prime);
|
||
|
||
mc->sat = sqrt(u*u + v*v);
|
||
mc->hue = atan2(u,v);
|
||
if(up) *up = u;
|
||
if(vp) *vp = v;
|
||
}
|
||
|
||
|
||
static double tint_part_1(multichan *work, SDL_Surface *in)
|
||
{
|
||
int xx,yy;
|
||
double u_total = 0;
|
||
double v_total = 0;
|
||
double u,v;
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int) = getpixels[in->format->BytesPerPixel];
|
||
|
||
|
||
SDL_LockSurface(in);
|
||
for (yy = 0; yy < in->h; yy++)
|
||
{
|
||
for (xx = 0; xx < in->w; xx++)
|
||
{
|
||
multichan *mc = work+yy*in->w+xx;
|
||
// put pixels into a more tolerable form
|
||
SDL_GetRGBA(getpixel(in, xx, yy),
|
||
in->format,
|
||
&mc->or, &mc->og, &mc->ob, &mc->alpha);
|
||
|
||
fill_multichan(mc,&u,&v);
|
||
// average out u and v, giving more weight to opaque high-saturation pixels
|
||
// (this is to take an initial guess at the primary hue)
|
||
u_total += mc->alpha * u * mc->sat;
|
||
v_total += mc->alpha * v * mc->sat;
|
||
}
|
||
}
|
||
SDL_UnlockSurface(in);
|
||
|
||
return atan2(u_total,v_total);
|
||
}
|
||
|
||
|
||
static void change_colors(SDL_Surface *out, multichan *work, double hue_range, multichan *key_color_ptr)
|
||
{
|
||
double lower_hue_1,upper_hue_1,lower_hue_2,upper_hue_2;
|
||
int xx,yy;
|
||
multichan dst;
|
||
double satratio;
|
||
double slope;
|
||
void (*putpixel)(SDL_Surface *, int, int, Uint32);
|
||
double old_sat;
|
||
double newsat;
|
||
double L;
|
||
double X,Y,Z;
|
||
double u_prime, v_prime; /* temp, part of official formula */
|
||
unsigned tries;
|
||
double u;
|
||
double v;
|
||
double r, g, b;
|
||
|
||
|
||
// prepare source and destination color info
|
||
// should reset hue_range or not? won't bother for now
|
||
multichan key_color = *key_color_ptr; // want to work from a copy, for safety
|
||
lower_hue_1 = key_color.hue - hue_range;
|
||
upper_hue_1 = key_color.hue + hue_range;
|
||
if (lower_hue_1 < -M_PI)
|
||
{
|
||
lower_hue_2 = lower_hue_1 + 2 * M_PI;
|
||
upper_hue_2 = upper_hue_1 + 2 * M_PI;
|
||
}
|
||
else
|
||
{
|
||
lower_hue_2 = lower_hue_1 - 2 * M_PI;
|
||
upper_hue_2 = upper_hue_1 - 2 * M_PI;
|
||
}
|
||
|
||
// get the destination color set up
|
||
dst.or = color_hexes[cur_color][0];
|
||
dst.og = color_hexes[cur_color][1];
|
||
dst.ob = color_hexes[cur_color][2];
|
||
fill_multichan(&dst,NULL,NULL);
|
||
|
||
satratio = dst.sat / key_color.sat;
|
||
slope = (dst.L-key_color.L)/dst.sat;
|
||
putpixel = putpixels[out->format->BytesPerPixel];
|
||
|
||
SDL_LockSurface(out);
|
||
for (yy = 0; yy < out->h; yy++)
|
||
{
|
||
for (xx = 0; xx < out->w; xx++)
|
||
{
|
||
multichan *mc = work+yy*out->w+xx;
|
||
double oldhue = mc->hue;
|
||
|
||
// if not in the first range, and not in the second range, skip this one
|
||
// (really should alpha-blend as a function of hue angle difference)
|
||
if( (oldhue<lower_hue_1 || oldhue>upper_hue_1) && (oldhue<lower_hue_2 || oldhue>upper_hue_2) )
|
||
{
|
||
putpixel(out, xx, yy, SDL_MapRGBA(out->format, mc->or, mc->og, mc->ob, mc->alpha));
|
||
continue;
|
||
}
|
||
|
||
// Modify the pixel
|
||
old_sat = mc->sat;
|
||
newsat = old_sat * satratio;
|
||
L = mc->L;
|
||
if(dst.sat>0)
|
||
L += newsat * slope; // not greyscale destination
|
||
else
|
||
L += old_sat*(dst.L-key_color.L)/key_color.sat;
|
||
|
||
// convert from L,u,v all the way back to sRGB with 8-bit channels
|
||
tries = 3;
|
||
trysat:;
|
||
u = newsat * sin(dst.hue);
|
||
v = newsat * cos(dst.hue);
|
||
|
||
// Luv to XYZ
|
||
u_prime = u/(13.0*L)+u0_prime;
|
||
v_prime = v/(13.0*L)+v0_prime;
|
||
Y = (L>7.99959199307) ? Y0*pow((L+16.0)/116.0,3.0) : Y0*L/903.3;
|
||
X = 2.25*Y*u_prime/v_prime;
|
||
Z = (3.0*Y - 0.75*Y*u_prime)/v_prime - 5.0*Y;
|
||
|
||
// coordinate change: XYZ to RGB
|
||
r = 3.2410*X + -1.5374*Y + -0.4986*Z;
|
||
g = -0.9692*X + 1.8760*Y + 0.0416*Z;
|
||
b = 0.0556*X + -0.2040*Y + 1.0570*Z;
|
||
|
||
// If it is out of gamut, try to de-saturate it a few times before truncating.
|
||
// (the linear_to_sRGB function will truncate)
|
||
if((r<=-0.5 || g<=-0.5 || b<=-0.5 || r>=255.5 || g>=255.5 || b>=255.5) && tries--)
|
||
{
|
||
newsat *= 0.8;
|
||
goto trysat;
|
||
}
|
||
|
||
putpixel(out, xx, yy,
|
||
SDL_MapRGBA(out->format, linear_to_sRGB(r), linear_to_sRGB(g), linear_to_sRGB(b), mc->alpha));
|
||
}
|
||
}
|
||
SDL_UnlockSurface(out);
|
||
}
|
||
|
||
|
||
static multichan *find_most_saturated(double initial_hue, multichan * work,
|
||
unsigned num, double * hue_range_ptr)
|
||
{
|
||
// find the most saturated pixel near the initial hue guess
|
||
multichan *key_color_ptr = NULL;
|
||
double hue_range;
|
||
unsigned i;
|
||
double max_sat;
|
||
double lower_hue_1;
|
||
double upper_hue_1;
|
||
double lower_hue_2;
|
||
double upper_hue_2;
|
||
multichan * mc;
|
||
|
||
switch (stamp_data[cur_stamp]->tinter)
|
||
{
|
||
default:
|
||
case TINTER_NORMAL:
|
||
hue_range = 18*M_PI/180.0; // plus or minus 18 degrees search, 27 replace
|
||
break;
|
||
case TINTER_NARROW:
|
||
hue_range = 6*M_PI/180.0; // plus or minus 6 degrees search, 9 replace
|
||
break;
|
||
case TINTER_ANYHUE:
|
||
hue_range = M_PI; // plus or minus 180 degrees
|
||
break;
|
||
}
|
||
|
||
hue_range_retry:;
|
||
|
||
max_sat = 0;
|
||
lower_hue_1 = initial_hue - hue_range;
|
||
upper_hue_1 = initial_hue + hue_range;
|
||
|
||
if (lower_hue_1 < -M_PI)
|
||
{
|
||
lower_hue_2 = lower_hue_1 + 2 * M_PI;
|
||
upper_hue_2 = upper_hue_1 + 2 * M_PI;
|
||
}
|
||
else
|
||
{
|
||
lower_hue_2 = lower_hue_1 - 2 * M_PI;
|
||
upper_hue_2 = upper_hue_1 - 2 * M_PI;
|
||
}
|
||
|
||
i = num;
|
||
while (i--)
|
||
{
|
||
mc = work + i;
|
||
|
||
// if not in the first range, and not in the second range, skip this one
|
||
//
|
||
if ((mc->hue<lower_hue_1 || mc->hue>upper_hue_1) &&
|
||
(mc->hue<lower_hue_2 || mc->hue>upper_hue_2))
|
||
continue;
|
||
|
||
if(mc->sat > max_sat)
|
||
{
|
||
max_sat = mc->sat;
|
||
key_color_ptr = mc;
|
||
}
|
||
}
|
||
|
||
if (!key_color_ptr)
|
||
{
|
||
hue_range *= 1.5;
|
||
|
||
if (hue_range < M_PI)
|
||
goto hue_range_retry;
|
||
}
|
||
|
||
*hue_range_ptr = hue_range;
|
||
|
||
return key_color_ptr;
|
||
}
|
||
|
||
|
||
static void vector_tint_surface(SDL_Surface * out, SDL_Surface * in)
|
||
{
|
||
int xx,yy;
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int) = getpixels[in->format->BytesPerPixel];
|
||
void (*putpixel)(SDL_Surface *, int, int, Uint32) = putpixels[out->format->BytesPerPixel];
|
||
|
||
double r = sRGB_to_linear_table[color_hexes[cur_color][0]];
|
||
double g = sRGB_to_linear_table[color_hexes[cur_color][1]];
|
||
double b = sRGB_to_linear_table[color_hexes[cur_color][2]];
|
||
|
||
SDL_LockSurface(in);
|
||
for (yy = 0; yy < in->h; yy++)
|
||
{
|
||
for (xx = 0; xx < in->w; xx++)
|
||
{
|
||
unsigned char r8, g8, b8, a8;
|
||
double old;
|
||
|
||
SDL_GetRGBA(getpixel(in, xx, yy),
|
||
in->format,
|
||
&r8, &g8, &b8, &a8);
|
||
// get the linear greyscale value
|
||
old = sRGB_to_linear_table[r8]*0.2126 + sRGB_to_linear_table[g8]*0.7152 + sRGB_to_linear_table[b8]*0.0722;
|
||
|
||
putpixel(out, xx, yy,
|
||
SDL_MapRGBA(out->format, linear_to_sRGB(r*old), linear_to_sRGB(g*old), linear_to_sRGB(b*old), a8));
|
||
}
|
||
}
|
||
SDL_UnlockSurface(in);
|
||
}
|
||
|
||
|
||
static void tint_surface(SDL_Surface * tmp_surf, SDL_Surface * surf_ptr)
|
||
{
|
||
unsigned width = surf_ptr->w;
|
||
unsigned height = surf_ptr->h;
|
||
multichan * work;
|
||
multichan * key_color_ptr;
|
||
double initial_hue;
|
||
double hue_range;
|
||
|
||
|
||
work = malloc(sizeof(multichan) * width * height);
|
||
|
||
if (work)
|
||
{
|
||
initial_hue = tint_part_1(work, surf_ptr);
|
||
|
||
key_color_ptr = find_most_saturated(initial_hue, work,
|
||
width * height, &hue_range);
|
||
|
||
if (key_color_ptr)
|
||
{
|
||
// wider for processing than for searching
|
||
hue_range *= 1.5;
|
||
|
||
change_colors(tmp_surf, work, hue_range, key_color_ptr);
|
||
|
||
free(work);
|
||
return;
|
||
}
|
||
|
||
free(work);
|
||
}
|
||
|
||
/* Failed! Fall back: */
|
||
|
||
//fprintf(stderr, "Falling back to tinter=vector, "
|
||
// "this should be in the *.dat file\n");
|
||
|
||
vector_tint_surface(tmp_surf, surf_ptr);
|
||
}
|
||
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
/* Draw using the current stamp: */
|
||
|
||
static void stamp_draw(int x, int y)
|
||
{
|
||
SDL_Rect dest;
|
||
SDL_Surface * tmp_surf, * surf_ptr, * final_surf;
|
||
Uint32 amask;
|
||
Uint8 r, g, b, a;
|
||
int xx, yy, dont_free_tmp_surf, base_x, base_y;
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int);
|
||
void (*putpixel)(SDL_Surface *, int, int, Uint32);
|
||
|
||
surf_ptr = active_stamp;
|
||
|
||
getpixel = getpixels[surf_ptr->format->BytesPerPixel];
|
||
|
||
|
||
/* Create a temp surface to play with: */
|
||
|
||
if (stamp_colorable(cur_stamp) || stamp_tintable(cur_stamp))
|
||
{
|
||
amask = ~(surf_ptr->format->Rmask |
|
||
surf_ptr->format->Gmask |
|
||
surf_ptr->format->Bmask);
|
||
|
||
tmp_surf =
|
||
SDL_CreateRGBSurface(SDL_SWSURFACE,
|
||
surf_ptr->w,
|
||
surf_ptr->h,
|
||
surf_ptr->format->BitsPerPixel,
|
||
surf_ptr->format->Rmask,
|
||
surf_ptr->format->Gmask,
|
||
surf_ptr->format->Bmask,
|
||
amask);
|
||
|
||
if (tmp_surf == NULL)
|
||
{
|
||
fprintf(stderr, "\nError: Can't render the colored stamp!\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
dont_free_tmp_surf = 0;
|
||
}
|
||
else
|
||
{
|
||
/* Not altering color; no need to create temp surf if we don't use it! */
|
||
|
||
tmp_surf = NULL;
|
||
dont_free_tmp_surf = 1;
|
||
}
|
||
|
||
if (tmp_surf != NULL)
|
||
putpixel = putpixels[tmp_surf->format->BytesPerPixel];
|
||
else
|
||
putpixel = NULL;
|
||
|
||
|
||
/* Alter the stamp's color, if needed: */
|
||
|
||
if (stamp_colorable(cur_stamp) && tmp_surf != NULL)
|
||
{
|
||
/* Render the stamp in the chosen color: */
|
||
|
||
/* FIXME: It sucks to render this EVERY TIME. Why not just when
|
||
they pick the color, or pick the stamp, like with brushes? */
|
||
|
||
/* Render the stamp: */
|
||
|
||
SDL_LockSurface(surf_ptr);
|
||
SDL_LockSurface(tmp_surf);
|
||
|
||
for (yy = 0; yy < surf_ptr->h; yy++)
|
||
{
|
||
for (xx = 0; xx < surf_ptr->w; xx++)
|
||
{
|
||
SDL_GetRGBA(getpixel(surf_ptr, xx, yy),
|
||
surf_ptr->format,
|
||
&r, &g, &b, &a);
|
||
|
||
putpixel(tmp_surf, xx, yy,
|
||
SDL_MapRGBA(tmp_surf->format,
|
||
color_hexes[cur_color][0],
|
||
color_hexes[cur_color][1],
|
||
color_hexes[cur_color][2],
|
||
a));
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(tmp_surf);
|
||
SDL_UnlockSurface(surf_ptr);
|
||
}
|
||
else if (stamp_tintable(cur_stamp))
|
||
{
|
||
if (stamp_data[cur_stamp]->tinter == TINTER_VECTOR)
|
||
vector_tint_surface(tmp_surf, surf_ptr);
|
||
else
|
||
tint_surface(tmp_surf, surf_ptr);
|
||
}
|
||
else
|
||
{
|
||
/* No color change, just use it! */
|
||
tmp_surf = surf_ptr;
|
||
}
|
||
|
||
/* Shrink or grow it! */
|
||
final_surf = thumbnail(tmp_surf, CUR_STAMP_W, CUR_STAMP_H, 0);
|
||
|
||
/* Where it will go? */
|
||
base_x = x - (CUR_STAMP_W+1)/2;
|
||
base_y = y - (CUR_STAMP_H+1)/2;
|
||
|
||
/* And blit it! */
|
||
dest.x = base_x;
|
||
dest.y = base_y;
|
||
SDL_BlitSurface(final_surf, NULL, canvas, &dest);
|
||
|
||
update_canvas(x - (CUR_STAMP_W+1)/2,
|
||
y - (CUR_STAMP_H+1)/2,
|
||
x + (CUR_STAMP_W+1)/2,
|
||
y + (CUR_STAMP_H+1)/2);
|
||
|
||
/* Free the temporary surfaces */
|
||
|
||
if (!dont_free_tmp_surf)
|
||
SDL_FreeSurface(tmp_surf);
|
||
|
||
SDL_FreeSurface(final_surf);
|
||
}
|
||
|
||
|
||
/* Draw using the current brush: */
|
||
|
||
static void magic_draw(int x1, int y1, int x2, int y2, int button_down)
|
||
{
|
||
int dx, dy, y;
|
||
int orig_x1, orig_y1, orig_x2, orig_y2, tmp;
|
||
float m, b;
|
||
|
||
if (cur_magic == MAGIC_RAINBOW)
|
||
rainbow_color = (rainbow_color + 1) % NUM_RAINBOW_COLORS;
|
||
|
||
orig_x1 = x1;
|
||
orig_y1 = y1;
|
||
|
||
orig_x2 = x2;
|
||
orig_y2 = y2;
|
||
|
||
|
||
dx = x2 - x1;
|
||
dy = y2 - y1;
|
||
|
||
if (dx != 0)
|
||
{
|
||
m = ((float) dy) / ((float) dx);
|
||
b = y1 - m * x1;
|
||
|
||
if (x2 >= x1)
|
||
dx = 1;
|
||
else
|
||
dx = -1;
|
||
|
||
|
||
while (x1 != x2)
|
||
{
|
||
y1 = m * x1 + b;
|
||
y2 = m * (x1 + dx) + b;
|
||
|
||
if (y1 > y2)
|
||
{
|
||
for (y = y1; y >= y2; y--)
|
||
blit_magic(x1, y, button_down);
|
||
}
|
||
else
|
||
{
|
||
for (y = y1; y <= y2; y++)
|
||
blit_magic(x1, y, button_down);
|
||
}
|
||
|
||
x1 = x1 + dx;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (y1 > y2)
|
||
{
|
||
for (y = y1; y >= y2; y--)
|
||
blit_magic(x1, y, button_down);
|
||
}
|
||
else
|
||
{
|
||
for (y = y1; y <= y2; y++)
|
||
blit_magic(x1, y, button_down);
|
||
}
|
||
}
|
||
|
||
if (orig_x1 > orig_x2)
|
||
{
|
||
tmp = orig_x1;
|
||
orig_x1 = orig_x2;
|
||
orig_x2 = tmp;
|
||
}
|
||
|
||
if (orig_y1 > orig_y2)
|
||
{
|
||
tmp = orig_y1;
|
||
orig_y1 = orig_y2;
|
||
orig_y2 = tmp;
|
||
}
|
||
|
||
|
||
/* Play sound: */
|
||
|
||
if (cur_magic == MAGIC_DRIP)
|
||
playsound(0, SND_DRIP, 0);
|
||
else if (cur_magic == MAGIC_CHALK)
|
||
playsound(0, SND_CHALK, 0);
|
||
else if (cur_magic == MAGIC_SPARKLES)
|
||
playsound(0, SND_SPARKLES1 + (rand() % 2), 0);
|
||
else if (cur_magic == MAGIC_FLIP)
|
||
playsound(0, SND_FLIP, 0);
|
||
else if (cur_magic == MAGIC_MIRROR)
|
||
playsound(0, SND_MIRROR, 0);
|
||
else if (cur_magic == MAGIC_NEGATIVE)
|
||
playsound(0, SND_NEGATIVE, 0);
|
||
else if (cur_magic == MAGIC_BLUR)
|
||
playsound(0, SND_BLUR, 0);
|
||
else if (cur_magic == MAGIC_BLOCKS && ((rand() % 10) < 5))
|
||
playsound(0, SND_BLOCKS, 0);
|
||
else if (cur_magic == MAGIC_FADE)
|
||
playsound(0, SND_FADE, 0);
|
||
else if (cur_magic == MAGIC_DARKEN)
|
||
playsound(0, SND_DARKEN, 0);
|
||
else if (cur_magic == MAGIC_RAINBOW)
|
||
playsound(0, SND_RAINBOW, 0);
|
||
else if (cur_magic == MAGIC_SMUDGE)
|
||
playsound(0, SND_SMUDGE, 0);
|
||
else if (cur_magic == MAGIC_CARTOON)
|
||
playsound(0, SND_CARTOON, 0);
|
||
else if (cur_magic == MAGIC_TINT)
|
||
playsound(0, SND_TINT, 0);
|
||
|
||
/* FIXME: Need sounds for:
|
||
Grass (mower?) */
|
||
|
||
|
||
/* FIXME: Arbitrary? */
|
||
|
||
update_canvas(orig_x1 - 32, orig_y1 - 32,
|
||
orig_x2 + 32, orig_y2 + 32);
|
||
}
|
||
|
||
|
||
// this one rounds down
|
||
static int log2int(int x)
|
||
{
|
||
int y = 0;
|
||
if(x<=1)
|
||
return 0;
|
||
x >>= 1;
|
||
while(x)
|
||
{
|
||
x >>= 1;
|
||
y++;
|
||
}
|
||
return y;
|
||
}
|
||
|
||
static void do_brick(int x, int y, int w, int h)
|
||
{
|
||
SDL_Rect dest;
|
||
|
||
// brick color: 127,76,73
|
||
double ran_r = rand()/(double)RAND_MAX;
|
||
double ran_g = rand()/(double)RAND_MAX;
|
||
double base_r = sRGB_to_linear_table[color_hexes[cur_color][0]]*1.5 + sRGB_to_linear_table[127]*5.0 + ran_r;
|
||
double base_g = sRGB_to_linear_table[color_hexes[cur_color][1]]*1.5 + sRGB_to_linear_table[76] *5.0 + ran_g;
|
||
double base_b = sRGB_to_linear_table[color_hexes[cur_color][2]]*1.5 + sRGB_to_linear_table[73] *5.0 + (ran_r+ran_g*2.0)/3.0;
|
||
|
||
Uint8 r = linear_to_sRGB(base_r/7.5);
|
||
Uint8 g = linear_to_sRGB(base_g/7.5);
|
||
Uint8 b = linear_to_sRGB(base_b/7.5);
|
||
|
||
dest.x = x;
|
||
dest.y = y;
|
||
dest.w = w;
|
||
dest.h = h;
|
||
|
||
|
||
SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format, r, g, b));
|
||
|
||
// This is better for debugging brick layout:
|
||
// SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format, rand()&255, rand()&255, rand()&255));
|
||
|
||
|
||
/* Note: We only play the brick sound when we actually DRAW a brick: */
|
||
|
||
playsound(0, SND_BRICK, 1);
|
||
}
|
||
|
||
/* Draw the current brush in the current color: */
|
||
|
||
static void blit_magic(int x, int y, int button_down)
|
||
{
|
||
int xx, yy, w, h;
|
||
Uint32 colr;
|
||
Uint8 r, g, b, a;
|
||
SDL_Surface * last;
|
||
SDL_Rect src, dest;
|
||
int undo_ctr;
|
||
Uint32 (*getpixel_canvas)(SDL_Surface *, int, int) = getpixels[canvas->format->BytesPerPixel];
|
||
void (*putpixel)(SDL_Surface *, int, int, Uint32) = putpixels[canvas->format->BytesPerPixel];
|
||
Uint32 (*getpixel_last)(SDL_Surface *, int, int);
|
||
|
||
|
||
/* In case we need to use the current canvas (just saved to undo buf)... */
|
||
|
||
if (cur_undo > 0)
|
||
undo_ctr = cur_undo - 1;
|
||
else
|
||
undo_ctr = NUM_UNDO_BUFS - 1;
|
||
|
||
last = undo_bufs[undo_ctr];
|
||
getpixel_last = getpixels[last->format->BytesPerPixel];
|
||
|
||
|
||
brush_counter++;
|
||
|
||
if (brush_counter >= 4) /* FIXME: Arbitrary? */
|
||
{
|
||
brush_counter = 0;
|
||
|
||
|
||
if (cur_magic == MAGIC_BLUR)
|
||
{
|
||
double state[32][32][3];
|
||
unsigned i = 32*32;
|
||
|
||
SDL_LockSurface(canvas);
|
||
|
||
while (i--)
|
||
{
|
||
int iy = i>>5;
|
||
int ix = i&0x1f;
|
||
// is it not on the circle of radius sqrt(220) at location 16,16?
|
||
if ( (ix-16)*(ix-16) + (iy-16)*(iy-16) > 220)
|
||
continue;
|
||
// it is on the circle, so grab it
|
||
|
||
SDL_GetRGB(getpixel_canvas(canvas, x+ix-16, y+iy-16), last->format, &r, &g, &b);
|
||
state[ix][iy][0] = sRGB_to_linear_table[r];
|
||
state[ix][iy][1] = sRGB_to_linear_table[g];
|
||
state[ix][iy][2] = sRGB_to_linear_table[b];
|
||
}
|
||
i = 32*32;
|
||
while (i--)
|
||
{
|
||
double lr, lg, lb; // linear red,green,blue
|
||
double weight;
|
||
int iy = i>>5;
|
||
int ix = i&0x1f;
|
||
int r2 = (ix-16)*(ix-16) + (iy-16)*(iy-16); // radius squared
|
||
|
||
// is it not on the circle of radius sqrt(140) at location 16,16?
|
||
if ( r2 > 140)
|
||
continue;
|
||
|
||
// It is on the circle, but how strongly will it be affected?
|
||
// This is lame; we should use something like a gaussian or cosine
|
||
// via a lookup table. (inverted, because this is the center weight)
|
||
weight = r2/16.0+3.0;
|
||
|
||
// Sampling more points would be good too, though it'd be slower.
|
||
|
||
lr = state[ix][iy-1][0]
|
||
+ state[ix-1][iy][0] + state[ix][iy ][0]*weight + state[ix+1][iy][0]
|
||
+ state[ix][iy+1][0];
|
||
|
||
lg = state[ix][iy-1][1]
|
||
+ state[ix-1][iy][1] + state[ix][iy ][1]*weight + state[ix+1][iy][1]
|
||
+ state[ix][iy+1][1];
|
||
|
||
lb = state[ix][iy-1][2]
|
||
+ state[ix-1][iy][2] + state[ix][iy ][2]*weight + state[ix+1][iy][2]
|
||
+ state[ix][iy+1][2];
|
||
|
||
lr /= weight+4.0;
|
||
lg /= weight+4.0;
|
||
lb /= weight+4.0;
|
||
putpixel(canvas, x+ix-16, y+iy-16, SDL_MapRGB(canvas->format, linear_to_sRGB(lr), linear_to_sRGB(lg), linear_to_sRGB(lb)));
|
||
}
|
||
SDL_UnlockSurface(canvas);
|
||
}
|
||
else if (cur_magic == MAGIC_BLOCKS)
|
||
{
|
||
/* Put x/y on exact grid points: */
|
||
|
||
x = (x / 4) * 4;
|
||
y = (y / 4) * 4;
|
||
|
||
SDL_LockSurface(last);
|
||
SDL_LockSurface(canvas);
|
||
|
||
for (yy = y - 8; yy < y + 8; yy = yy + 4)
|
||
{
|
||
for (xx = x - 8; xx < x + 8; xx = xx + 4)
|
||
{
|
||
Uint32 pix[16];
|
||
Uint32 p_or = 0;
|
||
Uint32 p_and = ~0;
|
||
unsigned i = 16;
|
||
while(i--)
|
||
{
|
||
Uint32 p_tmp;
|
||
p_tmp = getpixel_last(last, xx+(i>>2), yy+(i&3));
|
||
p_or |= p_tmp;
|
||
p_and &= p_tmp;
|
||
pix[i] = p_tmp;
|
||
}
|
||
if(p_or==p_and) // if all pixels the same already
|
||
{
|
||
SDL_GetRGB(p_or, last->format, &r, &g, &b);
|
||
}
|
||
else // nope, must average them
|
||
{
|
||
double r_sum = 0.0;
|
||
double g_sum = 0.0;
|
||
double b_sum = 0.0;
|
||
i = 16;
|
||
while(i--)
|
||
{
|
||
SDL_GetRGB(pix[i], last->format, &r, &g, &b);
|
||
r_sum += sRGB_to_linear_table[r];
|
||
g_sum += sRGB_to_linear_table[g];
|
||
b_sum += sRGB_to_linear_table[b];
|
||
}
|
||
r = linear_to_sRGB(r_sum/16.0);
|
||
g = linear_to_sRGB(g_sum/16.0);
|
||
b = linear_to_sRGB(b_sum/16.0);
|
||
}
|
||
|
||
/* Draw block: */
|
||
|
||
dest.x = xx;
|
||
dest.y = yy;
|
||
dest.w = 4;
|
||
dest.h = 4;
|
||
|
||
SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format, r, g, b));
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(canvas);
|
||
SDL_UnlockSurface(last);
|
||
}
|
||
else if (cur_magic == MAGIC_LARGEBRICK || cur_magic == MAGIC_SMALLBRICK)
|
||
{
|
||
// "specified" means the brick itself, w/o morter
|
||
// "nominal" means brick-to-brick (includes morter)
|
||
int specified_width, specified_height, specified_length;
|
||
int nominal_length;
|
||
int brick_x, brick_y;
|
||
|
||
int vertical_joint = 2; // between a brick and the one above/below
|
||
int horizontal_joint = 2; // between a brick and the one to the side
|
||
int nominal_width = 18;
|
||
int nominal_height = 12; // 11 to 14, for joints of 2
|
||
static unsigned char *map;
|
||
static int x_count;
|
||
static int y_count;
|
||
unsigned char *mybrick;
|
||
|
||
#if 0
|
||
if (cur_magic == MAGIC_SMALLBRICK)
|
||
{
|
||
vertical_joint = 1; // between a brick and the one above/below
|
||
horizontal_joint = 1; // between a brick and the one to the side
|
||
nominal_width = 9;
|
||
nominal_height = 6; // 11 to 14, for joints of 2
|
||
}
|
||
#endif
|
||
#if 0
|
||
if (cur_magic == MAGIC_LARGEBRICK)
|
||
{
|
||
vertical_joint = 3; // between a brick and the one above/below
|
||
horizontal_joint = 3; // between a brick and the one to the side
|
||
nominal_width = 27;
|
||
nominal_height = 18; // 11 to 14, for joints of 2
|
||
}
|
||
#endif
|
||
#if 1
|
||
if (cur_magic == MAGIC_LARGEBRICK)
|
||
{
|
||
vertical_joint = 4; // between a brick and the one above/below
|
||
horizontal_joint = 4; // between a brick and the one to the side
|
||
nominal_width = 36;
|
||
nominal_height = 24; // 11 to 14, for joints of 2
|
||
}
|
||
#endif
|
||
|
||
nominal_length = 2 * nominal_width;
|
||
specified_width = nominal_width - horizontal_joint;
|
||
specified_height = nominal_height - vertical_joint;
|
||
specified_length = nominal_length - horizontal_joint;
|
||
|
||
if (!button_down)
|
||
{
|
||
if (map)
|
||
free(map);
|
||
// the "+ 3" allows for both ends and misalignment
|
||
x_count = (canvas->w + nominal_width - 1) / nominal_width + 3;
|
||
y_count = (canvas->h + nominal_height - 1) / nominal_height + 3;
|
||
map = calloc(x_count,y_count);
|
||
}
|
||
|
||
brick_x = x / nominal_width;
|
||
brick_y = y / nominal_height;
|
||
|
||
mybrick = map + brick_x+1 + (brick_y+1)*x_count;
|
||
|
||
if ( (unsigned)x < (unsigned)canvas->w && (unsigned)y < (unsigned)canvas->h && !*mybrick)
|
||
{
|
||
int my_x = brick_x*nominal_width;
|
||
int my_w = specified_width;
|
||
*mybrick = 1;
|
||
SDL_LockSurface(canvas);
|
||
if((brick_y^brick_x)&1)
|
||
{
|
||
if(mybrick[1])
|
||
my_w = specified_length;
|
||
}
|
||
else
|
||
if(mybrick[-1])
|
||
{
|
||
my_x -= nominal_width;
|
||
my_w = specified_length;
|
||
}
|
||
do_brick(my_x, brick_y*nominal_height, my_w, specified_height);
|
||
SDL_UnlockSurface(canvas);
|
||
// upper left corner and lower right corner
|
||
update_canvas(brick_x*nominal_width-nominal_width, brick_y*nominal_height-vertical_joint, brick_x*nominal_width+specified_length, (brick_y+1)*nominal_height);
|
||
}
|
||
}
|
||
else if (cur_magic == MAGIC_SMUDGE)
|
||
{
|
||
static double state[32][32][3];
|
||
unsigned i = 32*32;
|
||
double rate = button_down ? 0.5 : 0.0;
|
||
|
||
SDL_LockSurface(canvas);
|
||
|
||
while (i--)
|
||
{
|
||
int iy = i>>5;
|
||
int ix = i&0x1f;
|
||
// is it not on the circle of radius sqrt(120) at location 16,16?
|
||
if ( (ix-16)*(ix-16) + (iy-16)*(iy-16) > 120)
|
||
continue;
|
||
// it is on the circle, so grab it
|
||
|
||
SDL_GetRGB(getpixel_canvas(canvas, x+ix-16, y+iy-16), last->format, &r, &g, &b);
|
||
state[ix][iy][0] = rate*state[ix][iy][0] + (1.0-rate)*sRGB_to_linear_table[r];
|
||
state[ix][iy][1] = rate*state[ix][iy][1] + (1.0-rate)*sRGB_to_linear_table[g];
|
||
state[ix][iy][2] = rate*state[ix][iy][2] + (1.0-rate)*sRGB_to_linear_table[b];
|
||
|
||
// opacity 100% --> new data not blended w/ existing data
|
||
putpixel(canvas, x+ix-16, y+iy-16, SDL_MapRGB(canvas->format, linear_to_sRGB(state[ix][iy][0]), linear_to_sRGB(state[ix][iy][1]), linear_to_sRGB(state[ix][iy][2])));
|
||
}
|
||
SDL_UnlockSurface(canvas);
|
||
}
|
||
else if (cur_magic == MAGIC_NEGATIVE)
|
||
{
|
||
SDL_LockSurface(last);
|
||
SDL_LockSurface(canvas);
|
||
|
||
for (yy = y - 16; yy < y + 16; yy++)
|
||
{
|
||
for (xx = x - 16; xx < x + 16; xx++)
|
||
{
|
||
SDL_GetRGB(getpixel_last(last, xx, yy), last->format,
|
||
&r, &g, &b);
|
||
|
||
r = 0xFF - r;
|
||
g = 0xFF - g;
|
||
b = 0xFF - b;
|
||
|
||
putpixel(canvas, xx, yy, SDL_MapRGB(canvas->format, r, g, b));
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(canvas);
|
||
SDL_UnlockSurface(last);
|
||
}
|
||
else if (cur_magic == MAGIC_FADE || cur_magic == MAGIC_DARKEN)
|
||
{
|
||
SDL_LockSurface(last);
|
||
SDL_LockSurface(canvas);
|
||
|
||
for (yy = y - 16; yy < y + 16; yy++)
|
||
{
|
||
for (xx = x - 16; xx < x + 16; xx++)
|
||
{
|
||
/* Get original color: */
|
||
|
||
SDL_GetRGB(getpixel_last(last, xx, yy), last->format,
|
||
&r, &g, &b);
|
||
|
||
if (cur_magic == MAGIC_FADE)
|
||
{
|
||
r = min(r + 48, 255);
|
||
g = min(g + 48, 255);
|
||
b = min(b + 48, 255);
|
||
}
|
||
else
|
||
{
|
||
r = max(r - 48, 0);
|
||
g = max(g - 48, 0);
|
||
b = max(b - 48, 0);
|
||
}
|
||
|
||
putpixel(canvas, xx, yy, SDL_MapRGB(canvas->format, r, g, b));
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(canvas);
|
||
SDL_UnlockSurface(last);
|
||
}
|
||
else if (cur_magic == MAGIC_TINT)
|
||
{
|
||
double rd = sRGB_to_linear_table[color_hexes[cur_color][0]];
|
||
double gd = sRGB_to_linear_table[color_hexes[cur_color][1]];
|
||
double bd = sRGB_to_linear_table[color_hexes[cur_color][2]];
|
||
double old;
|
||
|
||
SDL_LockSurface(last);
|
||
SDL_LockSurface(canvas);
|
||
|
||
for (yy = y - 16; yy < y + 16; yy++)
|
||
{
|
||
for (xx = x - 16; xx < x + 16; xx++)
|
||
{
|
||
/* Get original pixel: */
|
||
|
||
SDL_GetRGB(getpixel_last(last, xx, yy), last->format,
|
||
&r, &g, &b);
|
||
|
||
old = sRGB_to_linear_table[r] * 0.2126 +
|
||
sRGB_to_linear_table[g] * 0.7152 +
|
||
sRGB_to_linear_table[b] * 0.0722;
|
||
|
||
putpixel(canvas, xx, yy,
|
||
SDL_MapRGB(canvas->format,
|
||
linear_to_sRGB(rd * old),
|
||
linear_to_sRGB(gd * old),
|
||
linear_to_sRGB(bd * old)));
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(canvas);
|
||
SDL_UnlockSurface(last);
|
||
}
|
||
else if (cur_magic == MAGIC_CARTOON)
|
||
{
|
||
float hue, sat, val;
|
||
|
||
SDL_LockSurface(last);
|
||
SDL_LockSurface(canvas);
|
||
|
||
for (yy = y - 16; yy < y + 16; yy++)
|
||
{
|
||
for (xx = x - 16; xx < x + 16; xx++)
|
||
{
|
||
/* Get original color: */
|
||
|
||
SDL_GetRGB(getpixel_last(last, xx, yy), last->format,
|
||
&r, &g, &b);
|
||
|
||
rgbtohsv(r, g, b, &hue, &sat, &val);
|
||
|
||
if (sat <= 0.1)
|
||
sat = 0;
|
||
else
|
||
sat = 1.0;
|
||
|
||
val = val - 0.5;
|
||
val = val * 4;
|
||
val = val + 0.5;
|
||
|
||
if (val < 0)
|
||
val = 0;
|
||
else if (val > 1.0)
|
||
val = 1.0;
|
||
|
||
val = floor(val * 10) / 10;
|
||
|
||
hue = floor(hue * 10) / 10;
|
||
|
||
hsvtorgb(hue, sat, val, &r, &g, &b);
|
||
|
||
putpixel(canvas, xx, yy, SDL_MapRGB(canvas->format, r, g, b));
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(canvas);
|
||
SDL_UnlockSurface(last);
|
||
}
|
||
else if (cur_magic == MAGIC_RAINBOW)
|
||
{
|
||
/* Pick next color: */
|
||
|
||
colr = SDL_MapRGB(canvas->format,
|
||
rainbow_hexes[rainbow_color][0],
|
||
rainbow_hexes[rainbow_color][1],
|
||
rainbow_hexes[rainbow_color][2]);
|
||
|
||
|
||
/* Draw the shape: */
|
||
|
||
for (yy = 0; yy <= 16; yy++)
|
||
{
|
||
w = (yy * yy) / 16;
|
||
|
||
|
||
/* Top half: */
|
||
|
||
dest.x = x - 16 + w;
|
||
dest.w = 32 - (w * 2);
|
||
dest.y = y - yy;;
|
||
dest.h = 1;
|
||
|
||
SDL_FillRect(canvas, &dest, colr);
|
||
|
||
|
||
/* Bottom half: */
|
||
|
||
dest.x = x - 16 + w;
|
||
dest.w = 32 - (w * 2);
|
||
dest.y = y + yy;
|
||
dest.h = 1;
|
||
|
||
SDL_FillRect(canvas, &dest, colr);
|
||
}
|
||
}
|
||
else if (cur_magic == MAGIC_CHALK)
|
||
{
|
||
SDL_LockSurface(last);
|
||
|
||
for (yy = y - 8; yy <= y + 8; yy = yy + 4)
|
||
{
|
||
for (xx = x - 8; xx <= x + 8; xx = xx + 4)
|
||
{
|
||
dest.x = xx + ((rand() % 5) - 2);
|
||
dest.y = yy + ((rand() % 5) - 2);
|
||
dest.w = (rand() % 4) + 2;
|
||
dest.h = (rand() % 4) + 2;
|
||
|
||
colr = getpixel_last(last, clamp(0, xx, canvas->w-1),
|
||
clamp(0, yy, canvas->h-1));
|
||
SDL_FillRect(canvas, &dest, colr);
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(last);
|
||
}
|
||
else if (cur_magic == MAGIC_DRIP)
|
||
{
|
||
for (xx = x - 8; xx <= x + 8; xx++)
|
||
{
|
||
h = (rand() % 8) + 8;
|
||
|
||
for (yy = y; yy <= y + h; yy++)
|
||
{
|
||
src.x = xx;
|
||
src.y = y;
|
||
src.w = 1;
|
||
src.h = 16;
|
||
|
||
dest.x = xx;
|
||
dest.y = yy;
|
||
|
||
SDL_BlitSurface(last, &src, canvas, &dest);
|
||
}
|
||
}
|
||
}
|
||
else if (cur_magic == MAGIC_SPARKLES)
|
||
{
|
||
if ((rand() % 10) < 2)
|
||
{
|
||
src.x = 0;
|
||
src.y = (rand() % 4) * 32;
|
||
src.w = 32;
|
||
src.h = 32;
|
||
|
||
dest.x = x - 16;
|
||
dest.y = y - 16;
|
||
|
||
SDL_BlitSurface(img_sparkles, &src, canvas, &dest);
|
||
}
|
||
}
|
||
else if (cur_magic == MAGIC_GRASS)
|
||
{
|
||
// grass color: 82,180,17
|
||
static int bucket;
|
||
double tmp_red, tmp_green, tmp_blue;
|
||
Uint32 (*getpixel_grass)(SDL_Surface *, int, int);
|
||
|
||
if (!button_down)
|
||
bucket = 0;
|
||
bucket += (3.5+(rand()/(double)RAND_MAX)) * 7.0;
|
||
while (bucket >= 0)
|
||
{
|
||
int rank = log2int( ((double)y/canvas->h) * (0.99+(rand()/(double)RAND_MAX)) * 64 );
|
||
int ah = 1<<rank;
|
||
bucket -= ah;
|
||
src.x = (rand() % 4) * 64;
|
||
src.y = ah;
|
||
src.w = 64;
|
||
src.h = ah;
|
||
|
||
dest.x = x - 32;
|
||
dest.y = y - 30 + (int)((rand()/(double)RAND_MAX)*30);
|
||
|
||
#if 1
|
||
// grass color: 82,180,17
|
||
|
||
tmp_red = sRGB_to_linear_table[color_hexes[cur_color][0]]*2.0 + (rand()/(double)RAND_MAX);
|
||
tmp_green = sRGB_to_linear_table[color_hexes[cur_color][1]]*2.0 + (rand()/(double)RAND_MAX);
|
||
tmp_blue = sRGB_to_linear_table[color_hexes[cur_color][2]]*2.0 + sRGB_to_linear_table[17];
|
||
|
||
getpixel_grass = getpixels[img_grass->format->BytesPerPixel];
|
||
|
||
for (yy = 0; yy < ah; yy++)
|
||
{
|
||
for (xx = 0; xx < 64; xx++)
|
||
{
|
||
double rd, gd, bd;
|
||
|
||
SDL_GetRGBA(getpixel_grass(img_grass, xx+src.x, yy+src.y), img_grass->format,
|
||
&r, &g, &b, &a);
|
||
|
||
rd = sRGB_to_linear_table[r]*8.0 + tmp_red;
|
||
rd = rd * (a/255.0) / 11.0;
|
||
gd = sRGB_to_linear_table[g]*8.0 + tmp_green;
|
||
gd = gd * (a/255.0) / 11.0;
|
||
bd = sRGB_to_linear_table[b]*8.0 + tmp_blue;
|
||
bd = bd * (a/255.0) / 11.0;
|
||
|
||
SDL_GetRGB(getpixel_canvas(canvas, xx+dest.x, yy+dest.y), canvas->format,
|
||
&r, &g, &b);
|
||
|
||
r = linear_to_sRGB(sRGB_to_linear_table[r]*(1.0-a/255.0) + rd);
|
||
g = linear_to_sRGB(sRGB_to_linear_table[g]*(1.0-a/255.0) + gd);
|
||
b = linear_to_sRGB(sRGB_to_linear_table[b]*(1.0-a/255.0) + bd);
|
||
|
||
putpixel(canvas, xx+dest.x, yy+dest.y, SDL_MapRGB(canvas->format, r, g, b));
|
||
}
|
||
}
|
||
#else
|
||
// untinted
|
||
SDL_BlitSurface(img_grass, &src, canvas, &dest);
|
||
#endif
|
||
}
|
||
}
|
||
else if (cur_magic == MAGIC_FLIP)
|
||
{
|
||
/* Flip the canvas: */
|
||
|
||
for (yy = 0; yy < canvas->h; yy++)
|
||
{
|
||
src.x = 0;
|
||
src.y = yy;
|
||
src.w = canvas->w;
|
||
src.h = 1;
|
||
|
||
dest.x = 0;
|
||
dest.y = canvas->h - yy - 1;
|
||
|
||
SDL_BlitSurface(last, &src, canvas, &dest);
|
||
}
|
||
|
||
|
||
/* Flip starter, too! */
|
||
|
||
starter_flipped = !starter_flipped;
|
||
|
||
if (img_starter != NULL)
|
||
flip_starter();
|
||
}
|
||
else if (cur_magic == MAGIC_MIRROR)
|
||
{
|
||
/* Mirror-image the canvas: */
|
||
|
||
for (xx = 0; xx < canvas->w; xx++)
|
||
{
|
||
src.x = xx;
|
||
src.y = 0;
|
||
src.w = 1;
|
||
src.h = canvas->h;
|
||
|
||
dest.x = canvas->w - xx - 1;
|
||
dest.y = 0;
|
||
|
||
SDL_BlitSurface(last, &src, canvas, &dest);
|
||
}
|
||
|
||
|
||
/* Mirror starter, too! */
|
||
|
||
starter_mirrored = !starter_mirrored;
|
||
|
||
if (img_starter != NULL)
|
||
mirror_starter();
|
||
}
|
||
#if 0
|
||
else if (cur_magic == MAGIC_THIN || cur_magic == MAGIC_THICK)
|
||
{
|
||
SDL_LockSurface(last);
|
||
SDL_LockSurface(canvas);
|
||
|
||
for (xx = -8; xx <= 8; xx++)
|
||
{
|
||
for (yy = -8; yy <= 8; yy++)
|
||
{
|
||
SDL_GetRGB(getpixel(last, x + xx, y + yy), last->format,
|
||
&r, &g, &b);
|
||
|
||
r = min(r, (Uint8)255);
|
||
g = min(g, (Uint8)255);
|
||
b = min(b, (Uint8)255);
|
||
|
||
if ((cur_magic == MAGIC_THIN && (((r + g + b) / 3) > 128)) ||
|
||
(cur_magic == MAGIC_THICK && (((r + g + b) / 3) <= 128)))
|
||
{
|
||
putpixel(canvas, x + xx + 0, y + yy - 1,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx - 1, y + yy + 0,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx + 1, y + yy + 0,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx + 0, y + yy + 1,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
|
||
putpixel(canvas, x + xx - 1, y + yy - 1,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx - 1, y + yy + 1,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx + 1, y + yy - 1,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx + 1, y + yy + 1,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
|
||
putpixel(canvas, x + xx + 0, y + yy - 2,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx - 2, y + yy + 0,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx + 2, y + yy + 0,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
putpixel(canvas, x + xx + 0, y + yy + 2,
|
||
SDL_MapRGB(canvas->format, r, g, b));
|
||
}
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(canvas);
|
||
SDL_UnlockSurface(last);
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
|
||
/* Store canvas into undo buffer: */
|
||
|
||
static void rec_undo_buffer(void)
|
||
{
|
||
int wanna_update_toolbar;
|
||
|
||
wanna_update_toolbar = 0;
|
||
|
||
|
||
SDL_BlitSurface(canvas, NULL, undo_bufs[cur_undo], NULL);
|
||
undo_starters[cur_undo] = UNDO_STARTER_NONE;
|
||
|
||
cur_undo = (cur_undo + 1) % NUM_UNDO_BUFS;
|
||
|
||
if (cur_undo == oldest_undo)
|
||
oldest_undo = (oldest_undo + 1) % NUM_UNDO_BUFS;
|
||
|
||
newest_undo = cur_undo;
|
||
|
||
#ifdef DEBUG
|
||
printf("DRAW: Current=%d Oldest=%d Newest=%d\n",
|
||
cur_undo, oldest_undo, newest_undo);
|
||
#endif
|
||
|
||
|
||
/* Update toolbar buttons, if needed: */
|
||
|
||
if (tool_avail[TOOL_UNDO] == 0)
|
||
{
|
||
tool_avail[TOOL_UNDO] = 1;
|
||
wanna_update_toolbar = 1;
|
||
}
|
||
|
||
if (tool_avail[TOOL_REDO])
|
||
{
|
||
tool_avail[TOOL_REDO] = 0;
|
||
wanna_update_toolbar = 1;
|
||
}
|
||
|
||
if (wanna_update_toolbar)
|
||
{
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
}
|
||
|
||
|
||
/* Show program version: */
|
||
static void show_version(void)
|
||
{
|
||
printf("\nTux Paint\n");
|
||
printf(" Version " VER_VERSION " (" VER_DATE ")\n");
|
||
|
||
#ifdef LOW_QUALITY_THUMBNAILS
|
||
printf(" Low Quality Thumbnails enabled\n");
|
||
#endif
|
||
|
||
#ifdef LOW_QUALITY_COLOR_SELECTOR
|
||
printf(" Low Quality Color Selector enabled\n");
|
||
#endif
|
||
|
||
#ifdef LOW_QUALITY_STAMP_OUTLINE
|
||
printf(" Low Quality Stamp Outline enabled\n");
|
||
#endif
|
||
|
||
#ifdef LOW_QUALITY_FLOOD_FILL
|
||
printf(" Low Quality Flood Fill enabled\n");
|
||
#endif
|
||
|
||
#ifdef NO_PROMPT_SHADOWS
|
||
printf(" Prompt Shadows disabled\n");
|
||
#endif
|
||
|
||
#ifdef NOSOUND
|
||
printf(" Sound disabled\n");
|
||
#endif
|
||
|
||
#ifdef DEBUG
|
||
printf(" Verbose debugging enabled\n");
|
||
#endif
|
||
|
||
printf("\n");
|
||
}
|
||
|
||
|
||
/* Show usage display: */
|
||
|
||
static void show_usage(FILE * f, char * prg)
|
||
{
|
||
char * blank;
|
||
unsigned i;
|
||
|
||
blank = strdup(prg);
|
||
|
||
for (i = 0; i < strlen(blank); i++)
|
||
blank[i] = ' ';
|
||
|
||
fprintf(f,
|
||
"\n"
|
||
"Usage: %s {--usage | --help | --version | --copying}\n"
|
||
"\n"
|
||
" %s [--windowed | --fullscreen]\n"
|
||
" %s [--640x480 | --800x600 | --1024x768 |\n"
|
||
" %s --1280x1024 | --1400x1050 | --1600x1200]\n"
|
||
" %s [--sound | --nosound] [--quit | --noquit]\n"
|
||
" %s [--print | --noprint] [--complexshapes | --simpleshapes]\n"
|
||
" %s [--mixedcase | --uppercase] [--fancycursors | --nofancycursors]\n"
|
||
" %s [--mouse | --keyboard] [--dontgrab | --grab]\n"
|
||
" %s [--noshortcuts | --shortcuts] [--wheelmouse | --nowheelmouse]\n"
|
||
" %s [--outlines | --nooutlines] [--stamps | --nostamps]\n"
|
||
" %s [--sysfonts | --nosysfonts]\n"
|
||
" %s [--nostampcontrols | --stampcontrols]\n"
|
||
" %s [--mirrorstamps | --dontmirrorstamps]\n"
|
||
" %s [--saveoverask | --saveover | --saveovernew]\n"
|
||
" %s [--nosave | --save]\n"
|
||
" %s [--savedir DIRECTORY]\n"
|
||
#ifdef WIN32
|
||
" %s [--printcfg | --noprintcfg]\n"
|
||
#endif
|
||
" %s [--printdelay=SECONDS]\n"
|
||
" %s [--altprintmod | --altprintalways | --altprintnever]\n"
|
||
" %s [--lang LANGUAGE | --locale LOCALE | --lang help]\n"
|
||
" %s [--nosysconfig] [--nolockfile]\n"
|
||
/* " %s [--record FILE | --playback FILE]\n" */
|
||
"\n",
|
||
prg, prg,
|
||
blank, blank, blank,
|
||
blank, blank, blank,
|
||
blank, blank, blank,
|
||
blank, blank, blank,
|
||
blank, blank,
|
||
#ifdef WIN32
|
||
blank,
|
||
#endif
|
||
blank, blank, blank,
|
||
blank);
|
||
|
||
free(blank);
|
||
}
|
||
|
||
|
||
// The original Tux Paint canvas was 448x376. The canvas can be
|
||
// other sizes now, but many old stamps are sized for the small
|
||
// canvas. So, with larger canvases, we must choose a good scale
|
||
// factor to compensate. As the canvas size grows, the user will
|
||
// want a balance of "more stamps on the screen" and "stamps not
|
||
// getting tiny". This will calculate the needed scale factor.
|
||
static unsigned compute_default_scale_factor(double ratio)
|
||
{
|
||
double old_diag = sqrt(448*448+376*376);
|
||
double new_diag = sqrt(canvas->w*canvas->w+canvas->h*canvas->h);
|
||
double good_def = ratio*sqrt(new_diag/old_diag);
|
||
double good_log = log(good_def);
|
||
unsigned defsize = HARD_MAX_STAMP_SIZE;
|
||
while(defsize>0)
|
||
{
|
||
double this_err = good_log - log(scaletable[defsize].numer / (double)scaletable[defsize].denom);
|
||
double next_err = good_log - log(scaletable[defsize-1].numer / (double)scaletable[defsize-1].denom);
|
||
if( fabs(next_err) > fabs(this_err) ) break;
|
||
defsize--;
|
||
}
|
||
return defsize;
|
||
}
|
||
|
||
|
||
// backdoor into qsort operations, so we don't have to do work again
|
||
static int was_bad_font;
|
||
|
||
// see if two font surfaces are the same
|
||
static int do_surfcmp(const SDL_Surface *const *const v1, const SDL_Surface *const *const v2)
|
||
{
|
||
const SDL_Surface *const s1 = *v1;
|
||
const SDL_Surface *const s2 = *v2;
|
||
int width;
|
||
int cmp;
|
||
int i;
|
||
|
||
if(s1==s2)
|
||
{
|
||
printf("s1==s2?\n");
|
||
return 0;
|
||
}
|
||
if(!s1 || !s2 || !s1->w || !s2->w || !s1->h || !s2->h || !s1->format || !s2->format)
|
||
{
|
||
was_bad_font = 1;
|
||
return 0;
|
||
}
|
||
if(s1->format->BytesPerPixel != s2->format->BytesPerPixel)
|
||
{
|
||
// something really strange and bad happened
|
||
was_bad_font = 1;
|
||
return s1->format->BytesPerPixel - s2->format->BytesPerPixel;
|
||
}
|
||
|
||
if(s1->w != s2->w)
|
||
return s1->w - s2->w;
|
||
if(s1->h != s2->h)
|
||
return s1->h - s2->h;
|
||
|
||
{
|
||
const char *const c1 = (char *const)s1->pixels;
|
||
const char *const c2 = (char *const)s2->pixels;
|
||
width = s1->format->BytesPerPixel * s1->w;
|
||
if(width==s1->pitch)
|
||
return memcmp(c1,c2,width*s1->h);
|
||
cmp = 0;
|
||
i = s1->h;
|
||
while(i--)
|
||
{
|
||
cmp = memcmp(c1 + i*s1->pitch, c2 + i*s2->pitch, width);
|
||
if(cmp)
|
||
break;
|
||
}
|
||
}
|
||
|
||
return cmp;
|
||
}
|
||
|
||
// see if two font surfaces are the same
|
||
static int surfcmp(const void *s1, const void *s2)
|
||
{
|
||
int diff = do_surfcmp(s1, s2);
|
||
if(!diff)
|
||
was_bad_font = 1;
|
||
return diff;
|
||
}
|
||
|
||
// check if the characters will render distinctly
|
||
static int charset_works(TTF_Font *font, const char *s)
|
||
{
|
||
SDL_Color black = {0, 0, 0, 0};
|
||
SDL_Surface **surfs = malloc(strlen(s) * sizeof surfs[0]);
|
||
unsigned count = 0;
|
||
int ret = 0;
|
||
while(*s)
|
||
{
|
||
char c[8];
|
||
unsigned offset = 0;
|
||
SDL_Surface *tmp_surf;
|
||
do
|
||
c[offset++] = *s++;
|
||
while((*s & 0xc0u) == 0x80u); // assume safe input
|
||
c[offset++] = '\0';
|
||
tmp_surf = TTF_RenderUTF8_Blended(font, c, black);
|
||
if(!tmp_surf)
|
||
{
|
||
#if 0
|
||
// THREADED_FONTS
|
||
printf("could not render \"%s\" font\n", TTF_FontFaceFamilyName(font));
|
||
#endif
|
||
goto out;
|
||
}
|
||
surfs[count++] = tmp_surf;
|
||
}
|
||
was_bad_font = 0;
|
||
qsort(surfs, count, sizeof surfs[0], surfcmp);
|
||
ret = !was_bad_font;
|
||
out:
|
||
while(count--)
|
||
{
|
||
if (surfs[count] == NULL)
|
||
printf("TRYING TO RE-FREE!");
|
||
else
|
||
{
|
||
SDL_FreeSurface(surfs[count]);
|
||
surfs[count] = NULL;
|
||
}
|
||
}
|
||
free(surfs);
|
||
return ret;
|
||
}
|
||
|
||
|
||
/////////////////////////////// directory tree walking /////////////////////
|
||
|
||
#define TP_FTW_UNKNOWN 1
|
||
#define TP_FTW_DIRECTORY 2
|
||
#define TP_FTW_NORMAL 0
|
||
|
||
#define TP_FTW_PATHSIZE 400
|
||
|
||
typedef struct tp_ftw_str {
|
||
char *str;
|
||
unsigned char len;
|
||
// unsigned char is_rsrc;
|
||
} tp_ftw_str;
|
||
|
||
// For qsort()
|
||
static int compare_ftw_str(const void *v1, const void *v2)
|
||
{
|
||
const char *restrict const s1 = ((tp_ftw_str*)v1)->str;
|
||
const char *restrict const s2 = ((tp_ftw_str*)v2)->str;
|
||
return -strcmp(s1, s2);
|
||
}
|
||
|
||
static void tp_ftw(char *restrict const dir, unsigned dirlen, int rsrc,
|
||
void (*fn)(const char *restrict const dir, unsigned dirlen, tp_ftw_str *files, unsigned count)
|
||
)
|
||
{
|
||
DIR *d;
|
||
unsigned num_file_names = 0;
|
||
unsigned max_file_names = 0;
|
||
tp_ftw_str *file_names = NULL;
|
||
unsigned num_dir_names = 0;
|
||
unsigned max_dir_names = 0;
|
||
tp_ftw_str *dir_names = NULL;
|
||
int d_namlen;
|
||
int add_rsrc;
|
||
|
||
dir[dirlen++] = '/';
|
||
dir[dirlen] = '\0';
|
||
//printf("processing directory %s %d\n", dir, dirlen);
|
||
/* Open the directory: */
|
||
d = opendir(dir);
|
||
if (!d)
|
||
return;
|
||
|
||
for(;;)
|
||
{
|
||
struct dirent * f = readdir(d);
|
||
int filetype = TP_FTW_UNKNOWN;
|
||
|
||
if(!f)
|
||
break;
|
||
if(f->d_name[0]=='.')
|
||
continue;
|
||
// Linux and BSD can often provide file type info w/o the stat() call
|
||
#ifdef DT_UNKNOWN
|
||
switch(f->d_type)
|
||
{
|
||
default:
|
||
continue;
|
||
case DT_REG:
|
||
if(!rsrc) // if maybe opening resource files, need st_size
|
||
filetype = TP_FTW_NORMAL;
|
||
break;
|
||
case DT_DIR:
|
||
filetype = TP_FTW_DIRECTORY;
|
||
break;
|
||
case DT_UNKNOWN:
|
||
case DT_LNK:
|
||
;
|
||
}
|
||
#else
|
||
#warning Failed to see DT_UNKNOWN
|
||
#endif
|
||
|
||
#if defined(_DIRENT_HAVE_D_NAMLEN) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
|
||
d_namlen = f->d_namlen;
|
||
#else
|
||
d_namlen = strlen(f->d_name);
|
||
#endif
|
||
add_rsrc = 0;
|
||
|
||
if(filetype == TP_FTW_UNKNOWN)
|
||
{
|
||
struct stat sbuf;
|
||
memcpy(dir+dirlen, f->d_name, d_namlen+1);
|
||
if(stat(dir, &sbuf))
|
||
continue; // oh well... try the next one
|
||
if(S_ISDIR(sbuf.st_mode))
|
||
filetype = TP_FTW_DIRECTORY;
|
||
else if(S_ISREG(sbuf.st_mode))
|
||
{
|
||
filetype = TP_FTW_NORMAL;
|
||
if(rsrc && !sbuf.st_size)
|
||
add_rsrc = 5; // 5 is length of "/rsrc"
|
||
}
|
||
else
|
||
continue; // was a device file or somesuch
|
||
}
|
||
if(filetype==TP_FTW_NORMAL)
|
||
{
|
||
char *cp;
|
||
|
||
if(num_file_names==max_file_names)
|
||
{
|
||
max_file_names = max_file_names * 5 / 4 + 30;
|
||
file_names = realloc(file_names, max_file_names * sizeof *file_names);
|
||
}
|
||
cp = malloc(d_namlen + add_rsrc + 1);
|
||
memcpy(cp, f->d_name, d_namlen);
|
||
if(add_rsrc)
|
||
memcpy(cp+d_namlen, "/rsrc", 6);
|
||
else
|
||
cp[d_namlen] = '\0';
|
||
file_names[num_file_names].str = cp;
|
||
file_names[num_file_names].len = d_namlen;
|
||
num_file_names++;
|
||
}
|
||
if(filetype==TP_FTW_DIRECTORY)
|
||
{
|
||
char * cp;
|
||
if(num_dir_names==max_dir_names)
|
||
{
|
||
max_dir_names = max_dir_names * 5 / 4 + 3;
|
||
dir_names = realloc(dir_names, max_dir_names * sizeof *dir_names);
|
||
}
|
||
cp = malloc(d_namlen + 1);
|
||
memcpy(cp, f->d_name, d_namlen + 1);
|
||
dir_names[num_dir_names].str = cp;
|
||
dir_names[num_dir_names].len = d_namlen;
|
||
num_dir_names++;
|
||
}
|
||
}
|
||
|
||
closedir(d);
|
||
show_progress_bar();
|
||
dir[dirlen] = '\0'; // repair it (clobbered for stat() call above)
|
||
|
||
if(file_names)
|
||
{
|
||
// let callee sort and keep the string
|
||
#if 0
|
||
qsort(file_names, num_file_names, sizeof *file_names, compare_ftw_str);
|
||
while(num_file_names--)
|
||
{
|
||
free(file_names[num_file_names].str);
|
||
}
|
||
free(file_names);
|
||
#else
|
||
fn(dir, dirlen, file_names, num_file_names);
|
||
#endif
|
||
}
|
||
|
||
if(dir_names)
|
||
{
|
||
while(num_dir_names--)
|
||
{
|
||
memcpy(dir+dirlen, dir_names[num_dir_names].str, dir_names[num_dir_names].len+1);
|
||
tp_ftw(dir, dirlen+dir_names[num_dir_names].len, rsrc, fn);
|
||
free(dir_names[num_dir_names].str);
|
||
}
|
||
free(dir_names);
|
||
}
|
||
}
|
||
|
||
|
||
///////////////// directory walking callers and callbacks //////////////////
|
||
|
||
static void loadfont_callback(const char *restrict const dir, unsigned dirlen, tp_ftw_str *files, unsigned i)
|
||
{
|
||
dirlen = dirlen;
|
||
|
||
while(i--)
|
||
{
|
||
int loadable = 0;
|
||
const char *restrict const cp = strchr(files[i].str, '.');
|
||
show_progress_bar();
|
||
if(cp)
|
||
{
|
||
// need gcc 3.4 for the restrict in this location
|
||
const char * /*restrict*/ const suffixes[] = {"ttc", "dfont", "pfa", "pfb", "otf", "ttf",};
|
||
int j = sizeof suffixes / sizeof suffixes[0];
|
||
while(j--)
|
||
{
|
||
// only check part, because of potential .gz or .bz2 suffix
|
||
if(!strncasecmp(cp+1,suffixes[j],strlen(suffixes[j])))
|
||
{
|
||
loadable = 1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if(!loadable)
|
||
{
|
||
if(strcasestr(files[i].str, "/rsrc"))
|
||
loadable = 1;
|
||
}
|
||
// Loadable: TrueType (.ttf), OpenType (.otf), Type1 (.pfa and .pfb),
|
||
// and various useless bitmap fonts. Compressed files (with .gz or .bz2)
|
||
// should also work. A *.dfont is pretty much a Mac resource fork in a normal
|
||
// file, and may load with some library versions.
|
||
if (loadable)
|
||
{
|
||
char fname[512];
|
||
TTF_Font *font;
|
||
snprintf(fname, sizeof fname, "%s/%s", dir, files[i].str);
|
||
//printf("Loading font: %s\n", fname);
|
||
font = TTF_OpenFont(fname, text_sizes[text_size]);
|
||
if(font)
|
||
{
|
||
const char *restrict const family = TTF_FontFaceFamilyName(font);
|
||
const char *restrict const style = TTF_FontFaceStyleName(font);
|
||
int numfaces = TTF_FontFaces(font);
|
||
#ifdef DEBUG
|
||
if (numfaces != 1)
|
||
printf("Found %d faces in %s, %s, %s\n", numfaces, files[i].str, family, style);
|
||
#endif
|
||
|
||
if(strcmp("Zapfino",family) && strcmp("Elvish Ring NFI",family) && charset_works(font, gettext("jq")) && charset_works(font, gettext("JQ")))
|
||
{
|
||
if (num_font_styles==num_font_styles_max)
|
||
{
|
||
num_font_styles_max = num_font_styles_max * 5 / 4 + 30;
|
||
user_font_styles = realloc(user_font_styles, num_font_styles_max * sizeof *user_font_styles);
|
||
}
|
||
user_font_styles[num_font_styles] = malloc(sizeof *user_font_styles[num_font_styles]);
|
||
user_font_styles[num_font_styles]->directory = strdup(dir);
|
||
user_font_styles[num_font_styles]->filename = files[i].str; // steal it (mark NULL below)
|
||
user_font_styles[num_font_styles]->family = strdup(family);
|
||
user_font_styles[num_font_styles]->style = strdup(style);
|
||
user_font_styles[num_font_styles]->score = charset_works(font, gettext("oO"));
|
||
user_font_styles[num_font_styles]->score += charset_works(font, gettext("`\%_@$~#{}<>^&*"));
|
||
user_font_styles[num_font_styles]->score += charset_works(font, gettext(",.?!"));
|
||
user_font_styles[num_font_styles]->score += charset_works(font, gettext("017"));
|
||
user_font_styles[num_font_styles]->score += charset_works(font, gettext("O0"));
|
||
user_font_styles[num_font_styles]->score += charset_works(font, gettext("1Il|"));
|
||
num_font_styles++;
|
||
//printf("Accepted: %s, %s, %s\n", files[i].str, family, style);
|
||
files[i].str = NULL; // so free() won't crash -- we stole the memory
|
||
}
|
||
else
|
||
{
|
||
#if 0
|
||
// THREADED_FONTS
|
||
printf("Font is too defective: %s, %s, %s\n", files[i].str, family, style);
|
||
#endif
|
||
}
|
||
TTF_CloseFont(font);
|
||
}
|
||
else
|
||
{
|
||
#if 0
|
||
// THREADED_FONTS
|
||
printf("could not open %s\n", files[i].str);
|
||
#endif
|
||
}
|
||
}
|
||
free(files[i].str);
|
||
}
|
||
free(files);
|
||
}
|
||
|
||
|
||
|
||
static void loadfonts(const char * const dir)
|
||
{
|
||
char buf[TP_FTW_PATHSIZE];
|
||
unsigned dirlen = strlen(dir);
|
||
|
||
memcpy(buf,dir,dirlen);
|
||
tp_ftw(buf, dirlen, 1, loadfont_callback);
|
||
}
|
||
|
||
|
||
static void loadbrush_callback(const char *restrict const dir, unsigned dirlen, tp_ftw_str *files, unsigned i)
|
||
{
|
||
dirlen = dirlen;
|
||
|
||
qsort(files, i, sizeof *files, compare_ftw_str);
|
||
while(i--)
|
||
{
|
||
show_progress_bar();
|
||
if (strcasestr(files[i].str, ".png"))
|
||
{
|
||
char fname[512];
|
||
snprintf(fname, sizeof fname, "%s/%s", dir, files[i].str);
|
||
if (num_brushes==num_brushes_max)
|
||
{
|
||
num_brushes_max = num_brushes_max * 5 / 4 + 4;
|
||
img_brushes = realloc(img_brushes, num_brushes_max * sizeof *img_brushes);
|
||
}
|
||
img_brushes[num_brushes] = loadimage(fname);
|
||
num_brushes++;
|
||
}
|
||
free(files[i].str);
|
||
}
|
||
free(files);
|
||
}
|
||
|
||
|
||
|
||
static void load_brush_dir(const char * const dir)
|
||
{
|
||
char buf[TP_FTW_PATHSIZE];
|
||
unsigned dirlen = strlen(dir);
|
||
memcpy(buf,dir,dirlen);
|
||
tp_ftw(buf, dirlen, 0, loadbrush_callback);
|
||
}
|
||
|
||
SDL_Surface * mirror_surface(SDL_Surface *s)
|
||
{
|
||
SDL_Surface * new_surf;
|
||
int x;
|
||
SDL_Rect src, dest;
|
||
|
||
|
||
/* Mirror surface: */
|
||
|
||
new_surf = duplicate_surface(s);
|
||
|
||
if (new_surf != NULL)
|
||
{
|
||
for (x = 0; x < s->w; x++)
|
||
{
|
||
src.x = x;
|
||
src.y = 0;
|
||
src.w = 1;
|
||
src.h = s->h;
|
||
|
||
dest.x = s->w - x - 1;
|
||
dest.y = 0;
|
||
|
||
SDL_BlitSurface(s, &src, new_surf, &dest);
|
||
}
|
||
|
||
SDL_FreeSurface(s);
|
||
|
||
return(new_surf);
|
||
}
|
||
else
|
||
{
|
||
return(s);
|
||
}
|
||
}
|
||
|
||
SDL_Surface * flip_surface(SDL_Surface *s)
|
||
{
|
||
SDL_Surface * new_surf;
|
||
int y;
|
||
SDL_Rect src, dest;
|
||
|
||
|
||
/* Flip surface: */
|
||
|
||
new_surf = duplicate_surface(s);
|
||
|
||
if (new_surf != NULL)
|
||
{
|
||
for (y = 0; y < s->h; y++)
|
||
{
|
||
src.x = 0;
|
||
src.y = y;
|
||
src.w = s->w;
|
||
src.h = 1;
|
||
|
||
dest.x = 0;
|
||
dest.y = s->h - y - 1;
|
||
|
||
SDL_BlitSurface(s, &src, new_surf, &dest);
|
||
}
|
||
|
||
SDL_FreeSurface(s);
|
||
|
||
return(new_surf);
|
||
}
|
||
else
|
||
{
|
||
return(s);
|
||
}
|
||
}
|
||
|
||
static unsigned default_stamp_size;
|
||
|
||
static void loadstamp_finisher(stamp_type *sd, unsigned w, unsigned h, double ratio)
|
||
{
|
||
unsigned int upper = HARD_MAX_STAMP_SIZE;
|
||
unsigned int lower = 0;
|
||
unsigned mid;
|
||
|
||
/* If Tux Paint is in mirror-image-by-default mode, mirror, if we can: */
|
||
if (mirrorstamps && sd->mirrorable)
|
||
sd->mirrored = 1;
|
||
|
||
do
|
||
{
|
||
scaleparams *s = &scaletable[upper];
|
||
int pw, ph; // proposed width and height
|
||
|
||
pw = (w * s->numer + s->denom - 1) / s->denom;
|
||
ph = (h * s->numer + s->denom - 1) / s->denom;
|
||
|
||
// OK to let a stamp stick off the sides in one direction, not two
|
||
if (pw < canvas->w * 2 && ph < canvas->h * 1)
|
||
break;
|
||
if (pw < canvas->w * 1 && ph < canvas->h * 2)
|
||
break;
|
||
}
|
||
while (--upper);
|
||
|
||
do
|
||
{
|
||
scaleparams *s = &scaletable[lower];
|
||
int pw, ph; // proposed width and height
|
||
|
||
pw = (w * s->numer + s->denom - 1) / s->denom;
|
||
ph = (h * s->numer + s->denom - 1) / s->denom;
|
||
|
||
if (pw*ph > 20)
|
||
break;
|
||
}
|
||
while (++lower < HARD_MAX_STAMP_SIZE);
|
||
|
||
if(upper<lower)
|
||
{
|
||
// this, if it ever happens, is very bad
|
||
upper = (upper+lower)/2;
|
||
lower = upper;
|
||
}
|
||
|
||
mid = default_stamp_size;
|
||
if(ratio != 1.0)
|
||
mid = compute_default_scale_factor(ratio);
|
||
|
||
if(mid > upper)
|
||
mid = upper;
|
||
|
||
if(mid < lower)
|
||
mid = lower;
|
||
|
||
sd->min = lower;
|
||
sd->size = mid;
|
||
sd->max = upper;
|
||
}
|
||
|
||
|
||
// Note: must have read the *.dat file before calling this
|
||
static void set_active_stamp(void)
|
||
{
|
||
stamp_type *sd = stamp_data[cur_stamp];
|
||
unsigned len = strlen(sd->stampname);
|
||
char *buf = alloca(len + strlen("_mirror.png") + 1);
|
||
|
||
if(active_stamp)
|
||
SDL_FreeSurface(active_stamp);
|
||
active_stamp = NULL;
|
||
|
||
memcpy(buf, sd->stampname, len);
|
||
|
||
if(sd->mirrored && !sd->no_premirror)
|
||
{
|
||
memcpy(buf+len, "_mirror.png", 12);
|
||
active_stamp = do_loadimage(buf, 0);
|
||
}
|
||
if(!active_stamp)
|
||
{
|
||
memcpy(buf+len, ".png", 5);
|
||
active_stamp = do_loadimage(buf, 0);
|
||
if(!active_stamp)
|
||
active_stamp = thumbnail(img_dead40x40, 40, 40, 1); // copy it
|
||
if(sd->mirrored)
|
||
active_stamp = mirror_surface(active_stamp);
|
||
}
|
||
|
||
if(sd->flipped)
|
||
active_stamp = flip_surface(active_stamp);
|
||
}
|
||
|
||
static void get_stamp_thumb(stamp_type *sd)
|
||
{
|
||
SDL_Surface *bigimg = NULL;
|
||
unsigned len = strlen(sd->stampname);
|
||
char *buf = alloca(len + strlen("_mirror.png") + 1);
|
||
SDL_Surface *wrongmirror = NULL;
|
||
int need_mirror = 0;
|
||
double ratio;
|
||
unsigned w;
|
||
unsigned h;
|
||
|
||
memcpy(buf, sd->stampname, len);
|
||
|
||
if(!sd->processed)
|
||
{
|
||
memcpy(buf+len, ".dat", 5);
|
||
ratio = loadinfo(buf, sd);
|
||
}
|
||
else
|
||
ratio = 1.0;
|
||
|
||
#ifndef NOSOUND
|
||
// good time to load the sound
|
||
if(!sd->no_sound && !sd->ssnd && use_sound)
|
||
{
|
||
// damn thing wants a .png extension; give it one
|
||
memcpy(buf+len, ".png", 5);
|
||
sd->ssnd = loadsound(buf);
|
||
sd->no_sound = !sd->ssnd;
|
||
}
|
||
#endif
|
||
|
||
if(!sd->no_txt && !sd->stxt)
|
||
{
|
||
// damn thing wants a .png extension; give it one
|
||
memcpy(buf+len, ".png", 5);
|
||
sd->stxt = loaddesc(buf);
|
||
sd->no_txt = !sd->stxt;
|
||
}
|
||
|
||
// first see if we can re-use an existing thumbnail
|
||
if(sd->thumbnail)
|
||
{
|
||
if(sd->mirrored == sd->thumb_mirrored)
|
||
{
|
||
if(sd->flipped == sd->thumb_flipped)
|
||
return;
|
||
sd->thumbnail = flip_surface(sd->thumbnail);
|
||
sd->thumb_flipped = !sd->thumb_flipped;
|
||
return;
|
||
}
|
||
wrongmirror = sd->thumbnail;
|
||
}
|
||
|
||
// nope, unless perhaps it can be mirrored
|
||
if(sd->mirrored && !sd->no_premirror)
|
||
{
|
||
memcpy(buf+len, "_mirror.png", 12);
|
||
bigimg = do_loadimage(buf, 0);
|
||
if(bigimg)
|
||
{
|
||
if(wrongmirror)
|
||
SDL_FreeSurface(wrongmirror);
|
||
}
|
||
else
|
||
sd->no_premirror = 1;
|
||
}
|
||
if(wrongmirror && sd->no_premirror)
|
||
{
|
||
wrongmirror = mirror_surface(wrongmirror);
|
||
sd->thumbnail = wrongmirror;
|
||
sd->thumb_mirrored = !sd->thumb_mirrored;
|
||
|
||
if(sd->flipped == sd->thumb_flipped)
|
||
return;
|
||
sd->thumbnail = flip_surface(sd->thumbnail);
|
||
sd->thumb_flipped = !sd->thumb_flipped;
|
||
return;
|
||
}
|
||
if(!bigimg)
|
||
{
|
||
memcpy(buf+len, ".png", 5);
|
||
bigimg = do_loadimage(buf, 0);
|
||
if(sd->mirrored)
|
||
need_mirror = 1; // want to mirror after scaling
|
||
}
|
||
|
||
w = 40;
|
||
h = 40;
|
||
if(bigimg)
|
||
{
|
||
w = bigimg->w;
|
||
h = bigimg->h;
|
||
}
|
||
|
||
if(!bigimg)
|
||
sd->thumbnail = thumbnail(img_dead40x40, 40, 40, 1); // copy
|
||
else if (bigimg->w > 40 || bigimg->h > 40)
|
||
{
|
||
sd->thumbnail = thumbnail(bigimg, 40, 40, 1);
|
||
SDL_FreeSurface(bigimg);
|
||
}
|
||
else
|
||
sd->thumbnail = bigimg;
|
||
|
||
if(need_mirror)
|
||
sd->thumbnail = mirror_surface(sd->thumbnail);
|
||
sd->thumb_mirrored = sd->mirrored;
|
||
|
||
if(sd->flipped)
|
||
sd->thumbnail = flip_surface(sd->thumbnail);
|
||
sd->thumb_flipped = sd->flipped;
|
||
|
||
if(sd->processed)
|
||
return;
|
||
sd->processed = 1; // not really, but on the next line...
|
||
loadstamp_finisher(sd, w, h, ratio);
|
||
}
|
||
|
||
|
||
static void loadstamp_callback(const char *restrict const dir, unsigned dirlen, tp_ftw_str *files, unsigned i)
|
||
{
|
||
qsort(files, i, sizeof *files, compare_ftw_str);
|
||
while(i--)
|
||
{
|
||
char fname[512];
|
||
char *dotpng = strcasestr(files[i].str, ".png");
|
||
|
||
show_progress_bar();
|
||
|
||
if (dotpng>files[i].str && !strcasecmp(dotpng,".png") && (dotpng-files[i].str+1+dirlen < sizeof fname) && !strcasestr(files[i].str, "_mirror.png"))
|
||
{
|
||
snprintf(fname, sizeof fname, "%s/%s", dir, files[i].str);
|
||
if(num_stamps == max_stamps)
|
||
{
|
||
max_stamps = max_stamps * 5 / 4 + 15;
|
||
stamp_data = realloc(stamp_data, max_stamps * sizeof *stamp_data);
|
||
}
|
||
stamp_data[num_stamps] = calloc(1, sizeof *stamp_data[num_stamps]);
|
||
stamp_data[num_stamps]->stampname = malloc(dotpng-files[i].str+1+dirlen+1);
|
||
memcpy(stamp_data[num_stamps]->stampname, fname, dotpng-files[i].str+1+dirlen);
|
||
stamp_data[num_stamps]->stampname[dotpng-files[i].str+1+dirlen] = '\0';
|
||
num_stamps++;
|
||
}
|
||
free(files[i].str);
|
||
}
|
||
free(files);
|
||
}
|
||
|
||
|
||
|
||
static void load_stamp_dir(const char * const dir)
|
||
{
|
||
char buf[TP_FTW_PATHSIZE];
|
||
unsigned dirlen = strlen(dir);
|
||
memcpy(buf,dir,dirlen);
|
||
tp_ftw(buf, dirlen, 0, loadstamp_callback);
|
||
}
|
||
|
||
|
||
static void load_stamps(void)
|
||
{
|
||
char * homedirdir = get_fname("stamps");
|
||
|
||
default_stamp_size = compute_default_scale_factor(1.0);
|
||
|
||
load_stamp_dir(homedirdir);
|
||
load_stamp_dir(DATA_PREFIX "stamps");
|
||
#ifdef __APPLE__
|
||
load_stamp_dir("/Library/Application Support/TuxPaint/stamps");
|
||
#endif
|
||
#ifdef WIN32
|
||
free(homedirdir);
|
||
homedirdir = get_fname("data/stamps");
|
||
load_stamp_dir(homedirdir);
|
||
#endif
|
||
|
||
if (num_stamps == 0)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: No stamps found in " DATA_PREFIX "stamps/\n"
|
||
"or %s\n\n", homedirdir);
|
||
}
|
||
|
||
free(homedirdir);
|
||
}
|
||
|
||
|
||
|
||
static int load_user_fonts(void *vp)
|
||
{
|
||
char *homedirdir;
|
||
|
||
(void)vp; // junk passed by threading library
|
||
|
||
loadfonts(DATA_PREFIX "fonts");
|
||
|
||
if (!no_system_fonts)
|
||
{
|
||
#ifdef WIN32
|
||
loadfonts("%SystemRoot%\\Fonts");
|
||
#elif defined(__BEOS__)
|
||
loadfonts("/boot/home/config/font/ttffonts");
|
||
loadfonts("/usr/share/fonts");
|
||
loadfonts("/usr/X11R6/lib/X11/fonts");
|
||
#elif defined(__APPLE__)
|
||
loadfonts("/System/Library/Fonts");
|
||
loadfonts("/Library/Fonts");
|
||
loadfonts(macosx.fontsPath);
|
||
loadfonts("/usr/share/fonts");
|
||
loadfonts("/usr/X11R6/lib/X11/fonts");
|
||
#elif defined(__sun__)
|
||
loadfonts("/usr/openwin/lib/X11/fonts");
|
||
loadfonts("/usr/share/fonts");
|
||
loadfonts("/usr/X11R6/lib/X11/fonts");
|
||
#else
|
||
loadfonts("/usr/share/feh/fonts");
|
||
loadfonts("/usr/share/fonts");
|
||
loadfonts("/usr/X11R6/lib/X11/fonts");
|
||
loadfonts("/usr/share/texmf/fonts");
|
||
loadfonts("/usr/share/grace/fonts/type1");
|
||
loadfonts("/usr/share/hatman/fonts");
|
||
loadfonts("/usr/share/icewm/themes/jim-mac");
|
||
loadfonts("/usr/share/vlc/skins2/fonts");
|
||
loadfonts("/usr/share/xplanet/fonts");
|
||
#endif
|
||
}
|
||
|
||
homedirdir = get_fname("fonts");
|
||
loadfonts(homedirdir);
|
||
free(homedirdir);
|
||
#ifdef WIN32
|
||
homedirdir = get_fname("data/fonts");
|
||
loadfonts(homedirdir);
|
||
free(homedirdir);
|
||
#endif
|
||
|
||
groupfonts();
|
||
|
||
font_thread_done = 1;
|
||
// FIXME: need a memory barrier here
|
||
return 0; // useless, wanted by threading library
|
||
}
|
||
|
||
|
||
#ifdef FORKED_FONTS
|
||
|
||
#include <sys/socket.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <sys/poll.h>
|
||
#include <sys/wait.h>
|
||
|
||
#ifdef _POSIX_PRIORITY_SCHEDULING
|
||
#include <sched.h>
|
||
#else
|
||
#define sched_yield()
|
||
#endif
|
||
|
||
#ifdef __linux__
|
||
#include <sys/prctl.h>
|
||
#else
|
||
#define prctl(o,a1)
|
||
#define PR_SET_PDEATHSIG 0
|
||
#endif
|
||
|
||
static void reliable_write(int fd, const void *buf, size_t count)
|
||
{
|
||
struct pollfd p;
|
||
do
|
||
{
|
||
ssize_t rc = write(fd,buf,count);
|
||
if(rc==-1)
|
||
{
|
||
switch(errno)
|
||
{
|
||
default:
|
||
return;
|
||
case EAGAIN:
|
||
case ENOSPC:
|
||
; // satisfy a C syntax abomination
|
||
p = (struct pollfd){fd, POLLOUT, 0};
|
||
poll(&p, 1, -1); // try not to burn CPU time
|
||
// FALL THROUGH
|
||
case EINTR:
|
||
continue;
|
||
}
|
||
}
|
||
buf += rc;
|
||
count -= rc;
|
||
} while(count);
|
||
}
|
||
|
||
|
||
static void reliable_read(int fd, void *buf, size_t count)
|
||
{
|
||
struct pollfd p;
|
||
do
|
||
{
|
||
ssize_t rc = read(fd,buf,count);
|
||
if(rc==-1)
|
||
{
|
||
switch(errno)
|
||
{
|
||
default:
|
||
return;
|
||
case EAGAIN:
|
||
; // satisfy a C syntax abomination
|
||
p = (struct pollfd){fd, POLLIN, 0};
|
||
poll(&p, 1, -1); // try not to burn CPU time
|
||
// FALL THROUGH
|
||
case EINTR:
|
||
continue;
|
||
}
|
||
}
|
||
if(rc==0)
|
||
break; // EOF. Better not happen before the end!
|
||
buf += rc;
|
||
count -= rc;
|
||
} while(count);
|
||
}
|
||
|
||
|
||
static void run_font_scanner(void)
|
||
{
|
||
int sv[2];
|
||
int size, i;
|
||
char * buf, * walk;
|
||
|
||
if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
|
||
exit(42);
|
||
font_scanner_pid = fork();
|
||
if(font_scanner_pid)
|
||
{
|
||
// parent (or error -- but we're screwed in that case)
|
||
font_socket_fd = sv[0];
|
||
close(sv[1]);
|
||
return;
|
||
}
|
||
nice(42); // be nice, letting the main thread get the CPU
|
||
sched_yield(); // try to let the parent run right now
|
||
prctl(PR_SET_PDEATHSIG, 9); // get killed if parent exits
|
||
font_socket_fd = sv[1];
|
||
close(sv[0]);
|
||
progress_bar_disabled = 1;
|
||
reliable_read(font_socket_fd, &no_system_fonts, sizeof no_system_fonts);
|
||
sched_yield(); // try to let the parent run right now
|
||
SDL_Init(SDL_INIT_NOPARACHUTE);
|
||
TTF_Init();
|
||
load_user_fonts(NULL);
|
||
|
||
size = 0;
|
||
i = num_font_families;
|
||
while(i--)
|
||
{
|
||
char *s;
|
||
s = user_font_families[i]->directory;
|
||
if(s) size += strlen(s);
|
||
s = user_font_families[i]->family;
|
||
if(s) size += strlen(s);
|
||
s = user_font_families[i]->filename[0];
|
||
if(s) size += strlen(s);
|
||
s = user_font_families[i]->filename[1];
|
||
if(s) size += strlen(s);
|
||
s = user_font_families[i]->filename[2];
|
||
if(s) size += strlen(s);
|
||
s = user_font_families[i]->filename[3];
|
||
if(s) size += strlen(s);
|
||
size += 6; // for '\0' on each of the above
|
||
}
|
||
size += 2; // for 2-byte font count
|
||
buf = malloc(size);
|
||
walk = buf;
|
||
#ifdef DEBUG
|
||
printf("Sending %u bytes with %u families.\n", size, num_font_families);
|
||
#endif
|
||
*walk++ = num_font_families & 0xffu;
|
||
*walk++ = num_font_families >> 8u;
|
||
i = num_font_families;
|
||
while(i--)
|
||
{
|
||
int len;
|
||
char *s;
|
||
|
||
s = user_font_families[i]->directory;
|
||
if(s)
|
||
{
|
||
len = strlen(s);
|
||
memcpy(walk, s, len);
|
||
walk += len;
|
||
}
|
||
*walk++ = '\0';
|
||
|
||
s = user_font_families[i]->family;
|
||
if(s)
|
||
{
|
||
len = strlen(s);
|
||
memcpy(walk, s, len);
|
||
walk += len;
|
||
}
|
||
*walk++ = '\0';
|
||
|
||
s = user_font_families[i]->filename[0];
|
||
if(s)
|
||
{
|
||
len = strlen(s);
|
||
memcpy(walk, s, len);
|
||
walk += len;
|
||
}
|
||
*walk++ = '\0';
|
||
|
||
s = user_font_families[i]->filename[1];
|
||
if(s)
|
||
{
|
||
len = strlen(s);
|
||
memcpy(walk, s, len);
|
||
walk += len;
|
||
}
|
||
*walk++ = '\0';
|
||
|
||
s = user_font_families[i]->filename[2];
|
||
if(s)
|
||
{
|
||
len = strlen(s);
|
||
memcpy(walk, s, len);
|
||
walk += len;
|
||
}
|
||
*walk++ = '\0';
|
||
|
||
s = user_font_families[i]->filename[3];
|
||
if(s)
|
||
{
|
||
len = strlen(s);
|
||
memcpy(walk, s, len);
|
||
walk += len;
|
||
}
|
||
*walk++ = '\0';
|
||
}
|
||
reliable_write(font_socket_fd, buf, size);
|
||
exit(0);
|
||
}
|
||
|
||
static void receive_some_font_info(void)
|
||
{
|
||
char *buf = NULL;
|
||
unsigned buf_size = 0;
|
||
unsigned buf_fill = 0;
|
||
ssize_t rc;
|
||
struct pollfd p;
|
||
int status;
|
||
/* unsigned */ char *walk;
|
||
unsigned i;
|
||
family_info *fip;
|
||
|
||
fcntl(font_socket_fd, F_SETFL, O_NONBLOCK);
|
||
for(;;)
|
||
{
|
||
if(buf_size <= buf_fill*9/8+128)
|
||
{
|
||
buf_size = buf_size*5/4+256;
|
||
buf = realloc(buf, buf_size);
|
||
}
|
||
rc = read(font_socket_fd, buf+buf_fill, buf_size-buf_fill);
|
||
#ifdef DEBUG
|
||
printf("read: fd=%d buf_fill=%u buf_size=%u rc=%ld\n", font_socket_fd, buf_fill, buf_size, rc);
|
||
#endif
|
||
|
||
if(rc==-1)
|
||
{
|
||
switch(errno)
|
||
{
|
||
default:
|
||
return;
|
||
case EAGAIN:
|
||
; // satisfy a C syntax abomination
|
||
p = (struct pollfd){font_socket_fd, POLLIN, 0};
|
||
show_progress_bar();
|
||
poll(&p, 1, 29); // try not to burn CPU time
|
||
continue;
|
||
case EINTR:
|
||
continue;
|
||
}
|
||
}
|
||
buf_fill += rc;
|
||
if(!rc)
|
||
break;
|
||
}
|
||
close(font_socket_fd);
|
||
|
||
waitpid(font_scanner_pid,&status,0);
|
||
if(WIFSIGNALED(status))
|
||
{
|
||
printf("child killed by signal %u\n", WTERMSIG(status));
|
||
user_font_families = NULL;
|
||
num_font_families = 0;
|
||
font_thread_done = 1;
|
||
|
||
return;
|
||
}
|
||
|
||
show_progress_bar();
|
||
walk = buf;
|
||
num_font_families = *walk++;
|
||
num_font_families += *walk++ << 8u;
|
||
#ifdef DEBUG
|
||
printf("Got %u bytes with %u families.\n", buf_fill, num_font_families);
|
||
#endif
|
||
user_font_families = malloc(num_font_families * sizeof *user_font_families);
|
||
fip = malloc(num_font_families * sizeof **user_font_families);
|
||
i = num_font_families;
|
||
while(i--)
|
||
{
|
||
unsigned len;
|
||
user_font_families[i] = fip+i;
|
||
|
||
len = strlen(walk);
|
||
user_font_families[i]->directory = len ? walk : NULL;
|
||
walk += len + 1;
|
||
|
||
len = strlen(walk);
|
||
user_font_families[i]->family = len ? walk : NULL;
|
||
walk += len + 1;
|
||
|
||
len = strlen(walk);
|
||
user_font_families[i]->filename[0] = len ? walk : NULL;
|
||
walk += len + 1;
|
||
|
||
len = strlen(walk);
|
||
user_font_families[i]->filename[1] = len ? walk : NULL;
|
||
walk += len + 1;
|
||
|
||
len = strlen(walk);
|
||
user_font_families[i]->filename[2] = len ? walk : NULL;
|
||
walk += len + 1;
|
||
|
||
len = strlen(walk);
|
||
user_font_families[i]->filename[3] = len ? walk : NULL;
|
||
walk += len + 1;
|
||
|
||
user_font_families[i]->handle = NULL;
|
||
|
||
// score left uninitialized
|
||
}
|
||
font_thread_done = 1;
|
||
}
|
||
#endif
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
/* Setup: */
|
||
|
||
static void setup(int argc, char * argv[])
|
||
{
|
||
int i, ok_to_use_sysconfig;
|
||
char str[128];
|
||
char * upstr;
|
||
SDL_Color black = {0, 0, 0, 0};
|
||
char * homedirdir;
|
||
FILE * fi;
|
||
SDL_Surface * tmp_surf;
|
||
SDL_Rect dest;
|
||
int scale;
|
||
#ifndef LOW_QUALITY_COLOR_SELECTOR
|
||
int x, y;
|
||
SDL_Surface * tmp_btn_up;
|
||
SDL_Surface * tmp_btn_down;
|
||
Uint8 r, g, b;
|
||
#endif
|
||
SDL_Surface * tmp_imgcurup, * tmp_imgcurdown;
|
||
Uint32 init_flags;
|
||
char tmp_str[128];
|
||
SDL_Surface *img1;
|
||
SDL_Surface *img2;
|
||
Uint32 (*getpixel_tmp_btn_up)(SDL_Surface *, int, int);
|
||
Uint32 (*getpixel_tmp_btn_down)(SDL_Surface *, int, int);
|
||
Uint32 (*getpixel_img2)(SDL_Surface *, int, int);
|
||
|
||
|
||
|
||
#if defined(__BEOS__) || defined(WIN32)
|
||
/* if run from gui, like OpenTracker in BeOS or Explorer in Windows,
|
||
find path from which binary was run and change dir to it
|
||
so all files will be local :) */
|
||
/* UPDATE (2004.10.06): Since SDL 1.2.7 SDL sets that path correctly,
|
||
so this code wouldn't be needed if SDL was init before anything else,
|
||
(just basic init, window shouldn't be needed). */
|
||
/* UPDATE (2005 July 19): Enable and make work on Windows. Makes testing
|
||
with MINGW/MSYS easier */
|
||
|
||
if (argc && argv[0])
|
||
{
|
||
char *app_path = strdup(argv[0]);
|
||
char *slash = strrchr(app_path, '/');
|
||
|
||
if (!slash)
|
||
{
|
||
slash = strrchr(app_path, '\\');
|
||
}
|
||
if (slash)
|
||
{
|
||
*(slash + 1) = '\0';
|
||
chdir(app_path);
|
||
}
|
||
free(app_path);
|
||
}
|
||
#endif
|
||
|
||
|
||
/* Set default options: */
|
||
|
||
use_sound = 1;
|
||
fullscreen = 0;
|
||
noshortcuts = 0;
|
||
dont_do_xor = 0;
|
||
keymouse = 0;
|
||
wheely = 1;
|
||
no_button_distinction = 0;
|
||
grab_input = 0;
|
||
no_fancy_cursors = 0;
|
||
simple_shapes = 0;
|
||
only_uppercase = 0;
|
||
promptless_save = SAVE_OVER_PROMPT;
|
||
alt_print_command_default = ALTPRINT_MOD;
|
||
disable_quit = 0;
|
||
disable_save = 0;
|
||
disable_print = 0;
|
||
dont_load_stamps = 0;
|
||
no_system_fonts = 1;
|
||
print_delay = 0;
|
||
use_print_config = 0;
|
||
mirrorstamps = 0;
|
||
disable_stamp_controls = 0;
|
||
// WINDOW_WIDTH = 640;
|
||
// WINDOW_HEIGHT = 480;
|
||
WINDOW_WIDTH = 800;
|
||
WINDOW_HEIGHT = 600;
|
||
playfile = NULL;
|
||
recording = 0;
|
||
playing = 0;
|
||
ok_to_use_lockfile = 1;
|
||
|
||
|
||
#ifdef __BEOS__
|
||
/* Fancy cursors on BeOS are buggy in SDL */
|
||
|
||
no_fancy_cursors = 1;
|
||
#endif
|
||
|
||
|
||
#ifdef WIN32
|
||
savedir = GetDefaultSaveDir("TuxPaint");
|
||
#elif __BEOS__
|
||
savedir = strdup("./userdata");
|
||
#else
|
||
savedir = NULL;
|
||
#endif
|
||
|
||
|
||
|
||
/* Load options from global config file: */
|
||
|
||
#ifndef WIN32
|
||
|
||
/* Check to see if it's ok first: */
|
||
|
||
ok_to_use_sysconfig = 1;
|
||
|
||
for (i = 1; i < argc; i++)
|
||
{
|
||
if (strcmp(argv[i], "--nosysconfig") == 0)
|
||
{
|
||
ok_to_use_sysconfig = 0;
|
||
i = argc; /* aka break; */
|
||
}
|
||
}
|
||
|
||
|
||
if (ok_to_use_sysconfig)
|
||
{
|
||
snprintf(str, sizeof(str), "%s/tuxpaint.conf", CONFDIR);
|
||
|
||
fi = fopen(str, "r");
|
||
if (fi != NULL)
|
||
{
|
||
parse_options(fi);
|
||
fclose(fi);
|
||
}
|
||
else
|
||
debug(str);
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
/* Load options from user's own configuration (".rc" / ".cfg") file: */
|
||
|
||
#if defined(WIN32) || defined(__BEOS__)
|
||
/* Windows and BeOS: Use a "tuxpaint.cfg" file: */
|
||
|
||
strcpy(str, "tuxpaint.cfg");
|
||
|
||
//#elif __APPLE__
|
||
/* Mac: ??? */
|
||
/* FIXME! */
|
||
|
||
#else
|
||
/* Linux and other Unixes: Use 'rc' style (~/.tuxpaintrc) */
|
||
|
||
if (getenv("HOME") != NULL)
|
||
{
|
||
/* Should it be "~/.tuxpaint/tuxpaintrc" instead???
|
||
Comments welcome ... bill@newbreedsoftware.com */
|
||
|
||
snprintf(str, sizeof(str), "%s/.tuxpaintrc", getenv("HOME"));
|
||
}
|
||
else
|
||
{
|
||
/* WOAH! We don't know what our home directory is!? Last resort,
|
||
do it Windows/BeOS way: */
|
||
|
||
strcpy(str, "tuxpaint.cfg");
|
||
}
|
||
#endif
|
||
|
||
|
||
fi = fopen(str, "r");
|
||
if (fi != NULL)
|
||
{
|
||
parse_options(fi);
|
||
fclose(fi);
|
||
}
|
||
else
|
||
debug(str);
|
||
|
||
|
||
/* Handle command-line arguments: */
|
||
|
||
for (i = 1; i < argc; i++)
|
||
{
|
||
if (strcmp(argv[i], "--fullscreen") == 0 || strcmp(argv[i], "-f") == 0)
|
||
{
|
||
fullscreen = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--windowed") == 0 || strcmp(argv[i], "-w") == 0)
|
||
{
|
||
fullscreen = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--mirrorstamps") == 0)
|
||
{
|
||
mirrorstamps = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--dontmirrorstamps") == 0)
|
||
{
|
||
mirrorstamps = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--nostampcontrols") == 0)
|
||
{
|
||
disable_stamp_controls = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--stampcontrols") == 0)
|
||
{
|
||
disable_stamp_controls = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--noshortcuts") == 0)
|
||
{
|
||
noshortcuts = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--shortcuts") == 0)
|
||
{
|
||
noshortcuts = 0;
|
||
}
|
||
else if ( argv[i][0]=='-' && argv[i][1]=='-' && argv[i][2]>='1' && argv[i][2]<='9' )
|
||
{
|
||
char *endp1;
|
||
char *endp2;
|
||
int w,h;
|
||
w = strtoul(argv[i]+2, &endp1, 10);
|
||
h = strtoul(endp1+1, &endp2, 10);
|
||
// sanity check it
|
||
if(argv[i]+2==endp1 || endp1+1==endp2 || *endp1!='x' || *endp2 || w<500 || h<480 || h>w*3 || w>h*4)
|
||
{
|
||
show_usage(stderr, (char *) getfilename(argv[0]));
|
||
exit(1);
|
||
}
|
||
WINDOW_WIDTH = w;
|
||
WINDOW_HEIGHT = h;
|
||
}
|
||
else if (strcmp(argv[i], "--nooutlines") == 0)
|
||
{
|
||
dont_do_xor = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--outlines") == 0)
|
||
{
|
||
dont_do_xor = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--keyboard") == 0)
|
||
{
|
||
keymouse = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--mouse") == 0)
|
||
{
|
||
keymouse = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--nowheelmouse") == 0)
|
||
{
|
||
wheely = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--wheelmouse") == 0)
|
||
{
|
||
wheely = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--grab") == 0)
|
||
{
|
||
grab_input = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--dontgrab") == 0)
|
||
{
|
||
grab_input = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--nofancycursors") == 0)
|
||
{
|
||
no_fancy_cursors = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--fancycursors") == 0)
|
||
{
|
||
no_fancy_cursors = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--saveover") == 0)
|
||
{
|
||
promptless_save = SAVE_OVER_ALWAYS;
|
||
}
|
||
else if (strcmp(argv[i], "--saveoverask") == 0)
|
||
{
|
||
promptless_save = SAVE_OVER_PROMPT;
|
||
}
|
||
else if (strcmp(argv[i], "--saveovernew") == 0)
|
||
{
|
||
promptless_save = SAVE_OVER_NO;
|
||
}
|
||
else if (strcmp(argv[i], "--altprintnever") == 0)
|
||
{
|
||
alt_print_command_default = ALTPRINT_NEVER;
|
||
}
|
||
else if (strcmp(argv[i], "--altprintalways") == 0)
|
||
{
|
||
alt_print_command_default = ALTPRINT_ALWAYS;
|
||
}
|
||
else if (strcmp(argv[i], "--altprintmod") == 0)
|
||
{
|
||
alt_print_command_default = ALTPRINT_MOD;
|
||
}
|
||
else if (strcmp(argv[i], "--uppercase") == 0 || strcmp(argv[i], "-u") == 0)
|
||
{
|
||
only_uppercase = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--mixedcase") == 0 || strcmp(argv[i], "-m") == 0)
|
||
{
|
||
only_uppercase = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--simpleshapes") == 0 ||
|
||
strcmp(argv[i], "-s") == 0)
|
||
{
|
||
simple_shapes = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--complexshapes") == 0)
|
||
{
|
||
simple_shapes = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--noquit") == 0 || strcmp(argv[i], "-x") == 0)
|
||
{
|
||
disable_quit = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--quit") == 0)
|
||
{
|
||
disable_quit = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--nosave") == 0)
|
||
{
|
||
disable_save = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--save") == 0)
|
||
{
|
||
disable_save = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--nostamps") == 0)
|
||
{
|
||
dont_load_stamps = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--stamps") == 0)
|
||
{
|
||
dont_load_stamps = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--nosysfonts") == 0)
|
||
{
|
||
no_system_fonts = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--nobuttondistinction") == 0)
|
||
{
|
||
no_button_distinction = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--sysfonts") == 0)
|
||
{
|
||
no_system_fonts = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--noprint") == 0 || strcmp(argv[i], "-p") == 0)
|
||
{
|
||
disable_print = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--print") == 0)
|
||
{
|
||
disable_print = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--noprintcfg") == 0)
|
||
{
|
||
#ifndef WIN32
|
||
fprintf(stderr, "Note: printcfg option only applies to Windows!\n");
|
||
#endif
|
||
use_print_config = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--printcfg") == 0)
|
||
{
|
||
#ifndef WIN32
|
||
fprintf(stderr, "Note: printcfg option only applies to Windows!\n");
|
||
#endif
|
||
use_print_config = 1;
|
||
}
|
||
else if (strstr(argv[i], "--printdelay=") == argv[i])
|
||
{
|
||
sscanf(strstr(argv[i], "--printdelay=") + 13, "%d", &print_delay);
|
||
#ifdef DEBUG
|
||
printf("Print delay set to %d seconds\n", print_delay);
|
||
#endif
|
||
}
|
||
else if (strcmp(argv[i], "--nosound") == 0 || strcmp(argv[i], "-q") == 0)
|
||
{
|
||
use_sound = 0;
|
||
}
|
||
else if (strcmp(argv[i], "--sound") == 0)
|
||
{
|
||
use_sound = 1;
|
||
}
|
||
else if (strcmp(argv[i], "--locale") == 0 || strcmp(argv[i], "-L") == 0)
|
||
{
|
||
if (i < argc - 1)
|
||
{
|
||
do_locale_option(argv[++i]);
|
||
}
|
||
else
|
||
{
|
||
/* Forgot to specify the language (locale)! */
|
||
|
||
fprintf(stderr, "%s takes an argument\n", argv[i]);
|
||
show_locale_usage(stderr, (char *) getfilename(argv[0]));
|
||
exit(1);
|
||
}
|
||
}
|
||
else if (strstr(argv[i], "--lang=") == argv[i])
|
||
{
|
||
set_langstr(argv[i] + 7);
|
||
}
|
||
else if (strcmp(argv[i], "--lang") == 0 || strcmp(argv[i], "-l") == 0)
|
||
{
|
||
if (i < argc - 1)
|
||
{
|
||
set_langstr(argv[i + 1]);
|
||
i++;
|
||
}
|
||
else
|
||
{
|
||
/* Forgot to specify the language! */
|
||
|
||
fprintf(stderr, "%s takes an argument\n", argv[i]);
|
||
show_lang_usage(stderr, (char *) getfilename(argv[0]));
|
||
exit(1);
|
||
}
|
||
}
|
||
else if (strcmp(argv[i], "--savedir") == 0)
|
||
{
|
||
if (i < argc - 1)
|
||
{
|
||
if (savedir != NULL)
|
||
free(savedir);
|
||
|
||
savedir = strdup(argv[i + 1]);
|
||
remove_slash(savedir);
|
||
i++;
|
||
}
|
||
else
|
||
{
|
||
/* Forgot to specify the directory name! */
|
||
|
||
fprintf(stderr, "%s takes an argument\n", argv[i]);
|
||
show_usage(stderr, (char *) getfilename(argv[0]));
|
||
exit(1);
|
||
}
|
||
}
|
||
else if (strcmp(argv[i], "--record") == 0 ||
|
||
strcmp(argv[i], "--playback") == 0)
|
||
{
|
||
if (i < argc - 1)
|
||
{
|
||
playfile = strdup(argv[i + 1]);
|
||
|
||
if (strcmp(argv[i], "--record") == 0)
|
||
recording = 1;
|
||
else if (strcmp(argv[i], "--playback") == 0)
|
||
playing = 1;
|
||
|
||
i++;
|
||
}
|
||
else
|
||
{
|
||
/* Forgot to specify the filename! */
|
||
|
||
fprintf(stderr, "%s takes an argument\n", argv[i]);
|
||
show_usage(stderr, (char *) getfilename(argv[0]));
|
||
exit(1);
|
||
}
|
||
}
|
||
else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0)
|
||
{
|
||
show_version();
|
||
exit(0);
|
||
}
|
||
else if (strcmp(argv[i], "--copying") == 0 || strcmp(argv[i], "-c") == 0)
|
||
{
|
||
show_version();
|
||
printf(
|
||
"\n"
|
||
"This program is free software; you can redistribute it\n"
|
||
"and/or modify it under the terms of the GNU General Public\n"
|
||
"License as published by the Free Software Foundation;\n"
|
||
"either version 2 of the License, or (at your option) any\n"
|
||
"later version.\n"
|
||
"\n"
|
||
"This program is distributed in the hope that it will be\n"
|
||
"useful and entertaining, but WITHOUT ANY WARRANTY; without\n"
|
||
"even the implied warranty of MERCHANTABILITY or FITNESS\n"
|
||
"FOR A PARTICULAR PURPOSE. See the GNU General Public\n"
|
||
"License for more details.\n"
|
||
"\n"
|
||
"You should have received a copy of the GNU General Public\n"
|
||
"License along with this program; if not, write to the Free\n"
|
||
"Software Foundation, Inc., 59 Temple Place, Suite 330,\n"
|
||
"Boston, MA 02111-1307 USA\n"
|
||
"\n"
|
||
);
|
||
exit(0);
|
||
}
|
||
else if (strcmp(argv[i], "--help") == 0 ||
|
||
strcmp(argv[i], "-h") == 0)
|
||
{
|
||
show_version();
|
||
show_usage(stdout, (char *) getfilename(argv[0]));
|
||
|
||
printf(
|
||
"See: " DOC_PREFIX "README.txt\n"
|
||
"\n");
|
||
exit(0);
|
||
}
|
||
else if (strcmp(argv[i], "--usage") == 0 ||
|
||
strcmp(argv[i], "-u") == 0)
|
||
{
|
||
show_usage(stdout, (char *) getfilename(argv[0]));
|
||
exit(0);
|
||
}
|
||
else if (strcmp(argv[i], "--nosysconfig") == 0)
|
||
{
|
||
debug("Not using system config.");
|
||
}
|
||
else if (strcmp(argv[i], "--nolockfile") == 0)
|
||
{
|
||
debug("Not using lockfile");
|
||
ok_to_use_lockfile = 0;
|
||
}
|
||
else
|
||
{
|
||
show_usage(stderr, (char *) getfilename(argv[0]));
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
|
||
setup_language(getfilename(argv[0]));
|
||
|
||
|
||
#ifndef WIN32
|
||
putenv((char *) "SDL_VIDEO_X11_WMCLASS=TuxPaint.TuxPaint");
|
||
#endif
|
||
|
||
/* Test for lockfile, if we're using one: */
|
||
|
||
if (ok_to_use_lockfile)
|
||
{
|
||
char * lock_fname;
|
||
time_t time_lock, time_now;
|
||
char * homedirdir;
|
||
|
||
|
||
/* Get the current time: */
|
||
|
||
time_now = time(NULL);
|
||
|
||
|
||
/* Look for the lockfile... */
|
||
|
||
lock_fname = get_fname("lockfile.dat");
|
||
|
||
fi = fopen(lock_fname, "r");
|
||
if (fi != NULL)
|
||
{
|
||
/* If it exists, read its contents: */
|
||
|
||
if (fread(&time_lock, sizeof(time_t), 1, fi) > 0)
|
||
{
|
||
/* Has it not been 30 seconds yet? */
|
||
|
||
if (time_now < time_lock + 30)
|
||
{
|
||
/* FIXME: Wrap in gettext() */
|
||
printf("\nYou're already running a copy of Tux Paint!\n\n");
|
||
|
||
free(lock_fname);
|
||
|
||
fclose(fi);
|
||
exit(0);
|
||
}
|
||
}
|
||
|
||
fclose(fi);
|
||
}
|
||
|
||
|
||
/* Okay to run; create/update the lockfile */
|
||
|
||
/* (Make sure the directory exists, first!) */
|
||
homedirdir = get_fname("");
|
||
mkdir(homedirdir, 0755);
|
||
free(homedirdir);
|
||
|
||
|
||
fi = fopen(lock_fname, "w");
|
||
if (fi != NULL)
|
||
{
|
||
/* If we can write to it, do so! */
|
||
|
||
fwrite(&time_now, sizeof(time_t), 1, fi);
|
||
fclose(fi);
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: I couldn't create the lockfile (%s)\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n", lock_fname, strerror(errno));
|
||
}
|
||
|
||
free(lock_fname);
|
||
}
|
||
|
||
|
||
init_flags = SDL_INIT_VIDEO|SDL_INIT_TIMER;
|
||
if(use_sound)
|
||
init_flags |= SDL_INIT_AUDIO;
|
||
if(!fullscreen)
|
||
init_flags |= SDL_INIT_NOPARACHUTE; // allow debugger to catch crash
|
||
|
||
/* Init SDL */
|
||
if (SDL_Init(init_flags) < 0)
|
||
{
|
||
#ifndef NOSOUND
|
||
char *olderr = strdup(SDL_GetError());
|
||
use_sound = 0;
|
||
init_flags &= ~SDL_INIT_AUDIO;
|
||
if (SDL_Init(init_flags) >= 0)
|
||
{
|
||
// worked, w/o sound
|
||
fprintf(stderr,
|
||
"\nWarning: I could not initialize audio!\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", olderr);
|
||
free(olderr);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: I could not initialize video and/or the timer!\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
#ifndef NOSOUND
|
||
// need Mix_OpenAudio(44100, AUDIO_S16, 2, 2048) for WIN32 ?
|
||
if (use_sound && Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 1024) < 0)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: I could not set up audio for 44100 Hz "
|
||
"16-bit stereo.\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
use_sound = 0;
|
||
}
|
||
|
||
i = NUM_SOUNDS;
|
||
while(use_sound && i--)
|
||
{
|
||
sounds[i] = Mix_LoadWAV(sound_fnames[i]);
|
||
|
||
if (sounds[i] == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: I couldn't open a sound file:\n%s\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", sound_fnames[i], SDL_GetError());
|
||
use_sound = 0;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
/* Set-up Key-Repeat: */
|
||
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
|
||
SDL_DEFAULT_REPEAT_INTERVAL);
|
||
|
||
/* Init TTF stuff: */
|
||
if (TTF_Init() < 0)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: I could not initialize the font (TTF) library!\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
SDL_Quit();
|
||
exit(1);
|
||
}
|
||
|
||
|
||
|
||
setup_screen_layout();
|
||
|
||
/* Set window icon and caption: */
|
||
|
||
#ifndef __APPLE__
|
||
seticon();
|
||
#endif
|
||
SDL_WM_SetCaption("Tux Paint", "Tux Paint");
|
||
|
||
/* Open Window: */
|
||
|
||
if (fullscreen)
|
||
{
|
||
#ifdef USE_HWSURFACE
|
||
screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT,
|
||
VIDEO_BPP, SDL_FULLSCREEN | SDL_HWSURFACE);
|
||
#else
|
||
screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT,
|
||
VIDEO_BPP, SDL_FULLSCREEN | SDL_SWSURFACE);
|
||
#endif
|
||
|
||
if (screen == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: I could not open the display in fullscreen mode.\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
fullscreen = 0;
|
||
}
|
||
}
|
||
|
||
|
||
if (!fullscreen)
|
||
{
|
||
#ifdef USE_HWSURFACE
|
||
screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT,
|
||
VIDEO_BPP, SDL_HWSURFACE);
|
||
#else
|
||
screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT,
|
||
VIDEO_BPP, SDL_SWSURFACE);
|
||
#endif
|
||
}
|
||
|
||
if (screen == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: I could not open the display.\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
|
||
////////// quickly: title image, version, progress bar, and watch cursor
|
||
|
||
img_title = loadimage(DATA_PREFIX "images/title.png");
|
||
img_title_tuxpaint = loadimage(DATA_PREFIX "images/title-tuxpaint.png");
|
||
img_title_credits = loadimage(DATA_PREFIX "images/title-credits.png");
|
||
img_progress = loadimage(DATA_PREFIX "images/ui/progress.png");
|
||
|
||
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
|
||
|
||
dest.x = ((WINDOW_WIDTH - img_title->w - (img_title_tuxpaint->w / 2)) / 2) + (img_title_tuxpaint->w / 2) + 20;
|
||
dest.y = (WINDOW_HEIGHT - img_title->h);
|
||
|
||
SDL_BlitSurface(img_title, NULL, screen, &dest);
|
||
|
||
dest.x = 10;
|
||
dest.y = (WINDOW_HEIGHT - img_title->h) + img_title_tuxpaint->h * 0.8 + 7;
|
||
|
||
SDL_BlitSurface(img_title_tuxpaint, NULL, screen, &dest);
|
||
|
||
dest.x = 10;
|
||
dest.y = 5;
|
||
|
||
SDL_BlitSurface(img_title_credits, NULL, screen, &dest);
|
||
|
||
prog_bar_ctr = 0;
|
||
show_progress_bar();
|
||
|
||
SDL_Flip(screen);
|
||
|
||
medium_font = TTF_OpenFont(DATA_PREFIX "fonts/default_font.ttf",
|
||
18 - (only_uppercase * 3));
|
||
|
||
if (medium_font == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't load font file: "
|
||
DATA_PREFIX "fonts/default_font.ttf\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
snprintf(tmp_str, sizeof(tmp_str), "Version: %s – %s", VER_VERSION, VER_DATE);
|
||
tmp_surf = render_text(medium_font, tmp_str, black);
|
||
dest.x = 10;
|
||
dest.y = WINDOW_HEIGHT - img_progress->h - tmp_surf->h;
|
||
SDL_BlitSurface(tmp_surf, NULL, screen, &dest);
|
||
SDL_FreeSurface(tmp_surf);
|
||
printf("%s\n", tmp_str);
|
||
|
||
snprintf(tmp_str, sizeof(tmp_str),
|
||
"© 2002–2005 Bill Kendrick et al.");
|
||
tmp_surf = render_text(medium_font, tmp_str, black);
|
||
dest.x = 10;
|
||
dest.y = WINDOW_HEIGHT - img_progress->h - (tmp_surf->h * 2);
|
||
SDL_BlitSurface(tmp_surf, NULL, screen, &dest);
|
||
SDL_FreeSurface(tmp_surf);
|
||
|
||
SDL_Flip(screen);
|
||
|
||
|
||
#if defined(WIN32) && defined(LARGE_CURSOR_FULLSCREEN_BUG)
|
||
if (fullscreen && no_fancy_cursors == 0)
|
||
{
|
||
fprintf(stderr, "Warning: An SDL bug causes the fancy cursors to leave\n"
|
||
"trails in fullscreen mode. Disabling fancy cursors.\n"
|
||
"(You can do this yourself with 'nofancycursors' option,\n"
|
||
"to avoid this warning in the future.)\n");
|
||
no_fancy_cursors = 1;
|
||
}
|
||
#endif
|
||
|
||
|
||
/* Create cursors: */
|
||
|
||
scale = 1;
|
||
|
||
#ifdef SMALL_CURSOR_SHAPES
|
||
scale = 2;
|
||
#endif
|
||
|
||
// this one first, because we need it yesterday
|
||
cursor_watch = get_cursor(watch_bits, watch_mask_bits,
|
||
watch_width, watch_height,
|
||
14 / scale, 14 / scale);
|
||
|
||
do_setcursor(cursor_watch);
|
||
show_progress_bar();
|
||
|
||
#ifdef FORKED_FONTS
|
||
reliable_write(font_socket_fd, &no_system_fonts, sizeof no_system_fonts);
|
||
#else
|
||
font_thread = SDL_CreateThread(load_user_fonts, NULL);
|
||
#endif
|
||
|
||
// continuing on with the rest of the cursors...
|
||
|
||
cursor_hand = get_cursor(hand_bits, hand_mask_bits,
|
||
hand_width, hand_height,
|
||
12 / scale, 1 / scale);
|
||
|
||
cursor_wand = get_cursor(wand_bits, wand_mask_bits,
|
||
wand_width, wand_height,
|
||
4 / scale, 4 / scale);
|
||
|
||
cursor_insertion = get_cursor(insertion_bits, insertion_mask_bits,
|
||
insertion_width, insertion_height,
|
||
7 / scale, 4 / scale);
|
||
|
||
cursor_brush = get_cursor(brush_bits, brush_mask_bits,
|
||
brush_width, brush_height,
|
||
4 / scale, 28 / scale);
|
||
|
||
cursor_crosshair = get_cursor(crosshair_bits, crosshair_mask_bits,
|
||
crosshair_width, crosshair_height,
|
||
15 / scale, 15 / scale);
|
||
|
||
cursor_rotate = get_cursor(rotate_bits, rotate_mask_bits,
|
||
rotate_width, rotate_height,
|
||
15 / scale, 15 / scale);
|
||
|
||
cursor_arrow = get_cursor(arrow_bits, arrow_mask_bits,
|
||
arrow_width, arrow_height,
|
||
0, 0);
|
||
|
||
cursor_up = get_cursor(up_bits, up_mask_bits,
|
||
up_width, up_height,
|
||
15 / scale, 1 / scale);
|
||
|
||
cursor_down = get_cursor(down_bits, down_mask_bits,
|
||
down_width, down_height,
|
||
15 / scale, 30 / scale);
|
||
|
||
cursor_tiny = get_cursor(tiny_bits, tiny_mask_bits,
|
||
tiny_width, tiny_height,
|
||
3, 3); /* Exactly the same in SMALL (16x16) size! */
|
||
|
||
|
||
|
||
/* Create drawing canvas: */
|
||
|
||
canvas = SDL_CreateRGBSurface(screen->flags,
|
||
WINDOW_WIDTH - (96 * 2),
|
||
(48 * 7) + 40 + HEIGHTOFFSET,
|
||
screen->format->BitsPerPixel,
|
||
screen->format->Rmask,
|
||
screen->format->Gmask,
|
||
screen->format->Bmask,
|
||
0);
|
||
|
||
img_starter = NULL;
|
||
img_starter_bkgd = NULL;
|
||
starter_mirrored = 0;
|
||
starter_flipped = 0;
|
||
|
||
if (canvas == NULL)
|
||
{
|
||
fprintf(stderr, "\nError: Can't build drawing canvas!\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
|
||
|
||
|
||
/* Create undo buffer space: */
|
||
|
||
for (i = 0; i < NUM_UNDO_BUFS; i++)
|
||
{
|
||
undo_bufs[i] = SDL_CreateRGBSurface(screen->flags,
|
||
WINDOW_WIDTH - (96 * 2),
|
||
(48 * 7) + 40 + HEIGHTOFFSET,
|
||
screen->format->BitsPerPixel,
|
||
screen->format->Rmask,
|
||
screen->format->Gmask,
|
||
screen->format->Bmask,
|
||
0);
|
||
|
||
|
||
if (undo_bufs[i] == NULL)
|
||
{
|
||
fprintf(stderr, "\nError: Can't build undo buffer! (%d of %d)\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", i + 1, NUM_UNDO_BUFS, SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
undo_starters[i] = UNDO_STARTER_NONE;
|
||
}
|
||
|
||
|
||
|
||
/* Load other images: */
|
||
|
||
for (i = 0; i < NUM_TOOLS; i++)
|
||
img_tools[i] = loadimage(tool_img_fnames[i]);
|
||
|
||
img_title_on = loadimage(DATA_PREFIX "images/ui/title.png");
|
||
img_title_large_on = loadimage(DATA_PREFIX "images/ui/title_large.png");
|
||
img_title_off = loadimage(DATA_PREFIX "images/ui/no_title.png");
|
||
img_title_large_off = loadimage(DATA_PREFIX "images/ui/no_title_large.png");
|
||
|
||
img_btn_up = loadimage(DATA_PREFIX "images/ui/btn_up.png");
|
||
img_btn_down = loadimage(DATA_PREFIX "images/ui/btn_down.png");
|
||
img_btn_off = loadimage(DATA_PREFIX "images/ui/btn_off.png");
|
||
|
||
img_black = SDL_CreateRGBSurface(SDL_SRCALPHA|SDL_SWSURFACE,
|
||
img_btn_off->w, img_btn_off->h,
|
||
img_btn_off->format->BitsPerPixel,
|
||
img_btn_off->format->Rmask,
|
||
img_btn_off->format->Gmask,
|
||
img_btn_off->format->Bmask,
|
||
img_btn_off->format->Amask);
|
||
SDL_FillRect(img_black, NULL, SDL_MapRGBA(screen->format, 0, 0, 0, 255));
|
||
|
||
img_grey = SDL_CreateRGBSurface(SDL_SRCALPHA|SDL_SWSURFACE,
|
||
img_btn_off->w, img_btn_off->h,
|
||
img_btn_off->format->BitsPerPixel,
|
||
img_btn_off->format->Rmask,
|
||
img_btn_off->format->Gmask,
|
||
img_btn_off->format->Bmask,
|
||
img_btn_off->format->Amask);
|
||
SDL_FillRect(img_grey, NULL, SDL_MapRGBA(screen->format, 0x88, 0x88, 0x88, 255));
|
||
|
||
show_progress_bar();
|
||
|
||
img_yes = loadimage(DATA_PREFIX "images/ui/yes.png");
|
||
img_no = loadimage(DATA_PREFIX "images/ui/no.png");
|
||
|
||
img_open = loadimage(DATA_PREFIX "images/ui/open.png");
|
||
img_erase = loadimage(DATA_PREFIX "images/ui/erase.png");
|
||
img_back = loadimage(DATA_PREFIX "images/ui/back.png");
|
||
img_trash = loadimage(DATA_PREFIX "images/ui/trash.png");
|
||
|
||
img_popup_arrow = loadimage(DATA_PREFIX "images/ui/popup_arrow.png");
|
||
|
||
img_dead40x40 = loadimage(DATA_PREFIX "images/ui/dead40x40.png");
|
||
|
||
img_printer = loadimage(DATA_PREFIX "images/ui/printer.png");
|
||
img_printer_wait = loadimage(DATA_PREFIX "images/ui/printer_wait.png");
|
||
|
||
img_save_over = loadimage(DATA_PREFIX "images/ui/save_over.png");
|
||
|
||
img_grow = loadimage(DATA_PREFIX "images/ui/grow.png");
|
||
img_shrink = loadimage(DATA_PREFIX "images/ui/shrink.png");
|
||
|
||
img_bold = loadimage(DATA_PREFIX "images/ui/bold.png");
|
||
img_italic = loadimage(DATA_PREFIX "images/ui/italic.png");
|
||
|
||
show_progress_bar();
|
||
|
||
tmp_imgcurup = loadimage(DATA_PREFIX "images/ui/cursor_up_large.png");
|
||
tmp_imgcurdown = loadimage(DATA_PREFIX "images/ui/cursor_down_large.png");
|
||
img_cursor_up = thumbnail(tmp_imgcurup, THUMB_W, THUMB_H, 0);
|
||
img_cursor_down = thumbnail(tmp_imgcurdown, THUMB_W, THUMB_H, 0);
|
||
|
||
tmp_imgcurup = loadimage(DATA_PREFIX "images/ui/cursor_starter_up.png");
|
||
tmp_imgcurdown = loadimage(DATA_PREFIX "images/ui/cursor_starter_down.png");
|
||
img_cursor_starter_up = thumbnail(tmp_imgcurup, THUMB_W, THUMB_H, 0);
|
||
img_cursor_starter_down = thumbnail(tmp_imgcurdown, THUMB_W, THUMB_H, 0);
|
||
SDL_FreeSurface(tmp_imgcurup);
|
||
SDL_FreeSurface(tmp_imgcurdown);
|
||
|
||
show_progress_bar();
|
||
|
||
img_scroll_up = loadimage(DATA_PREFIX "images/ui/scroll_up.png");
|
||
img_scroll_down = loadimage(DATA_PREFIX "images/ui/scroll_down.png");
|
||
|
||
img_scroll_up_off = loadimage(DATA_PREFIX "images/ui/scroll_up_off.png");
|
||
img_scroll_down_off = loadimage(DATA_PREFIX "images/ui/scroll_down_off.png");
|
||
|
||
#ifdef LOW_QUALITY_COLOR_SELECTOR
|
||
img_paintcan = loadimage(DATA_PREFIX "images/ui/paintcan.png");
|
||
#endif
|
||
|
||
show_progress_bar();
|
||
|
||
img_sparkles = loadimage(DATA_PREFIX "images/ui/sparkles.png");
|
||
img_grass = loadimage(DATA_PREFIX "images/ui/grass.png");
|
||
|
||
|
||
/* Load brushes: */
|
||
load_brush_dir(DATA_PREFIX "brushes");
|
||
homedirdir = get_fname("brushes");
|
||
load_brush_dir(homedirdir);
|
||
#ifdef WIN32
|
||
free(homedirdir);
|
||
homedirdir = get_fname("data/brushes");
|
||
load_brush_dir(homedirdir);
|
||
#endif
|
||
|
||
if (num_brushes == 0)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: No brushes found in " DATA_PREFIX "brushes/\n"
|
||
"or %s\n\n", homedirdir);
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
free(homedirdir);
|
||
|
||
|
||
/* Load system fonts: */
|
||
|
||
large_font = TTF_OpenFont(DATA_PREFIX "fonts/default_font.ttf",
|
||
36 - (only_uppercase * 3));
|
||
|
||
if (large_font == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't load font file: "
|
||
DATA_PREFIX "fonts/default_font.ttf\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
|
||
small_font = TTF_OpenFont(DATA_PREFIX "fonts/default_font.ttf",
|
||
14 - (only_uppercase * 2));
|
||
|
||
if (small_font == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't load font file: "
|
||
DATA_PREFIX "fonts/default_font.ttf\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
|
||
locale_font = load_locale_font(medium_font,18);
|
||
|
||
#if 0
|
||
// put elsewhere for THREADED_FONTS
|
||
/* Load user fonts, for the text tool */
|
||
load_user_fonts();
|
||
#endif
|
||
|
||
if (!dont_load_stamps)
|
||
load_stamps();
|
||
|
||
|
||
/* Load magic icons: */
|
||
for (i = 0; i < NUM_MAGICS; i++)
|
||
img_magics[i] = loadimage(magic_img_fnames[i]);
|
||
|
||
show_progress_bar();
|
||
|
||
/* Load shape icons: */
|
||
for (i = 0; i < NUM_SHAPES; i++)
|
||
img_shapes[i] = loadimage(shape_img_fnames[i]);
|
||
|
||
show_progress_bar();
|
||
|
||
/* Load tip tux images: */
|
||
for (i = 0; i < NUM_TIP_TUX; i++)
|
||
img_tux[i] = loadimage(tux_img_fnames[i]);
|
||
|
||
show_progress_bar();
|
||
|
||
img_mouse = loadimage(DATA_PREFIX "images/ui/mouse.png");
|
||
img_mouse_click = loadimage(DATA_PREFIX "images/ui/mouse_click.png");
|
||
|
||
show_progress_bar();
|
||
|
||
/* Create toolbox and selector labels: */
|
||
|
||
for (i = 0; i < NUM_TITLES; i++)
|
||
{
|
||
if (strlen(title_names[i]) > 0)
|
||
{
|
||
TTF_Font * myfont = large_font;
|
||
char *td_str = textdir(gettext(title_names[i]));
|
||
|
||
if (need_own_font && strcmp(gettext(title_names[i]), title_names[i]))
|
||
myfont = locale_font;
|
||
upstr = uppercase(td_str);
|
||
free(td_str);
|
||
tmp_surf = render_text(myfont, upstr, black);
|
||
free(upstr);
|
||
img_title_names[i] = thumbnail(tmp_surf, min(84, tmp_surf->w), tmp_surf->h, 0);
|
||
SDL_FreeSurface(tmp_surf);
|
||
}
|
||
else
|
||
{
|
||
img_title_names[i] = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/* Generate color selection buttons: */
|
||
|
||
#ifndef LOW_QUALITY_COLOR_SELECTOR
|
||
|
||
/* Create appropriately-shaped buttons: */
|
||
img1 = loadimage(DATA_PREFIX "images/ui/paintwell.png");
|
||
img2 = thumbnail(img1, color_button_w, color_button_h, 0);
|
||
tmp_btn_up = thumbnail(img_btn_up, color_button_w, color_button_h, 0);
|
||
tmp_btn_down = thumbnail(img_btn_down,color_button_w, color_button_h, 0);
|
||
img_color_btn_off = thumbnail(img_btn_off, color_button_w, color_button_h, 0);
|
||
SDL_FreeSurface(img1);
|
||
|
||
/* Create surfaces to draw them into: */
|
||
|
||
for (i = 0; i < NUM_COLORS*2; i++)
|
||
{
|
||
|
||
img_color_btns[i] = SDL_CreateRGBSurface(screen->flags,
|
||
/* (WINDOW_WIDTH - 96) / NUM_COLORS, 48, */
|
||
tmp_btn_up->w, tmp_btn_up->h,
|
||
screen->format->BitsPerPixel,
|
||
screen->format->Rmask,
|
||
screen->format->Gmask,
|
||
screen->format->Bmask,
|
||
0);
|
||
|
||
if (img_color_btns[i] == NULL)
|
||
{
|
||
fprintf(stderr, "\nError: Can't build color button!\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
SDL_LockSurface(img_color_btns[i]);
|
||
}
|
||
|
||
|
||
/* Generate the buttons based on the thumbnails: */
|
||
|
||
SDL_LockSurface(tmp_btn_down);
|
||
SDL_LockSurface(tmp_btn_up);
|
||
|
||
getpixel_tmp_btn_up = getpixels[tmp_btn_up->format->BytesPerPixel];
|
||
getpixel_tmp_btn_down = getpixels[tmp_btn_down->format->BytesPerPixel];
|
||
getpixel_img2 = getpixels[img2->format->BytesPerPixel];
|
||
|
||
|
||
for (y = 0; y < tmp_btn_up->h /* 48 */; y++)
|
||
{
|
||
for (x = 0; x < tmp_btn_up->w /* (WINDOW_WIDTH - 96) / NUM_COLORS */; x++)
|
||
{
|
||
double ru, gu, bu, rd, gd, bd, aa;
|
||
Uint8 a;
|
||
|
||
SDL_GetRGB(getpixel_tmp_btn_up(tmp_btn_up, x, y), tmp_btn_up->format, &r, &g, &b);
|
||
|
||
ru = sRGB_to_linear_table[r];
|
||
gu = sRGB_to_linear_table[g];
|
||
bu = sRGB_to_linear_table[b];
|
||
SDL_GetRGB(getpixel_tmp_btn_down(tmp_btn_down, x, y), tmp_btn_down->format, &r, &g, &b);
|
||
rd = sRGB_to_linear_table[r];
|
||
gd = sRGB_to_linear_table[g];
|
||
bd = sRGB_to_linear_table[b];
|
||
SDL_GetRGBA(getpixel_img2(img2, x, y), img2->format, &r, &g, &b, &a);
|
||
aa = a/255.0;
|
||
|
||
for (i = 0; i < NUM_COLORS; i++)
|
||
{
|
||
double rh = sRGB_to_linear_table[color_hexes[i][0]];
|
||
double gh = sRGB_to_linear_table[color_hexes[i][1]];
|
||
double bh = sRGB_to_linear_table[color_hexes[i][2]];
|
||
putpixels[img_color_btns[i]->format->BytesPerPixel]
|
||
(img_color_btns[i], x, y,
|
||
SDL_MapRGB(img_color_btns[i]->format,
|
||
linear_to_sRGB(rh*aa + ru*(1.0-aa)),
|
||
linear_to_sRGB(gh*aa + gu*(1.0-aa)),
|
||
linear_to_sRGB(bh*aa + bu*(1.0-aa))));
|
||
putpixels[img_color_btns[i]->format->BytesPerPixel]
|
||
(img_color_btns[i+NUM_COLORS], x, y,
|
||
SDL_MapRGB(img_color_btns[i+NUM_COLORS]->format,
|
||
linear_to_sRGB(rh*aa + rd*(1.0-aa)),
|
||
linear_to_sRGB(gh*aa + gd*(1.0-aa)),
|
||
linear_to_sRGB(bh*aa + bd*(1.0-aa))));
|
||
}
|
||
}
|
||
}
|
||
|
||
for (i = 0; i < NUM_COLORS*2; i++)
|
||
SDL_UnlockSurface(img_color_btns[i]);
|
||
|
||
SDL_UnlockSurface(tmp_btn_up);
|
||
SDL_UnlockSurface(tmp_btn_down);
|
||
SDL_FreeSurface(tmp_btn_up);
|
||
SDL_FreeSurface(tmp_btn_down);
|
||
SDL_FreeSurface(img2);
|
||
|
||
#endif
|
||
|
||
create_button_labels();
|
||
|
||
|
||
/* Seed random-number generator: */
|
||
|
||
srand(SDL_GetTicks());
|
||
|
||
|
||
/* Enable Unicode support in SDL: */
|
||
|
||
SDL_EnableUNICODE(1);
|
||
|
||
#ifndef WIN32
|
||
/* Set up signal handler for SIGPIPE (in case printer command dies;
|
||
e.g., altprintcommand=kprinter, but 'Cancel' is clicked,
|
||
instead of 'Ok') */
|
||
|
||
signal(SIGPIPE, signal_handler);
|
||
#endif
|
||
|
||
/* Open demo recording or playback file: */
|
||
|
||
if (recording)
|
||
{
|
||
demofi = fopen(playfile, "w");
|
||
|
||
if (demofi == NULL)
|
||
{
|
||
fprintf(stderr, "Error: Cannot create recording file: %s\n"
|
||
"%s\n\n",
|
||
playfile, strerror(errno));
|
||
exit(1);
|
||
}
|
||
}
|
||
else if (playing)
|
||
{
|
||
demofi = fopen(playfile, "r");
|
||
|
||
if (demofi == NULL)
|
||
{
|
||
fprintf(stderr, "Error: Cannot open playback file: %s\n"
|
||
"%s\n\n",
|
||
playfile, strerror(errno));
|
||
exit(1);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
demofi = NULL;
|
||
}
|
||
}
|
||
|
||
#ifndef WIN32
|
||
void signal_handler(int sig)
|
||
{
|
||
sig = sig;
|
||
/*
|
||
if (sig == SIGPIPE)
|
||
fprintf(stderr, "SIGPIPE!\n");
|
||
*/
|
||
}
|
||
#endif
|
||
|
||
/* Render a button label using the appropriate string/font: */
|
||
static SDL_Surface * do_render_button_label(const char * const label)
|
||
{
|
||
SDL_Surface * tmp_surf, * surf;
|
||
SDL_Color black = {0, 0, 0, 0};
|
||
TTF_Font * myfont = small_font;
|
||
char *td_str = textdir(gettext(label));
|
||
char *upstr = uppercase(td_str);
|
||
|
||
free(td_str);
|
||
|
||
if (need_own_font && strcmp(gettext(label), label))
|
||
myfont = locale_font;
|
||
tmp_surf = render_text(myfont, upstr, black);
|
||
free(upstr);
|
||
surf = thumbnail(tmp_surf, min(48, tmp_surf->w), tmp_surf->h, 0);
|
||
SDL_FreeSurface(tmp_surf);
|
||
|
||
return surf;
|
||
}
|
||
|
||
static void create_button_labels(void)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < NUM_TOOLS; i++)
|
||
img_tool_names[i] = do_render_button_label(tool_names[i]);
|
||
|
||
for (i = 0; i < NUM_MAGICS; i++)
|
||
img_magic_names[i] = do_render_button_label(magic_names[i]);
|
||
|
||
for (i = 0; i < NUM_SHAPES; i++)
|
||
img_shape_names[i] = do_render_button_label(shape_names[i]);
|
||
|
||
// buttons for the file open dialog
|
||
img_openlabels_open = do_render_button_label(gettext_noop("Open"));
|
||
img_openlabels_erase = do_render_button_label(gettext_noop("Erase"));
|
||
img_openlabels_back = do_render_button_label(gettext_noop("Back"));
|
||
}
|
||
|
||
|
||
static void seticon(void)
|
||
{
|
||
int masklen;
|
||
Uint8 * mask;
|
||
SDL_Surface * icon;
|
||
|
||
/* Load icon into a surface: */
|
||
|
||
#ifndef WIN32
|
||
icon = IMG_Load(DATA_PREFIX "images/icon.png");
|
||
#else
|
||
icon = IMG_Load(DATA_PREFIX "images/icon32x32.png");
|
||
#endif
|
||
|
||
if (icon == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: I could not load the icon image: %s\n"
|
||
"The Simple DirectMedia error that occurred was:\n"
|
||
"%s\n\n", DATA_PREFIX "images/icon.png", SDL_GetError());
|
||
return;
|
||
}
|
||
|
||
|
||
#ifndef WIN32
|
||
/* Create mask: */
|
||
masklen = (((icon -> w) + 7) / 8) * (icon -> h);
|
||
mask = malloc(masklen * sizeof(Uint8));
|
||
memset(mask, 0xFF, masklen);
|
||
|
||
/* Set icon: */
|
||
SDL_WM_SetIcon(icon, mask);
|
||
|
||
/* Free icon surface & mask: */
|
||
free(mask);
|
||
#else
|
||
/* Set icon: */
|
||
SDL_WM_SetIcon(icon, NULL);
|
||
#endif
|
||
SDL_FreeSurface(icon);
|
||
|
||
|
||
/* Grab keyboard and mouse, if requested: */
|
||
|
||
if (grab_input)
|
||
{
|
||
debug("Grabbing input!");
|
||
SDL_WM_GrabInput(SDL_GRAB_ON);
|
||
}
|
||
}
|
||
|
||
|
||
/* Load a mouse pointer (cursor) shape: */
|
||
|
||
static SDL_Cursor * get_cursor(unsigned char * bits, unsigned char * mask_bits,
|
||
unsigned int width, unsigned int height,
|
||
unsigned int x, unsigned int y)
|
||
{
|
||
Uint8 b;
|
||
Uint8 temp_bitmap[128], temp_bitmask[128];
|
||
unsigned int i;
|
||
|
||
|
||
if (((width + 7) / 8) * height > 128)
|
||
{
|
||
fprintf(stderr, "Error: Cursor is too large!\n");
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
for (i = 0; i < ((width + 7) / 8) * height; i++)
|
||
{
|
||
b = bits[i];
|
||
|
||
temp_bitmap[i] = (((b & 0x01) << 7) |
|
||
((b & 0x02) << 5) |
|
||
((b & 0x04) << 3) |
|
||
((b & 0x08) << 1) |
|
||
((b & 0x10) >> 1) |
|
||
((b & 0x20) >> 3) |
|
||
((b & 0x40) >> 5) |
|
||
((b & 0x80) >> 7));
|
||
|
||
b = mask_bits[i];
|
||
|
||
temp_bitmask[i] = (((b & 0x01) << 7) |
|
||
((b & 0x02) << 5) |
|
||
((b & 0x04) << 3) |
|
||
((b & 0x08) << 1) |
|
||
((b & 0x10) >> 1) |
|
||
((b & 0x20) >> 3) |
|
||
((b & 0x40) >> 5) |
|
||
((b & 0x80) >> 7));
|
||
}
|
||
|
||
return(SDL_CreateCursor(temp_bitmap, temp_bitmask, width, height, x, y));
|
||
}
|
||
|
||
|
||
/* Load an image (with errors): */
|
||
|
||
static SDL_Surface * loadimage(const char * const fname)
|
||
{
|
||
return(do_loadimage(fname, 1));
|
||
}
|
||
|
||
|
||
/* Load an image: */
|
||
|
||
static SDL_Surface * do_loadimage(const char * const fname, int abort_on_error)
|
||
{
|
||
SDL_Surface * s, * disp_fmt_s;
|
||
|
||
|
||
/* Load the image file: */
|
||
|
||
s = IMG_Load(fname);
|
||
if (s == NULL)
|
||
{
|
||
if (abort_on_error)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: I couldn't load a graphics file:\n"
|
||
"%s\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", fname, SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
else
|
||
{
|
||
return(NULL);
|
||
}
|
||
}
|
||
|
||
|
||
/* Convert to the display format: */
|
||
|
||
disp_fmt_s = SDL_DisplayFormatAlpha(s);
|
||
if (disp_fmt_s == NULL)
|
||
{
|
||
if (abort_on_error)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: I couldn't convert a graphics file:\n"
|
||
"%s\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", fname, SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
else
|
||
{
|
||
SDL_FreeSurface(s);
|
||
return(NULL);
|
||
}
|
||
}
|
||
|
||
|
||
/* Free the temp. surface & return the converted one: */
|
||
|
||
SDL_FreeSurface(s);
|
||
|
||
return(disp_fmt_s);
|
||
}
|
||
|
||
|
||
/* Draw the toolbar: */
|
||
|
||
static void draw_toolbar(void)
|
||
{
|
||
int i;
|
||
SDL_Rect dest;
|
||
|
||
|
||
/* FIXME: Only allow print if we have something to print! */
|
||
|
||
|
||
draw_image_title(TITLE_TOOLS, r_ttools);
|
||
|
||
for (i = 0; i < NUM_TOOLS + TOOLOFFSET; i++)
|
||
{
|
||
dest.x = ((i % 2) * 48);
|
||
dest.y = ((i / 2) * 48) + 40;
|
||
|
||
|
||
if (i < NUM_TOOLS)
|
||
{
|
||
SDL_Surface *button_color;
|
||
SDL_Surface *button_body;
|
||
if (i == cur_tool)
|
||
{
|
||
button_body = img_btn_down;
|
||
button_color = img_black;
|
||
}
|
||
else if (tool_avail[i])
|
||
{
|
||
button_body = img_btn_up;
|
||
button_color = img_black;
|
||
}
|
||
else
|
||
{
|
||
button_body = img_btn_off;
|
||
button_color = img_grey;
|
||
}
|
||
SDL_BlitSurface(button_body, NULL, screen, &dest);
|
||
SDL_BlitSurface(button_color, NULL, img_tools[i], NULL);
|
||
SDL_BlitSurface(button_color, NULL, img_tool_names[i], NULL);
|
||
|
||
dest.x = ((i % 2) * 48) + 4;
|
||
dest.y = ((i / 2) * 48) + 40 + 4;
|
||
|
||
SDL_BlitSurface(img_tools[i], NULL, screen, &dest);
|
||
|
||
|
||
dest.x = ((i % 2) * 48) + 4 + (40 - img_tool_names[i]->w) / 2;
|
||
dest.y = ((i / 2) * 48) + 40 + 4 + (44 - img_tool_names[i]->h);
|
||
|
||
SDL_BlitSurface(img_tool_names[i], NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw magic controls: */
|
||
|
||
static void draw_magic(void)
|
||
{
|
||
int magic, i, max, off_y;
|
||
SDL_Rect dest;
|
||
|
||
// restore these to black (stamp and text controls borrow them)
|
||
SDL_BlitSurface(img_black, NULL, img_magics[MAGIC_FLIP], NULL);
|
||
SDL_BlitSurface(img_black, NULL, img_magics[MAGIC_MIRROR], NULL);
|
||
|
||
/* FIXME: Should we worry about more than 14 magic effects? :^/ */
|
||
|
||
|
||
draw_image_title(TITLE_MAGIC, r_ttoolopt);
|
||
|
||
if (NUM_MAGICS > 14 + TOOLOFFSET)
|
||
{
|
||
off_y = 24;
|
||
max = 12 + TOOLOFFSET;
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40;
|
||
|
||
if (magic_scroll > 0)
|
||
{
|
||
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
||
}
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40 + 24 + ((6 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (magic_scroll < NUM_MAGICS - 12 - TOOLOFFSET)
|
||
{
|
||
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
off_y = 0;
|
||
max = 14 + TOOLOFFSET;
|
||
}
|
||
|
||
|
||
for (magic = magic_scroll;
|
||
magic < magic_scroll + max;
|
||
magic++)
|
||
{
|
||
i = magic - magic_scroll;
|
||
|
||
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
||
dest.y = ((i / 2) * 48) + 40 + off_y;
|
||
|
||
if (magic < NUM_MAGICS)
|
||
{
|
||
if (magic == cur_magic)
|
||
{
|
||
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
||
}
|
||
|
||
dest.x = WINDOW_WIDTH - 96 + ((i % 2) * 48) + 4;
|
||
dest.y = ((i / 2) * 48) + 40 + 4 + off_y;
|
||
|
||
SDL_BlitSurface(img_magics[magic], NULL, screen, &dest);
|
||
|
||
|
||
dest.x = WINDOW_WIDTH - 96 + ((i % 2) * 48) + 4 +
|
||
(40 - img_magic_names[magic]->w) / 2;
|
||
dest.y = (((i / 2) * 48) + 40 + 4 +
|
||
(44 - img_magic_names[magic]->h) + off_y);
|
||
|
||
SDL_BlitSurface(img_magic_names[magic], NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw color selector: */
|
||
|
||
static unsigned colors_state = COLORSEL_ENABLE | COLORSEL_CLOBBER;
|
||
|
||
static unsigned draw_colors(unsigned action)
|
||
{
|
||
unsigned i;
|
||
SDL_Rect dest;
|
||
static unsigned old_color;
|
||
|
||
unsigned old_colors_state = colors_state;
|
||
if (action==COLORSEL_CLOBBER)
|
||
colors_state |= COLORSEL_CLOBBER;
|
||
if (action==COLORSEL_REFRESH)
|
||
colors_state &= ~COLORSEL_CLOBBER;
|
||
if (action==COLORSEL_DISABLE)
|
||
colors_state = COLORSEL_DISABLE;
|
||
if (action==COLORSEL_ENABLE)
|
||
colors_state = COLORSEL_ENABLE;
|
||
|
||
colors_are_selectable = colors_state == COLORSEL_ENABLE;
|
||
|
||
if (colors_state&COLORSEL_CLOBBER)
|
||
return old_colors_state;
|
||
if (cur_color==old_color && colors_state==old_colors_state)
|
||
return old_colors_state;
|
||
old_color = cur_color;
|
||
|
||
for (i = 0; i < NUM_COLORS; i++)
|
||
{
|
||
dest.x = r_colors.x + i%gd_colors.cols*color_button_w;
|
||
dest.y = r_colors.y + i/gd_colors.cols*color_button_h;
|
||
#ifndef LOW_QUALITY_COLOR_SELECTOR
|
||
SDL_BlitSurface(
|
||
(colors_state == COLORSEL_ENABLE)
|
||
? img_color_btns[i + (i==cur_color)*NUM_COLORS]
|
||
: img_color_btn_off,
|
||
NULL,
|
||
screen,
|
||
&dest
|
||
);
|
||
#else
|
||
dest.w = color_button_w;
|
||
dest.h = color_button_h;
|
||
|
||
if (colors_state == COLORSEL_ENABLE)
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(screen->format,
|
||
color_hexes[i][0],
|
||
color_hexes[i][1],
|
||
color_hexes[i][2]));
|
||
else
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(screen->format, 240, 240, 240));
|
||
if (i==cur_color && colors_state==COLORSEL_ENABLE)
|
||
{
|
||
dest.y += 4;
|
||
SDL_BlitSurface(img_paintcan, NULL, screen, &dest);
|
||
}
|
||
#endif
|
||
|
||
}
|
||
update_screen_rect(&r_colors);
|
||
|
||
// if only the color changed, no need to draw the title
|
||
if (colors_state==old_colors_state)
|
||
return old_colors_state;
|
||
|
||
if (colors_state == COLORSEL_ENABLE)
|
||
{
|
||
SDL_BlitSurface(img_title_large_on, NULL, screen, &r_tcolors);
|
||
|
||
dest.x = r_tcolors.x + (r_tcolors.w - img_title_names[TITLE_COLORS]->w) / 2;
|
||
dest.y = r_tcolors.y + (r_tcolors.h - img_title_names[TITLE_COLORS]->h) / 2;
|
||
SDL_BlitSurface(img_title_names[TITLE_COLORS], NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_title_large_off, NULL, screen, &r_tcolors);
|
||
}
|
||
|
||
update_screen_rect(&r_tcolors);
|
||
return old_colors_state;
|
||
}
|
||
|
||
|
||
/* Draw brushes: */
|
||
|
||
static void draw_brushes(void)
|
||
{
|
||
int i, off_y, max, brush;
|
||
SDL_Rect dest;
|
||
|
||
|
||
/* Draw the title: */
|
||
draw_image_title(TITLE_BRUSHES, r_ttoolopt);
|
||
|
||
|
||
/* Do we need scrollbars? */
|
||
|
||
if (num_brushes > 14 + TOOLOFFSET)
|
||
{
|
||
off_y = 24;
|
||
max = 12 + TOOLOFFSET;
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40;
|
||
|
||
if (brush_scroll > 0)
|
||
{
|
||
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
||
}
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40 + 24 + ((6 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (brush_scroll < num_brushes - 12 - TOOLOFFSET)
|
||
{
|
||
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
off_y = 0;
|
||
max = 14 + TOOLOFFSET;
|
||
}
|
||
|
||
|
||
/* Draw each of the shown brushes: */
|
||
|
||
for (brush = brush_scroll;
|
||
brush < brush_scroll + max;
|
||
brush++)
|
||
{
|
||
i = brush - brush_scroll;
|
||
|
||
|
||
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
||
dest.y = ((i / 2) * 48) + 40 + off_y;
|
||
|
||
if (brush == cur_brush)
|
||
{
|
||
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
||
}
|
||
else if (brush < num_brushes)
|
||
{
|
||
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
}
|
||
|
||
if (brush < num_brushes)
|
||
{
|
||
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96) +
|
||
((48 - (img_brushes[brush]->w)) / 2);
|
||
|
||
/* FIXME: Shouldn't that be ->h??? */
|
||
|
||
dest.y = ((i / 2) * 48) + 40 + ((48 - (img_brushes[brush]->w)) / 2) +
|
||
off_y;
|
||
|
||
SDL_BlitSurface(img_brushes[brush], NULL, screen, &dest);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw fonts: */
|
||
static void draw_fonts(void)
|
||
{
|
||
int i, off_y, max, font, most;
|
||
SDL_Rect dest, src;
|
||
SDL_Surface * tmp_surf;
|
||
SDL_Color black = {0, 0, 0, 0};
|
||
|
||
/* Draw the title: */
|
||
draw_image_title(TITLE_LETTERS, r_ttoolopt);
|
||
|
||
|
||
/* How many can we show? */
|
||
|
||
most = 10;
|
||
if (disable_stamp_controls)
|
||
most = 14;
|
||
|
||
/* Do we need scrollbars? */
|
||
|
||
if (num_font_families > most + TOOLOFFSET)
|
||
{
|
||
off_y = 24;
|
||
max = most-2 + TOOLOFFSET;
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40;
|
||
|
||
if (font_scroll > 0)
|
||
{
|
||
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
||
}
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40 + 24 + ((6 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (!disable_stamp_controls)
|
||
dest.y = dest.y - (48 * 2);
|
||
|
||
if (font_scroll < num_font_families - (most-2) - TOOLOFFSET)
|
||
{
|
||
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
off_y = 0;
|
||
max = most + TOOLOFFSET;
|
||
}
|
||
|
||
|
||
/* Draw each of the shown fonts: */
|
||
|
||
for (font = font_scroll;
|
||
font < font_scroll + max;
|
||
font++)
|
||
{
|
||
i = font - font_scroll;
|
||
|
||
|
||
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
||
dest.y = ((i / 2) * 48) + 40 + off_y;
|
||
|
||
if (font == cur_font)
|
||
{
|
||
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
||
}
|
||
else if (font < num_font_families)
|
||
{
|
||
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
}
|
||
|
||
|
||
if (font < num_font_families)
|
||
{
|
||
SDL_Surface * tmp_surf_1;
|
||
|
||
tmp_surf_1 = render_text(getfonthandle(font),
|
||
gettext("Aa"), black);
|
||
|
||
if (tmp_surf_1->w > 48 || tmp_surf_1->h > 48)
|
||
{
|
||
tmp_surf = thumbnail(tmp_surf_1, 48, 48, 1);
|
||
SDL_FreeSurface(tmp_surf_1);
|
||
}
|
||
else
|
||
tmp_surf = tmp_surf_1;
|
||
|
||
src.x = (tmp_surf->w - 48) / 2;
|
||
src.y = (tmp_surf->h - 48) / 2;
|
||
src.w = 48;
|
||
src.h = 48;
|
||
|
||
if (src.x < 0)
|
||
src.x = 0;
|
||
if (src.y < 0)
|
||
src.y = 0;
|
||
|
||
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
||
if (src.w > tmp_surf->w)
|
||
{
|
||
src.w = tmp_surf->w;
|
||
dest.x = dest.x + ((48 - (tmp_surf->w)) / 2);
|
||
}
|
||
|
||
|
||
dest.y = ((i / 2) * 48) + 40 + off_y;
|
||
if (src.h > tmp_surf->h)
|
||
{
|
||
src.h = tmp_surf->h;
|
||
dest.y = dest.y + ((48 - (tmp_surf->h)) / 2);
|
||
}
|
||
|
||
SDL_BlitSurface(tmp_surf, &src, screen, &dest);
|
||
|
||
SDL_FreeSurface(tmp_surf);
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw text controls: */
|
||
|
||
if (!disable_stamp_controls)
|
||
{
|
||
SDL_Surface *button_color;
|
||
SDL_Surface *button_body;
|
||
|
||
/* Show bold button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40 + ((5 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (text_state & TTF_STYLE_BOLD)
|
||
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
||
else
|
||
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 96 + (48 - img_bold->w) / 2;
|
||
dest.y = (40 + ((5 + TOOLOFFSET / 2) * 48) +
|
||
(48 - img_bold->h) / 2);
|
||
|
||
SDL_BlitSurface(img_bold, NULL, screen, &dest);
|
||
|
||
|
||
/* Show italic button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 48;
|
||
dest.y = 40 + ((5 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (text_state & TTF_STYLE_ITALIC)
|
||
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
||
else
|
||
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 48 + (48 - img_italic->w) / 2;
|
||
dest.y = (40 + ((5 + TOOLOFFSET / 2) * 48) +
|
||
(48 - img_italic->h) / 2);
|
||
|
||
SDL_BlitSurface(img_italic, NULL, screen, &dest);
|
||
|
||
|
||
/* Show shrink button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40 + ((6 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (text_size > MIN_TEXT_SIZE)
|
||
{
|
||
button_color = img_black;
|
||
button_body = img_btn_up;
|
||
}
|
||
else
|
||
{
|
||
button_color = img_grey;
|
||
button_body = img_btn_off;
|
||
}
|
||
SDL_BlitSurface(button_body, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 96 + (48 - img_shrink->w) / 2;
|
||
dest.y = (40 + ((6 + TOOLOFFSET / 2) * 48) +
|
||
(48 - img_shrink->h) / 2);
|
||
|
||
SDL_BlitSurface(button_color, NULL, img_shrink, NULL);
|
||
SDL_BlitSurface(img_shrink, NULL, screen, &dest);
|
||
|
||
|
||
/* Show grow button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 48;
|
||
dest.y = 40 + ((6 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (text_size < MAX_TEXT_SIZE)
|
||
{
|
||
button_color = img_black;
|
||
button_body = img_btn_up;
|
||
}
|
||
else
|
||
{
|
||
button_color = img_grey;
|
||
button_body = img_btn_off;
|
||
}
|
||
SDL_BlitSurface(button_body, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 48 + (48 - img_grow->w) / 2;
|
||
dest.y = (40 + ((6 + TOOLOFFSET / 2) * 48) +
|
||
(48 - img_grow->h) / 2);
|
||
|
||
SDL_BlitSurface(button_color, NULL, img_grow, NULL);
|
||
SDL_BlitSurface(img_grow, NULL, screen, &dest);
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw stamps: */
|
||
|
||
static void draw_stamps(void)
|
||
{
|
||
int i, off_y, max, stamp, most;
|
||
int base_x, base_y;
|
||
SDL_Rect dest;
|
||
SDL_Surface * img;
|
||
|
||
|
||
/* Draw the title: */
|
||
draw_image_title(TITLE_STAMPS, r_ttoolopt);
|
||
|
||
|
||
/* How many can we show? */
|
||
|
||
most = 10;
|
||
if (disable_stamp_controls)
|
||
most = 14;
|
||
|
||
|
||
/* Do we need scrollbars? */
|
||
|
||
if (num_stamps > most + TOOLOFFSET)
|
||
{
|
||
off_y = 24;
|
||
max = (most - 2) + TOOLOFFSET;
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40;
|
||
|
||
if (stamp_scroll > 0)
|
||
{
|
||
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
||
}
|
||
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40 + 24 + ((6 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (!disable_stamp_controls)
|
||
dest.y = dest.y - (48 * 2);
|
||
|
||
if (stamp_scroll < num_stamps - (most - 2) - TOOLOFFSET)
|
||
{
|
||
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
off_y = 0;
|
||
max = most + TOOLOFFSET;
|
||
}
|
||
|
||
|
||
/* Draw each of the shown stamps: */
|
||
|
||
for (stamp = stamp_scroll;
|
||
stamp < stamp_scroll + max;
|
||
stamp++)
|
||
{
|
||
i = stamp - stamp_scroll;
|
||
|
||
|
||
dest.x = ((i % 2) * 48) + (WINDOW_WIDTH - 96);
|
||
dest.y = ((i / 2) * 48) + 40 + off_y;
|
||
|
||
if (stamp == cur_stamp)
|
||
{
|
||
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
||
}
|
||
else if (stamp < num_stamps)
|
||
{
|
||
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
}
|
||
|
||
if (stamp < num_stamps)
|
||
{
|
||
get_stamp_thumb(stamp_data[stamp]);
|
||
img = stamp_data[stamp]->thumbnail;
|
||
|
||
base_x = ((i % 2) * 48) + (WINDOW_WIDTH - 96) +
|
||
((48 - (img->w)) / 2);
|
||
|
||
base_y = ((i / 2) * 48) + 40 + ((48 - (img->h)) / 2) + off_y;
|
||
|
||
dest.x = base_x;
|
||
dest.y = base_y;
|
||
|
||
SDL_BlitSurface(img, NULL, screen, &dest);
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw stamp controls: */
|
||
|
||
if (!disable_stamp_controls)
|
||
{
|
||
SDL_Surface *button_color;
|
||
SDL_Surface *button_body;
|
||
|
||
/* Show mirror button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40 + ((5 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (stamp_data[cur_stamp]->mirrorable)
|
||
{
|
||
if (stamp_data[cur_stamp]->mirrored)
|
||
{
|
||
button_color = img_black;
|
||
button_body = img_btn_down;
|
||
}
|
||
else
|
||
{
|
||
button_color = img_black;
|
||
button_body = img_btn_up;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
button_color = img_grey;
|
||
button_body = img_btn_off;
|
||
}
|
||
SDL_BlitSurface(button_body, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 96 + (48 - img_magics[MAGIC_MIRROR]->w) / 2;
|
||
dest.y = (40 + ((5 + TOOLOFFSET / 2) * 48) +
|
||
(48 - img_magics[MAGIC_MIRROR]->h) / 2);
|
||
|
||
SDL_BlitSurface(button_color, NULL, img_magics[MAGIC_MIRROR], NULL);
|
||
SDL_BlitSurface(img_magics[MAGIC_MIRROR], NULL, screen, &dest);
|
||
|
||
|
||
/* Show flip button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 48;
|
||
dest.y = 40 + ((5 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (stamp_data[cur_stamp]->flipable)
|
||
{
|
||
if (stamp_data[cur_stamp]->flipped)
|
||
{
|
||
button_color = img_black;
|
||
button_body = img_btn_down;
|
||
}
|
||
else
|
||
{
|
||
button_color = img_black;
|
||
button_body = img_btn_up;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
button_color = img_grey;
|
||
button_body = img_btn_off;
|
||
}
|
||
SDL_BlitSurface(button_body, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 48 + (48 - img_magics[MAGIC_FLIP]->w) / 2;
|
||
dest.y = (40 + ((5 + TOOLOFFSET / 2) * 48) +
|
||
(48 - img_magics[MAGIC_FLIP]->h) / 2);
|
||
|
||
SDL_BlitSurface(button_color, NULL, img_magics[MAGIC_FLIP], NULL);
|
||
SDL_BlitSurface(img_magics[MAGIC_FLIP], NULL, screen, &dest);
|
||
|
||
|
||
/* Show shrink button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 40 + ((6 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (stamp_data[cur_stamp]->size > MIN_STAMP_SIZE)
|
||
{
|
||
button_color = img_black;
|
||
button_body = img_btn_up;
|
||
}
|
||
else
|
||
{
|
||
button_color = img_grey;
|
||
button_body = img_btn_off;
|
||
}
|
||
SDL_BlitSurface(button_body, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 96 + (48 - img_shrink->w) / 2;
|
||
dest.y = (40 + ((6 + TOOLOFFSET / 2) * 48) +
|
||
(48 - img_shrink->h) / 2);
|
||
|
||
SDL_BlitSurface(button_color, NULL, img_shrink, NULL);
|
||
SDL_BlitSurface(img_shrink, NULL, screen, &dest);
|
||
|
||
|
||
/* Show grow button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 48;
|
||
dest.y = 40 + ((6 + TOOLOFFSET / 2) * 48);
|
||
|
||
if (stamp_data[cur_stamp]->size < MAX_STAMP_SIZE)
|
||
{
|
||
button_color = img_black;
|
||
button_body = img_btn_up;
|
||
}
|
||
else
|
||
{
|
||
button_color = img_grey;
|
||
button_body = img_btn_off;
|
||
}
|
||
SDL_BlitSurface(button_body, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 48 + (48 - img_grow->w) / 2;
|
||
dest.y = (40 + ((6 + TOOLOFFSET / 2) * 48) +
|
||
(48 - img_grow->h) / 2);
|
||
|
||
SDL_BlitSurface(button_color, NULL, img_grow, NULL);
|
||
SDL_BlitSurface(img_grow, NULL, screen, &dest);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw the shape selector: */
|
||
|
||
static void draw_shapes(void)
|
||
{
|
||
int i;
|
||
SDL_Rect dest;
|
||
|
||
|
||
draw_image_title(TITLE_SHAPES, r_ttoolopt);
|
||
|
||
for (i = 0; i < 14 + TOOLOFFSET; i++)
|
||
{
|
||
dest.x = ((i % 2) * 48) + WINDOW_WIDTH - 96;
|
||
dest.y = ((i / 2) * 48) + 40;
|
||
|
||
|
||
if (i == cur_shape)
|
||
{
|
||
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
||
}
|
||
else if (i < NUM_SHAPES)
|
||
{
|
||
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
}
|
||
|
||
|
||
if (i < NUM_SHAPES)
|
||
{
|
||
dest.x = ((i % 2) * 48) + 4 + WINDOW_WIDTH - 96;
|
||
dest.y = ((i / 2) * 48) + 40 + 4;
|
||
|
||
SDL_BlitSurface(img_shapes[i], NULL, screen, &dest);
|
||
|
||
dest.x = ((i % 2) * 48) + 4 + WINDOW_WIDTH - 96 +
|
||
(40 - img_shape_names[i]->w) / 2;
|
||
dest.y = ((i / 2) * 48) + 40 + 4 + (44 - img_shape_names[i]->h);
|
||
|
||
SDL_BlitSurface(img_shape_names[i], NULL, screen, &dest);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw the eraser selector: */
|
||
|
||
static void draw_erasers(void)
|
||
{
|
||
int i, x, y, sz;
|
||
SDL_Rect dest;
|
||
|
||
|
||
draw_image_title(TITLE_ERASERS, r_ttoolopt);
|
||
|
||
for (i = 0; i < 14 + TOOLOFFSET; i++)
|
||
{
|
||
dest.x = ((i % 2) * 48) + WINDOW_WIDTH - 96;
|
||
dest.y = ((i / 2) * 48) + 40;
|
||
|
||
|
||
if (i == cur_eraser)
|
||
{
|
||
SDL_BlitSurface(img_btn_down, NULL, screen, &dest);
|
||
}
|
||
else if (i < NUM_ERASERS)
|
||
{
|
||
SDL_BlitSurface(img_btn_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
}
|
||
|
||
|
||
if (i < NUM_ERASERS)
|
||
{
|
||
sz = (2 + ((NUM_ERASERS - 1 - i) * (38 / (NUM_ERASERS - 1))));
|
||
|
||
x = ((i % 2) * 48) + WINDOW_WIDTH - 96 + 24 - sz / 2;
|
||
y = ((i / 2) * 48) + 40 + 24 - sz / 2;
|
||
|
||
dest.x = x;
|
||
dest.y = y;
|
||
dest.w = sz;
|
||
dest.h = 2;
|
||
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(screen->format, 0, 0, 0));
|
||
|
||
dest.x = x;
|
||
dest.y = y + sz - 2;
|
||
dest.w = sz;
|
||
dest.h = 2;
|
||
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(screen->format, 0, 0, 0));
|
||
|
||
dest.x = x;
|
||
dest.y = y;
|
||
dest.w = 2;
|
||
dest.h = sz;
|
||
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(screen->format, 0, 0, 0));
|
||
|
||
dest.x = x + sz - 2;
|
||
dest.y = y;
|
||
dest.w = 2;
|
||
dest.h = sz;
|
||
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(screen->format, 0, 0, 0));
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw no selectables: */
|
||
|
||
static void draw_none(void)
|
||
{
|
||
int i;
|
||
SDL_Rect dest;
|
||
|
||
dest.x = WINDOW_WIDTH - 96;
|
||
dest.y = 0;
|
||
SDL_BlitSurface(img_title_off, NULL, screen, &dest);
|
||
|
||
for (i = 0; i < 14 + TOOLOFFSET; i++)
|
||
{
|
||
dest.x = ((i % 2) * 48) + WINDOW_WIDTH - 96;
|
||
dest.y = ((i / 2) * 48) + 40;
|
||
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/* Create a thumbnail: */
|
||
|
||
static SDL_Surface * thumbnail(SDL_Surface * src, int max_x, int max_y,
|
||
int keep_aspect)
|
||
{
|
||
int x, y;
|
||
float src_x, src_y, off_x, off_y;
|
||
SDL_Surface * s;
|
||
Uint32 amask, tr, tg, tb, ta;
|
||
Uint8 r, g, b, a;
|
||
float xscale, yscale;
|
||
int tmp;
|
||
void (*putpixel)(SDL_Surface *, int, int, Uint32);
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int) = getpixels[src->format->BytesPerPixel];
|
||
|
||
|
||
/* Determine scale and centering offsets: */
|
||
|
||
if (!keep_aspect)
|
||
{
|
||
yscale = (float) ((float) src->h / (float) max_y);
|
||
xscale = (float) ((float) src->w / (float) max_x);
|
||
|
||
off_x = 0;
|
||
off_y = 0;
|
||
}
|
||
else
|
||
{
|
||
if (src->h > src->w)
|
||
{
|
||
yscale = (float) ((float) src->h / (float) max_y);
|
||
xscale = yscale;
|
||
|
||
off_x = ((src->h - src->w) / xscale) / 2;
|
||
off_y = 0;
|
||
}
|
||
else
|
||
{
|
||
xscale = (float) ((float) src->w / (float) max_x);
|
||
yscale = xscale;
|
||
|
||
off_x = 0;
|
||
off_y = ((src->w - src->h) / xscale) / 2;
|
||
}
|
||
}
|
||
|
||
|
||
/* Create surface for thumbnail: */
|
||
|
||
amask = ~(src->format->Rmask |
|
||
src->format->Gmask |
|
||
src->format->Bmask);
|
||
|
||
s = SDL_CreateRGBSurface(src->flags, /* SDL_SWSURFACE, */
|
||
max_x, max_y,
|
||
src->format->BitsPerPixel,
|
||
src->format->Rmask,
|
||
src->format->Gmask,
|
||
src->format->Bmask,
|
||
src->format->Amask); /* amask); */
|
||
|
||
|
||
if (s == NULL)
|
||
{
|
||
fprintf(stderr, "\nError: Can't build stamp thumbnails\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
putpixel= putpixels[s->format->BytesPerPixel];
|
||
|
||
/* Create thumbnail itself: */
|
||
|
||
SDL_LockSurface(src);
|
||
SDL_LockSurface(s);
|
||
|
||
for (y = 0; y < max_y; y++)
|
||
{
|
||
for (x = 0; x < max_x; x++)
|
||
{
|
||
#ifndef LOW_QUALITY_THUMBNAILS
|
||
tr = 0;
|
||
tg = 0;
|
||
tb = 0;
|
||
ta = 0;
|
||
|
||
tmp = 0;
|
||
|
||
for (src_y = y * yscale; src_y < y * yscale + yscale &&
|
||
src_y < src->h; src_y++)
|
||
{
|
||
for (src_x = x * xscale; src_x < x * xscale + xscale &&
|
||
src_x < src->w; src_x++)
|
||
{
|
||
SDL_GetRGBA(getpixel(src, src_x, src_y),
|
||
src->format,
|
||
&r, &g, &b, &a);
|
||
|
||
tr = tr + r;
|
||
tb = tb + b;
|
||
tg = tg + g;
|
||
ta = ta + a;
|
||
|
||
tmp++;
|
||
}
|
||
}
|
||
|
||
if (tmp != 0)
|
||
{
|
||
tr = tr / tmp;
|
||
tb = tb / tmp;
|
||
tg = tg / tmp;
|
||
ta = ta / tmp;
|
||
|
||
putpixel(s, x + off_x, y + off_y, SDL_MapRGBA(s->format,
|
||
(Uint8) tr,
|
||
(Uint8) tg,
|
||
(Uint8) tb,
|
||
(Uint8) ta));
|
||
}
|
||
#else
|
||
src_x = x * xscale;
|
||
src_y = y * yscale;
|
||
|
||
putpixel(s, x + off_x, y + off_y, getpixel(src, src_x, src_y));
|
||
#endif
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(s);
|
||
SDL_UnlockSurface(src);
|
||
|
||
return s;
|
||
}
|
||
|
||
|
||
/* Get a pixel: */
|
||
static Uint32 getpixel8(SDL_Surface * surface, int x, int y)
|
||
{
|
||
Uint8 * p;
|
||
|
||
/* get the X/Y values within the bounds of this surface */
|
||
if (unlikely( (unsigned)x > (unsigned)surface->w - 1u ))
|
||
x = (x<0) ? 0 : surface->w - 1;
|
||
if (unlikely( (unsigned)y > (unsigned)surface->h - 1u ))
|
||
y = (y<0) ? 0 : surface->h - 1;
|
||
|
||
/* Set a pointer to the exact location in memory of the pixel
|
||
in question: */
|
||
|
||
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start at top of RAM */
|
||
(y * surface->pitch) + /* Go down Y lines */
|
||
x); /* Go in X pixels */
|
||
|
||
|
||
/* Return the correctly-sized piece of data containing the
|
||
* pixel's value (an 8-bit palette value, or a 16-, 24- or 32-bit
|
||
* RGB value) */
|
||
|
||
return(*p);
|
||
}
|
||
|
||
|
||
/* Get a pixel: */
|
||
static Uint32 getpixel16(SDL_Surface * surface, int x, int y)
|
||
{
|
||
Uint8 * p;
|
||
|
||
/* get the X/Y values within the bounds of this surface */
|
||
if (unlikely( (unsigned)x > (unsigned)surface->w - 1u ))
|
||
x = (x<0) ? 0 : surface->w - 1;
|
||
if (unlikely( (unsigned)y > (unsigned)surface->h - 1u ))
|
||
y = (y<0) ? 0 : surface->h - 1;
|
||
|
||
/* Set a pointer to the exact location in memory of the pixel
|
||
in question: */
|
||
|
||
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start at top of RAM */
|
||
(y * surface->pitch) + /* Go down Y lines */
|
||
(x * 2)); /* Go in X pixels */
|
||
|
||
|
||
/* Return the correctly-sized piece of data containing the
|
||
* pixel's value (an 8-bit palette value, or a 16-, 24- or 32-bit
|
||
* RGB value) */
|
||
|
||
return(*(Uint16 *)p);
|
||
}
|
||
|
||
/* Get a pixel: */
|
||
static Uint32 getpixel24(SDL_Surface * surface, int x, int y)
|
||
{
|
||
Uint8 * p;
|
||
Uint32 pixel;
|
||
|
||
/* get the X/Y values within the bounds of this surface */
|
||
if (unlikely( (unsigned)x > (unsigned)surface->w - 1u ))
|
||
x = (x<0) ? 0 : surface->w - 1;
|
||
if (unlikely( (unsigned)y > (unsigned)surface->h - 1u ))
|
||
y = (y<0) ? 0 : surface->h - 1;
|
||
|
||
/* Set a pointer to the exact location in memory of the pixel
|
||
in question: */
|
||
|
||
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start at top of RAM */
|
||
(y * surface->pitch) + /* Go down Y lines */
|
||
(x * 3)); /* Go in X pixels */
|
||
|
||
|
||
/* Return the correctly-sized piece of data containing the
|
||
* pixel's value (an 8-bit palette value, or a 16-, 24- or 32-bit
|
||
* RGB value) */
|
||
|
||
/* Depending on the byte-order, it could be stored RGB or BGR! */
|
||
|
||
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
||
pixel = p[0] << 16 | p[1] << 8 | p[2];
|
||
else
|
||
pixel = p[0] | p[1] << 8 | p[2] << 16;
|
||
|
||
return pixel;
|
||
}
|
||
|
||
/* Get a pixel: */
|
||
static Uint32 getpixel32(SDL_Surface * surface, int x, int y)
|
||
{
|
||
Uint8 * p;
|
||
|
||
/* get the X/Y values within the bounds of this surface */
|
||
if (unlikely( (unsigned)x > (unsigned)surface->w - 1u ))
|
||
x = (x<0) ? 0 : surface->w - 1;
|
||
if (unlikely( (unsigned)y > (unsigned)surface->h - 1u ))
|
||
y = (y<0) ? 0 : surface->h - 1;
|
||
|
||
/* Set a pointer to the exact location in memory of the pixel
|
||
in question: */
|
||
|
||
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start at top of RAM */
|
||
(y * surface->pitch) + /* Go down Y lines */
|
||
(x * 4)); /* Go in X pixels */
|
||
|
||
|
||
/* Return the correctly-sized piece of data containing the
|
||
* pixel's value (an 8-bit palette value, or a 16-, 24- or 32-bit
|
||
* RGB value) */
|
||
|
||
return *(Uint32 *)p; // 32-bit display
|
||
}
|
||
|
||
|
||
/* Draw a single pixel into the surface: */
|
||
static void putpixel8(SDL_Surface * surface, int x, int y, Uint32 pixel)
|
||
{
|
||
Uint8 * p;
|
||
|
||
/* Assuming the X/Y values are within the bounds of this surface... */
|
||
if (likely( likely((unsigned)x<(unsigned)surface->w) && likely((unsigned)y<(unsigned)surface->h) ))
|
||
{
|
||
// Set a pointer to the exact location in memory of the pixel
|
||
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start: beginning of RAM */
|
||
(y * surface->pitch) + /* Go down Y lines */
|
||
x); /* Go in X pixels */
|
||
|
||
|
||
/* Set the (correctly-sized) piece of data in the surface's RAM
|
||
* to the pixel value sent in: */
|
||
|
||
*p = pixel;
|
||
}
|
||
}
|
||
|
||
/* Draw a single pixel into the surface: */
|
||
static void putpixel16(SDL_Surface * surface, int x, int y, Uint32 pixel)
|
||
{
|
||
Uint8 * p;
|
||
|
||
/* Assuming the X/Y values are within the bounds of this surface... */
|
||
if (likely( likely((unsigned)x<(unsigned)surface->w) && likely((unsigned)y<(unsigned)surface->h) ))
|
||
{
|
||
// Set a pointer to the exact location in memory of the pixel
|
||
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start: beginning of RAM */
|
||
(y * surface->pitch) + /* Go down Y lines */
|
||
(x * 2)); /* Go in X pixels */
|
||
|
||
|
||
/* Set the (correctly-sized) piece of data in the surface's RAM
|
||
* to the pixel value sent in: */
|
||
|
||
*(Uint16 *)p = pixel;
|
||
}
|
||
}
|
||
|
||
/* Draw a single pixel into the surface: */
|
||
static void putpixel24(SDL_Surface * surface, int x, int y, Uint32 pixel)
|
||
{
|
||
Uint8 * p;
|
||
|
||
/* Assuming the X/Y values are within the bounds of this surface... */
|
||
if (likely( likely((unsigned)x<(unsigned)surface->w) && likely((unsigned)y<(unsigned)surface->h) ))
|
||
{
|
||
// Set a pointer to the exact location in memory of the pixel
|
||
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start: beginning of RAM */
|
||
(y * surface->pitch) + /* Go down Y lines */
|
||
(x * 3)); /* Go in X pixels */
|
||
|
||
|
||
/* Set the (correctly-sized) piece of data in the surface's RAM
|
||
* to the pixel value sent in: */
|
||
|
||
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
||
{
|
||
p[0] = (pixel >> 16) & 0xff;
|
||
p[1] = (pixel >> 8) & 0xff;
|
||
p[2] = pixel & 0xff;
|
||
}
|
||
else
|
||
{
|
||
p[0] = pixel & 0xff;
|
||
p[1] = (pixel >> 8) & 0xff;
|
||
p[2] = (pixel >> 16) & 0xff;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
/* Draw a single pixel into the surface: */
|
||
static void putpixel32(SDL_Surface * surface, int x, int y, Uint32 pixel)
|
||
{
|
||
Uint8 * p;
|
||
|
||
/* Assuming the X/Y values are within the bounds of this surface... */
|
||
if (likely( likely((unsigned)x<(unsigned)surface->w) && likely((unsigned)y<(unsigned)surface->h) ))
|
||
{
|
||
// Set a pointer to the exact location in memory of the pixel
|
||
p = (Uint8 *) (((Uint8 *)surface->pixels) + /* Start: beginning of RAM */
|
||
(y * surface->pitch) + /* Go down Y lines */
|
||
(x * 4)); /* Go in X pixels */
|
||
|
||
|
||
/* Set the (correctly-sized) piece of data in the surface's RAM
|
||
* to the pixel value sent in: */
|
||
|
||
*(Uint32 *)p = pixel; // 32-bit display
|
||
}
|
||
}
|
||
|
||
|
||
// XOR must show up on black, white, 0x7f grey, and 0x80 grey.
|
||
// XOR must be exactly 100% perfectly reversable.
|
||
static void xorpixel(int x, int y)
|
||
{
|
||
Uint8 * p;
|
||
int BytesPerPixel;
|
||
|
||
// if outside the canvas, return
|
||
if( (unsigned)x >= (unsigned)canvas->w || (unsigned)y >= (unsigned)canvas->h)
|
||
return;
|
||
// now switch to screen coordinates
|
||
x += r_canvas.x;
|
||
y += r_canvas.y;
|
||
|
||
// Always 4, except 3 when loading a saved image.
|
||
BytesPerPixel = screen->format->BytesPerPixel;
|
||
|
||
// Set a pointer to the exact location in memory of the pixel
|
||
p = (Uint8 *) (((Uint8 *)screen->pixels) + /* Start: beginning of RAM */
|
||
(y * screen->pitch) + /* Go down Y lines */
|
||
(x * BytesPerPixel)); /* Go in X pixels */
|
||
|
||
// XOR the (correctly-sized) piece of data in the screen's RAM
|
||
if (likely(BytesPerPixel == 4))
|
||
*(Uint32 *)p ^= 0x80808080u; // 32-bit display
|
||
else if (BytesPerPixel == 1)
|
||
*p ^= 0x80;
|
||
else if (BytesPerPixel == 2)
|
||
*(Uint16 *)p ^= 0xd6d6;
|
||
else if (BytesPerPixel == 3)
|
||
{
|
||
p[0] ^= 0x80;
|
||
p[1] ^= 0x80;
|
||
p[2] ^= 0x80;
|
||
}
|
||
}
|
||
|
||
|
||
/* Undo! */
|
||
|
||
static void do_undo(void)
|
||
{
|
||
int wanna_update_toolbar;
|
||
|
||
wanna_update_toolbar = 0;
|
||
|
||
if (cur_undo != oldest_undo)
|
||
{
|
||
cur_undo--;
|
||
|
||
if (cur_undo < 0)
|
||
cur_undo = NUM_UNDO_BUFS - 1;
|
||
|
||
#ifdef DEBUG
|
||
printf("BLITTING: %d\n", cur_undo);
|
||
#endif
|
||
SDL_BlitSurface(undo_bufs[cur_undo], NULL, canvas, NULL);
|
||
|
||
|
||
if (img_starter != NULL)
|
||
{
|
||
if (undo_starters[cur_undo] == UNDO_STARTER_MIRRORED)
|
||
{
|
||
starter_mirrored = !starter_mirrored;
|
||
mirror_starter();
|
||
}
|
||
else if (undo_starters[cur_undo] == UNDO_STARTER_FLIPPED)
|
||
{
|
||
starter_flipped = !starter_flipped;
|
||
flip_starter();
|
||
}
|
||
}
|
||
|
||
update_canvas(0, 0, (WINDOW_WIDTH - 96), (48 * 7) + 40 + HEIGHTOFFSET);
|
||
|
||
|
||
if (cur_undo == oldest_undo)
|
||
{
|
||
tool_avail[TOOL_UNDO] = 0;
|
||
wanna_update_toolbar = 1;
|
||
}
|
||
|
||
if (tool_avail[TOOL_REDO] == 0)
|
||
{
|
||
tool_avail[TOOL_REDO] = 1;
|
||
wanna_update_toolbar = 1;
|
||
}
|
||
|
||
if (wanna_update_toolbar)
|
||
{
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
|
||
been_saved = 0;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
printf("UNDO: Current=%d Oldest=%d Newest=%d\n",
|
||
cur_undo, oldest_undo, newest_undo);
|
||
#endif
|
||
}
|
||
|
||
|
||
/* Redo! */
|
||
|
||
static void do_redo(void)
|
||
{
|
||
if (cur_undo != newest_undo)
|
||
{
|
||
if (img_starter != NULL)
|
||
{
|
||
if (undo_starters[cur_undo] == UNDO_STARTER_MIRRORED)
|
||
{
|
||
starter_mirrored = !starter_mirrored;
|
||
mirror_starter();
|
||
}
|
||
else if (undo_starters[cur_undo] == UNDO_STARTER_FLIPPED)
|
||
{
|
||
starter_flipped = !starter_flipped;
|
||
flip_starter();
|
||
}
|
||
}
|
||
|
||
cur_undo = (cur_undo + 1) % NUM_UNDO_BUFS;
|
||
|
||
#ifdef DEBUG
|
||
printf("BLITTING: %d\n", cur_undo);
|
||
#endif
|
||
SDL_BlitSurface(undo_bufs[cur_undo], NULL, canvas, NULL);
|
||
|
||
update_canvas(0, 0, (WINDOW_WIDTH - 96), (48 * 7) + 40 + HEIGHTOFFSET);
|
||
|
||
been_saved = 0;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
printf("REDO: Current=%d Oldest=%d Newest=%d\n",
|
||
cur_undo, oldest_undo, newest_undo);
|
||
#endif
|
||
|
||
|
||
if (((cur_undo + 1) % NUM_UNDO_BUFS) == newest_undo)
|
||
{
|
||
tool_avail[TOOL_REDO] = 0;
|
||
}
|
||
|
||
tool_avail[TOOL_UNDO] = 1;
|
||
|
||
draw_toolbar();
|
||
update_screen_rect(&r_tools);
|
||
}
|
||
|
||
|
||
/* Create the current brush in the current color: */
|
||
|
||
static void render_brush(void)
|
||
{
|
||
Uint32 amask;
|
||
int x, y;
|
||
Uint8 r, g, b, a;
|
||
Uint32 (*getpixel_brush)(SDL_Surface *, int, int) = getpixels[img_brushes[cur_brush]->format->BytesPerPixel];
|
||
void (*putpixel_brush)(SDL_Surface *, int, int, Uint32) = putpixels[img_brushes[cur_brush]->format->BytesPerPixel];
|
||
|
||
|
||
/* Kludge; not sure why cur_brush would become greater! */
|
||
|
||
if (cur_brush >= num_brushes)
|
||
cur_brush = 0;
|
||
|
||
|
||
/* Free the old rendered brush (if any): */
|
||
|
||
if (img_cur_brush != NULL)
|
||
{
|
||
SDL_FreeSurface(img_cur_brush);
|
||
}
|
||
|
||
|
||
/* Create a surface to render into: */
|
||
|
||
amask = ~(img_brushes[cur_brush]->format->Rmask |
|
||
img_brushes[cur_brush]->format->Gmask |
|
||
img_brushes[cur_brush]->format->Bmask);
|
||
|
||
img_cur_brush =
|
||
SDL_CreateRGBSurface(SDL_SWSURFACE,
|
||
img_brushes[cur_brush]->w,
|
||
img_brushes[cur_brush]->h,
|
||
img_brushes[cur_brush]->format->BitsPerPixel,
|
||
img_brushes[cur_brush]->format->Rmask,
|
||
img_brushes[cur_brush]->format->Gmask,
|
||
img_brushes[cur_brush]->format->Bmask,
|
||
amask);
|
||
|
||
if (img_cur_brush == NULL)
|
||
{
|
||
fprintf(stderr, "\nError: Can't render a brush!\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", SDL_GetError());
|
||
|
||
cleanup();
|
||
exit(1);
|
||
}
|
||
|
||
|
||
/* Render the new brush: */
|
||
|
||
SDL_LockSurface(img_brushes[cur_brush]);
|
||
SDL_LockSurface(img_cur_brush);
|
||
|
||
for (y = 0; y < img_brushes[cur_brush]->h; y++)
|
||
{
|
||
for (x = 0; x < img_brushes[cur_brush]->w; x++)
|
||
{
|
||
SDL_GetRGBA(getpixel_brush(img_brushes[cur_brush], x, y),
|
||
img_brushes[cur_brush]->format,
|
||
&r, &g, &b, &a);
|
||
|
||
putpixel_brush(img_cur_brush, x, y,
|
||
SDL_MapRGBA(img_cur_brush->format,
|
||
color_hexes[cur_color][0],
|
||
color_hexes[cur_color][1],
|
||
color_hexes[cur_color][2],
|
||
a));
|
||
}
|
||
}
|
||
|
||
SDL_UnlockSurface(img_cur_brush);
|
||
SDL_UnlockSurface(img_brushes[cur_brush]);
|
||
|
||
brush_counter = 0;
|
||
}
|
||
|
||
|
||
/* Play a sound: */
|
||
|
||
static void playsound(int chan, int s, int override)
|
||
{
|
||
#ifndef NOSOUND
|
||
if (use_sound && s != SND_NONE)
|
||
{
|
||
if (override || !Mix_Playing(chan))
|
||
Mix_PlayChannel(chan, sounds[s], 0);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
/* Draw a XOR line: */
|
||
|
||
static void line_xor(int x1, int y1, int x2, int y2)
|
||
{
|
||
int dx, dy, y, num_drawn;
|
||
float m, b;
|
||
|
||
|
||
/* Kludgey, but it works: */
|
||
|
||
//SDL_LockSurface(screen);
|
||
|
||
dx = x2 - x1;
|
||
dy = y2 - y1;
|
||
|
||
num_drawn = 0;
|
||
|
||
if (dx != 0)
|
||
{
|
||
m = ((float) dy) / ((float) dx);
|
||
b = y1 - m * x1;
|
||
|
||
if (x2 >= x1)
|
||
dx = 1;
|
||
else
|
||
dx = -1;
|
||
|
||
|
||
while (x1 != x2)
|
||
{
|
||
y1 = m * x1 + b;
|
||
y2 = m * (x1 + dx) + b;
|
||
|
||
if (y1 > y2)
|
||
{
|
||
y = y1;
|
||
y1 = y2;
|
||
y2 = y;
|
||
}
|
||
|
||
for (y = y1; y <= y2; y++)
|
||
{
|
||
num_drawn++;
|
||
if (num_drawn < 10 || dont_do_xor == 0)
|
||
xorpixel(x1,y);
|
||
}
|
||
|
||
x1 = x1 + dx;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (y1 > y2)
|
||
{
|
||
for (y = y1; y >= y2; y--)
|
||
{
|
||
num_drawn++;
|
||
|
||
if (num_drawn < 10 || dont_do_xor == 0)
|
||
xorpixel(x1,y);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (y = y1; y <= y2; y++)
|
||
{
|
||
num_drawn++;
|
||
|
||
if (num_drawn < 10 || dont_do_xor == 0)
|
||
xorpixel(x1,y);
|
||
}
|
||
}
|
||
}
|
||
|
||
//SDL_UnlockSurface(screen);
|
||
}
|
||
|
||
|
||
/* Draw a XOR rectangle: */
|
||
|
||
static void rect_xor(int x1, int y1, int x2, int y2)
|
||
{
|
||
if (x1 < 0)
|
||
x1 = 0;
|
||
|
||
if (x2 < 0)
|
||
x2 = 0;
|
||
|
||
if (y1 < 0)
|
||
y1 = 0;
|
||
|
||
if (y2 < 0)
|
||
y2 = 0;
|
||
|
||
if (x1 >= (WINDOW_WIDTH - 96 - 96))
|
||
x1 = (WINDOW_WIDTH - 96 - 96) - 1;
|
||
|
||
if (x2 >= (WINDOW_WIDTH - 96 - 96))
|
||
x2 = (WINDOW_WIDTH - 96 - 96) - 1;
|
||
|
||
if (y1 >= (48 * 7) + 40 + HEIGHTOFFSET)
|
||
y1 = (48 * 7) + 40 + HEIGHTOFFSET - 1;
|
||
|
||
if (y2 >= (48 * 7) + 40 + HEIGHTOFFSET)
|
||
y2 = (48 * 7) + 40 + HEIGHTOFFSET - 1;
|
||
|
||
line_xor(x1, y1, x2, y1);
|
||
line_xor(x2, y1, x2, y2);
|
||
line_xor(x2, y2, x1, y2);
|
||
line_xor(x1, y2, x1, y1);
|
||
}
|
||
|
||
|
||
/* Erase at the cursor! */
|
||
|
||
static void do_eraser(int x, int y)
|
||
{
|
||
SDL_Rect dest;
|
||
int sz;
|
||
|
||
sz = (ERASER_MIN +
|
||
((NUM_ERASERS - 1 - cur_eraser) *
|
||
((ERASER_MAX - ERASER_MIN) / (NUM_ERASERS - 1))));
|
||
|
||
dest.x = x - (sz / 2);
|
||
dest.y = y - (sz / 2);
|
||
dest.w = sz;
|
||
dest.h = sz;
|
||
|
||
if (img_starter_bkgd == NULL)
|
||
{
|
||
SDL_FillRect(canvas, &dest,
|
||
SDL_MapRGB(canvas->format, 255, 255, 255));
|
||
}
|
||
else
|
||
{
|
||
SDL_BlitSurface(img_starter_bkgd, &dest, canvas, &dest);
|
||
}
|
||
|
||
|
||
#ifndef NOSOUND
|
||
if (use_sound)
|
||
{
|
||
if (!Mix_Playing(0))
|
||
{
|
||
eraser_sound = (eraser_sound + 1) % 2;
|
||
|
||
playsound(0, SND_ERASER1 + eraser_sound, 0);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
update_canvas(x - sz / 2, y - sz / 2, x + sz / 2, y + sz / 2);
|
||
|
||
rect_xor(x - sz / 2, y - sz / 2,
|
||
x + sz / 2, y + sz / 2);
|
||
}
|
||
|
||
|
||
/* Reset available tools (for new image / starting out): */
|
||
|
||
static void reset_avail_tools(void)
|
||
{
|
||
int i;
|
||
int disallow_print = disable_print; /* set to 1 later if printer unavail */
|
||
|
||
for (i = 0; i < NUM_TOOLS; i++)
|
||
{
|
||
tool_avail[i] = 1;
|
||
}
|
||
|
||
|
||
/* Unavailable at the beginning of a new canvas: */
|
||
|
||
tool_avail[TOOL_UNDO] = 0;
|
||
tool_avail[TOOL_REDO] = 0;
|
||
|
||
if (been_saved)
|
||
tool_avail[TOOL_SAVE] = 0;
|
||
|
||
|
||
/* Unavailable in rare circumstances: */
|
||
|
||
if (num_stamps == 0)
|
||
tool_avail[TOOL_STAMP] = 0;
|
||
|
||
|
||
/* Disable quit? */
|
||
|
||
if (disable_quit)
|
||
tool_avail[TOOL_QUIT] = 0;
|
||
|
||
|
||
/* Disable save? */
|
||
|
||
if (disable_save)
|
||
tool_avail[TOOL_SAVE] = 0;
|
||
|
||
|
||
#ifdef WIN32
|
||
if(!IsPrinterAvailable()) disallow_print = 1;
|
||
#endif
|
||
|
||
#ifdef __BEOS__
|
||
if(!IsPrinterAvailable()) disallow_print = disable_print = 1;
|
||
#endif
|
||
|
||
|
||
/* Disable print? */
|
||
|
||
if (disallow_print)
|
||
tool_avail[TOOL_PRINT] = 0;
|
||
}
|
||
|
||
|
||
/* Save and disable available tools (for Open-Dialog) */
|
||
|
||
static void disable_avail_tools(void)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < NUM_TOOLS; i++)
|
||
{
|
||
tool_avail_bak[i] = tool_avail[i];
|
||
tool_avail[i]=0;
|
||
}
|
||
}
|
||
|
||
/* Restore and enable available tools (for End-Of-Open-Dialog) */
|
||
|
||
static void enable_avail_tools(void)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < NUM_TOOLS; i++)
|
||
{
|
||
tool_avail[i] = tool_avail_bak[i];
|
||
}
|
||
}
|
||
|
||
|
||
/* For qsort() call in do_open()... */
|
||
|
||
static int compare_dirent2s(struct dirent2 * f1, struct dirent2 * f2)
|
||
{
|
||
#ifdef DEBUG
|
||
printf("compare_dirents: %s\t%s\n", f1->f.d_name, f2->f.d_name);
|
||
#endif
|
||
|
||
if (f1->place == f2->place)
|
||
return (strcmp(f1->f.d_name, f2->f.d_name));
|
||
else
|
||
return (f1->place - f2->place);
|
||
}
|
||
|
||
|
||
/* Draw tux's text on the screen: */
|
||
|
||
static void draw_tux_text(int which_tux, const char * const str,
|
||
int want_right_to_left)
|
||
{
|
||
SDL_Rect dest;
|
||
SDL_Color black = {0, 0, 0, 0};
|
||
|
||
/* Remove any text-changing timer if one is running: */
|
||
control_drawtext_timer(0, "");
|
||
|
||
/* Clear first: */
|
||
SDL_FillRect(screen, &r_tuxarea, SDL_MapRGB(screen->format, 255, 255, 255));
|
||
|
||
/* Draw tux: */
|
||
dest.x = r_tuxarea.x;
|
||
dest.y = r_tuxarea.y + r_tuxarea.h - img_tux[which_tux]->h;
|
||
|
||
// if he's too tall to fit, go off the bottom (not top) edge
|
||
if (dest.y < r_tuxarea.y)
|
||
dest.y = r_tuxarea.y;
|
||
|
||
SDL_BlitSurface(img_tux[which_tux], NULL, screen, &dest);
|
||
|
||
wordwrap_text(str, black,
|
||
img_tux[which_tux]->w + 5,
|
||
r_tuxarea.y,
|
||
r_tuxarea.w,
|
||
want_right_to_left);
|
||
|
||
update_screen_rect(&r_tuxarea);
|
||
}
|
||
|
||
|
||
static void wordwrap_text(const char * const str, SDL_Color color,
|
||
int left, int top, int right,
|
||
int want_right_to_left)
|
||
{
|
||
int x, y, j;
|
||
unsigned int i;
|
||
char substr[512];
|
||
unsigned char * locale_str;
|
||
char * tstr;
|
||
unsigned char utf8_char[5];
|
||
int len;
|
||
SDL_Surface * text;
|
||
SDL_Rect dest, src;
|
||
|
||
int utf8_str_len, last_text_height;
|
||
unsigned char utf8_str[512];
|
||
|
||
|
||
/* Cursor starting position: */
|
||
|
||
x = left;
|
||
y = top;
|
||
|
||
last_text_height = 0;
|
||
|
||
debug(str);
|
||
debug(gettext(str));
|
||
debug("...");
|
||
|
||
if (strcmp(str, "") != 0)
|
||
{
|
||
if (strcmp(str, gettext(str)) == 0)
|
||
{
|
||
/* String isn't translated! Don't write right-to-left */
|
||
want_right_to_left = 0;
|
||
}
|
||
|
||
if (want_right_to_left == 0)
|
||
locale_str = (unsigned char *) strdup(gettext(str));
|
||
else
|
||
locale_str = (unsigned char *) textdir(gettext(str));
|
||
|
||
|
||
/* For each UTF8 character: */
|
||
|
||
utf8_str_len = 0;
|
||
utf8_str[0] = '\0';
|
||
|
||
for (i = 0; i <= strlen((char *) locale_str); i++)
|
||
{
|
||
if (locale_str[i] < 128)
|
||
{
|
||
utf8_str[utf8_str_len++] = locale_str[i];
|
||
utf8_str[utf8_str_len] = '\0';
|
||
|
||
|
||
/* Space? Blit the word! (Word-wrap if necessary) */
|
||
|
||
if (locale_str[i] == ' ' || locale_str[i] == '\0')
|
||
{
|
||
text = render_text(locale_font, (char *) utf8_str, color);
|
||
|
||
if (!text) continue; /* Didn't render anything... */
|
||
|
||
/* ----------- */
|
||
if (text->w > right - left)
|
||
{
|
||
/* Move left and down (if not already at left!) */
|
||
|
||
if (x > left)
|
||
{
|
||
if (need_right_to_left && want_right_to_left)
|
||
anti_carriage_return(left, right, top, top + text->h, y + text->h,
|
||
x - left);
|
||
|
||
x = left;
|
||
y = y + text->h;
|
||
}
|
||
|
||
|
||
/* Junk the blitted word; it's too long! */
|
||
|
||
last_text_height = text->h;
|
||
SDL_FreeSurface(text);
|
||
|
||
|
||
/* For each UTF8 character: */
|
||
|
||
for (j = 0; j < utf8_str_len; j++)
|
||
{
|
||
/* How many bytes does this character need? */
|
||
|
||
if (utf8_str[j] < 128) /* 0xxx xxxx - 1 byte */
|
||
{
|
||
utf8_char[0] = utf8_str[j];
|
||
utf8_char[1] = '\0';
|
||
}
|
||
else if ((utf8_str[j] & 0xE0) == 0xC0) /* 110x xxxx - 2 bytes */
|
||
{
|
||
utf8_char[0] = utf8_str[j];
|
||
utf8_char[1] = utf8_str[j + 1];
|
||
utf8_char[2] = '\0';
|
||
j = j + 1;
|
||
}
|
||
else if ((utf8_str[j] & 0xF0) == 0xE0) /* 1110 xxxx - 3 bytes */
|
||
{
|
||
utf8_char[0] = utf8_str[j];
|
||
utf8_char[1] = utf8_str[j + 1];
|
||
utf8_char[2] = utf8_str[j + 2];
|
||
utf8_char[3] = '\0';
|
||
j = j + 2;
|
||
}
|
||
else /* 1111 0xxx - 4 bytes */
|
||
{
|
||
utf8_char[0] = utf8_str[j];
|
||
utf8_char[1] = utf8_str[j + 1];
|
||
utf8_char[2] = utf8_str[j + 2];
|
||
utf8_char[3] = utf8_str[j + 3];
|
||
utf8_char[4] = '\0';
|
||
j = j + 3;
|
||
}
|
||
|
||
|
||
if (utf8_char[0] != '\0')
|
||
{
|
||
text = render_text(locale_font, (char *) utf8_char, color);
|
||
if (text != NULL)
|
||
{
|
||
if (x + text->w > right)
|
||
{
|
||
if (need_right_to_left && want_right_to_left)
|
||
anti_carriage_return(left, right, top, top + text->h,
|
||
y + text->h, x - left);
|
||
|
||
x = left;
|
||
y = y + text->h;
|
||
}
|
||
|
||
dest.x = x;
|
||
|
||
if (need_right_to_left && want_right_to_left)
|
||
dest.y = top;
|
||
else
|
||
dest.y = y;
|
||
|
||
SDL_BlitSurface(text, NULL, screen, &dest);
|
||
|
||
last_text_height = text->h;
|
||
|
||
x = x + text->w;
|
||
|
||
SDL_FreeSurface(text);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Not too wide for one line... */
|
||
|
||
if (x + text->w > right)
|
||
{
|
||
/* This word needs to move down? */
|
||
|
||
if (need_right_to_left && want_right_to_left)
|
||
anti_carriage_return(left, right, top, top + text->h, y + text->h,
|
||
x - left);
|
||
|
||
x = left;
|
||
y = y + text->h;
|
||
}
|
||
|
||
dest.x = x;
|
||
|
||
if (need_right_to_left && want_right_to_left)
|
||
dest.y = top;
|
||
else
|
||
dest.y = y;
|
||
|
||
SDL_BlitSurface(text, NULL, screen, &dest);
|
||
|
||
last_text_height = text->h;
|
||
x = x + text->w;
|
||
|
||
SDL_FreeSurface(text);
|
||
}
|
||
|
||
|
||
utf8_str_len = 0;
|
||
utf8_str[0] = '\0';
|
||
}
|
||
}
|
||
else if ((locale_str[i] & 0xE0) == 0xC0)
|
||
{
|
||
utf8_str[utf8_str_len++] = locale_str[i];
|
||
utf8_str[utf8_str_len++] = locale_str[i + 1];
|
||
utf8_str[utf8_str_len] = '\0';
|
||
i++;
|
||
}
|
||
else if ((locale_str[i] & 0xF0) == 0xE0)
|
||
{
|
||
utf8_str[utf8_str_len++] = locale_str[i];
|
||
utf8_str[utf8_str_len++] = locale_str[i + 1];
|
||
utf8_str[utf8_str_len++] = locale_str[i + 2];
|
||
utf8_str[utf8_str_len] = '\0';
|
||
i = i + 2;
|
||
}
|
||
else
|
||
{
|
||
utf8_str[utf8_str_len++] = locale_str[i];
|
||
utf8_str[utf8_str_len++] = locale_str[i + 1];
|
||
utf8_str[utf8_str_len++] = locale_str[i + 2];
|
||
utf8_str[utf8_str_len++] = locale_str[i + 3];
|
||
utf8_str[utf8_str_len] = '\0';
|
||
i = i + 3;
|
||
}
|
||
}
|
||
|
||
free(locale_str);
|
||
}
|
||
else if (strlen(str) != 0)
|
||
{
|
||
/* Truncate if too big! (sorry!) */
|
||
|
||
{
|
||
char *s1 = gettext(str);
|
||
if (want_right_to_left)
|
||
{
|
||
char *freeme = s1;
|
||
s1 = textdir(s1);
|
||
free(freeme);
|
||
}
|
||
tstr = uppercase(s1);
|
||
free(s1);
|
||
}
|
||
|
||
if (strlen(tstr) > sizeof(substr) - 1)
|
||
tstr[sizeof(substr) - 1] = '\0';
|
||
|
||
|
||
/* For each word... */
|
||
|
||
for (i = 0; i < strlen(tstr); i++)
|
||
{
|
||
/* Figure out the word... */
|
||
|
||
len = 0;
|
||
|
||
for (j = i; tstr[j] != ' ' && tstr[j] != '\0'; j++)
|
||
{
|
||
substr[len++] = tstr[j];
|
||
}
|
||
|
||
substr[len++] = ' ';
|
||
substr[len] = '\0';
|
||
|
||
|
||
/* Render the word for display... */
|
||
|
||
|
||
text = render_text(locale_font, substr, color);
|
||
|
||
|
||
/* If it won't fit on this line, move to the next! */
|
||
|
||
if (x + text->w > right) /* Correct? */
|
||
{
|
||
if (need_right_to_left && want_right_to_left)
|
||
anti_carriage_return(left, right, top, top + text->h, y + text->h,
|
||
x - left);
|
||
|
||
x = left;
|
||
y = y + text->h;
|
||
}
|
||
|
||
|
||
/* Draw the word: */
|
||
|
||
dest.x = x;
|
||
|
||
if (need_right_to_left && want_right_to_left)
|
||
dest.y = top;
|
||
else
|
||
dest.y = y;
|
||
|
||
SDL_BlitSurface(text, NULL, screen, &dest);
|
||
|
||
|
||
/* Move the cursor one word's worth: */
|
||
|
||
x = x + text->w;
|
||
|
||
|
||
/* Free the temp. surface: */
|
||
|
||
last_text_height = text->h;
|
||
SDL_FreeSurface(text);
|
||
|
||
|
||
/* Now on to the next word... */
|
||
|
||
i = j;
|
||
}
|
||
|
||
free(tstr);
|
||
}
|
||
|
||
|
||
/* Right-justify the final line of text, in right-to-left mode: */
|
||
|
||
if (need_right_to_left && want_right_to_left && last_text_height > 0)
|
||
{
|
||
src.x = left;
|
||
src.y = top;
|
||
src.w = x - left;
|
||
src.h = last_text_height;
|
||
|
||
dest.x = right - src.w;
|
||
dest.y = top;
|
||
|
||
SDL_BlitSurface(screen, &src, screen, &dest);
|
||
|
||
dest.x = left;
|
||
dest.y = top;
|
||
dest.w = (right - left - src.w);
|
||
dest.h = last_text_height;
|
||
|
||
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
|
||
}
|
||
}
|
||
|
||
|
||
/* Load a file's sound: */
|
||
|
||
#ifndef NOSOUND
|
||
|
||
static Mix_Chunk * loadsound(const char * const fname)
|
||
{
|
||
char * snd_fname;
|
||
char tmp_str[64];
|
||
Mix_Chunk * tmp_snd;
|
||
|
||
|
||
debug(fname);
|
||
|
||
|
||
/* First, check for localized version of sound: */
|
||
|
||
snd_fname = malloc(strlen(fname) + strlen(lang_prefix) + 2);
|
||
|
||
strcpy(snd_fname, fname);
|
||
snprintf(tmp_str, sizeof(tmp_str), "_%s.wav", lang_prefix);
|
||
|
||
|
||
if (strcasestr(snd_fname, ".png") != NULL)
|
||
{
|
||
strcpy(strcasestr(snd_fname, ".png"), tmp_str);
|
||
debug(snd_fname);
|
||
|
||
tmp_snd = Mix_LoadWAV(snd_fname);
|
||
|
||
if (tmp_snd == NULL)
|
||
{
|
||
debug("...No local version of sound!");
|
||
|
||
/* Now, check for default sound: */
|
||
|
||
free(snd_fname);
|
||
|
||
snd_fname = strdup(fname);
|
||
|
||
if (strcasestr(snd_fname, ".png") != NULL)
|
||
{
|
||
strcpy(strcasestr(snd_fname, ".png"), ".wav");
|
||
debug(snd_fname);
|
||
tmp_snd = Mix_LoadWAV(snd_fname);
|
||
free(snd_fname);
|
||
|
||
if (tmp_snd == NULL)
|
||
{
|
||
debug("...No default version of sound!");
|
||
return NULL;
|
||
}
|
||
|
||
return (tmp_snd);
|
||
}
|
||
else
|
||
{
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
return (tmp_snd);
|
||
}
|
||
else
|
||
{
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
/* Strip any trailing spaces: */
|
||
|
||
static void strip_trailing_whitespace( char *buf )
|
||
{
|
||
unsigned i = strlen(buf);
|
||
while(i--)
|
||
{
|
||
if(!isspace(buf[i])) break;
|
||
buf[i] = '\0';
|
||
}
|
||
}
|
||
|
||
|
||
/* Load a file's description: */
|
||
|
||
static char * loaddesc(const char * const fname)
|
||
{
|
||
char * txt_fname;
|
||
char buf[256], def_buf[256];
|
||
int found, got_first;
|
||
FILE * fi;
|
||
|
||
|
||
txt_fname = strdup(fname);
|
||
|
||
if (strcasestr(txt_fname, ".png") != NULL) // FIXME: isn't this always OK?
|
||
{
|
||
strcpy(strcasestr(txt_fname, ".png"), ".txt");
|
||
|
||
fi = fopen(txt_fname, "r");
|
||
free(txt_fname);
|
||
|
||
if (!fi)
|
||
return NULL;
|
||
|
||
|
||
got_first = 0;
|
||
found = 0;
|
||
|
||
strcpy(def_buf, "");
|
||
|
||
do
|
||
{
|
||
fgets(buf, sizeof(buf), fi);
|
||
|
||
if (!feof(fi))
|
||
{
|
||
strip_trailing_whitespace(buf);
|
||
|
||
|
||
if (!got_first)
|
||
{
|
||
/* First one is the default: */
|
||
|
||
strcpy(def_buf, buf);
|
||
got_first = 1;
|
||
}
|
||
|
||
|
||
debug(buf);
|
||
|
||
|
||
/* See if it's the one for this locale... */
|
||
|
||
if (strcasestr(buf, lang_prefix) == buf)
|
||
{
|
||
|
||
debug(buf + strlen(lang_prefix));
|
||
if (strcasestr(buf + strlen(lang_prefix), ".utf8=") ==
|
||
buf + strlen(lang_prefix))
|
||
{
|
||
found = 1;
|
||
|
||
debug("...FOUND!");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
while (!feof(fi) && !found);
|
||
|
||
fclose(fi);
|
||
|
||
|
||
/* Return the string: */
|
||
|
||
if (found)
|
||
{
|
||
return(strdup(buf + (strlen(lang_prefix)) + 6));
|
||
}
|
||
else
|
||
{
|
||
/* No locale-specific translation; use the default (English) */
|
||
|
||
return(strdup(def_buf));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
|
||
/* Load a *.dat file */
|
||
static double loadinfo(const char * const fname, stamp_type *inf)
|
||
{
|
||
char buf[256];
|
||
FILE * fi;
|
||
double ratio = 1.0;
|
||
|
||
inf->colorable = 0;
|
||
inf->tintable = 0;
|
||
inf->mirrorable = 1;
|
||
inf->flipable = 1;
|
||
inf->tinter = TINTER_NORMAL;
|
||
|
||
fi = fopen(fname, "r");
|
||
if (!fi)
|
||
return ratio;
|
||
|
||
do
|
||
{
|
||
fgets(buf, sizeof buf, fi);
|
||
|
||
if (!feof(fi))
|
||
{
|
||
strip_trailing_whitespace(buf);
|
||
|
||
if (strcmp(buf, "colorable") == 0)
|
||
inf->colorable = 1;
|
||
else if (strcmp(buf, "tintable") == 0)
|
||
inf->tintable = 1;
|
||
else if (!memcmp(buf, "scale", 5) && (isspace(buf[5]) || buf[5]=='='))
|
||
{
|
||
double tmp, tmp2;
|
||
char *cp = buf+6;
|
||
while (isspace(*cp) || *cp=='=')
|
||
cp++;
|
||
if (strchr(cp,'%'))
|
||
{
|
||
tmp = strtod(cp,NULL) / 100.0;
|
||
if (tmp > 0.0001 && tmp < 10000.0)
|
||
ratio = tmp;
|
||
}
|
||
else if (strchr(cp,'/'))
|
||
{
|
||
tmp = strtod(cp,&cp);
|
||
while(*cp && !isdigit(*cp))
|
||
cp++;
|
||
tmp2 = strtod(cp,NULL);
|
||
if (tmp>0.0001 && tmp<10000.0 && tmp2>0.0001 && tmp2<10000.0 && tmp/tmp2>0.0001 && tmp/tmp2<10000.0)
|
||
ratio = tmp/tmp2;
|
||
}
|
||
else if (strchr(cp,':'))
|
||
{
|
||
tmp = strtod(cp,&cp);
|
||
while(*cp && !isdigit(*cp))
|
||
cp++;
|
||
tmp2 = strtod(cp,NULL);
|
||
if (tmp>0.0001 && tmp<10000.0 && tmp2>0.0001 && tmp2<10000.0 && tmp2/tmp>0.0001 && tmp2/tmp<10000.0)
|
||
ratio = tmp2/tmp;
|
||
}
|
||
else
|
||
{
|
||
tmp = strtod(cp,NULL);
|
||
if (tmp > 0.0001 && tmp < 10000.0)
|
||
ratio = 1.0 / tmp;
|
||
}
|
||
}
|
||
else if (!memcmp(buf, "tinter", 6) && (isspace(buf[6]) || buf[6]=='='))
|
||
{
|
||
char *cp = buf+7;
|
||
while (isspace(*cp) || *cp=='=')
|
||
cp++;
|
||
if (!strcmp(cp,"anyhue"))
|
||
{
|
||
inf->tinter = TINTER_ANYHUE;
|
||
}
|
||
else if (!strcmp(cp,"narrow"))
|
||
{
|
||
inf->tinter = TINTER_NARROW;
|
||
}
|
||
else if (!strcmp(cp,"normal"))
|
||
{
|
||
inf->tinter = TINTER_NORMAL;
|
||
}
|
||
else if (!strcmp(cp,"vector"))
|
||
{
|
||
inf->tinter = TINTER_VECTOR;
|
||
}
|
||
else
|
||
{
|
||
debug(cp);
|
||
}
|
||
}
|
||
else if (strcmp(buf, "nomirror") == 0)
|
||
inf->mirrorable = 0;
|
||
else if (strcmp(buf, "noflip") == 0)
|
||
inf->flipable = 0;
|
||
}
|
||
}
|
||
while (!feof(fi));
|
||
|
||
fclose(fi);
|
||
return ratio;
|
||
}
|
||
|
||
|
||
static int SDLCALL NondefectiveBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
|
||
{
|
||
int dstx = 0;
|
||
int dsty = 0;
|
||
int srcx = 0;
|
||
int srcy = 0;
|
||
int srcw = src->w;
|
||
int srch = src->h;
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int) = getpixels[src->format->BytesPerPixel];
|
||
void (*putpixel)(SDL_Surface *, int, int, Uint32) = putpixels[dst->format->BytesPerPixel];
|
||
|
||
|
||
if(srcrect)
|
||
{
|
||
srcx = srcrect->x;
|
||
srcy = srcrect->y;
|
||
srcw = srcrect->w;
|
||
srch = srcrect->h;
|
||
}
|
||
if(dstrect)
|
||
{
|
||
dstx = dstrect->x;
|
||
dsty = dstrect->y;
|
||
}
|
||
if(dsty<0)
|
||
{
|
||
srcy += -dsty;
|
||
srch -= -dsty;
|
||
dsty = 0;
|
||
}
|
||
if(dstx<0)
|
||
{
|
||
srcx += -dstx;
|
||
srcw -= -dstx;
|
||
dstx = 0;
|
||
}
|
||
if(dstx+srcw > dst->w-1)
|
||
{
|
||
srcw -= (dstx+srcw) - (dst->w-1);
|
||
}
|
||
if(dsty+srch > dst->h-1)
|
||
{
|
||
srch -= (dsty+srch) - (dst->h-1);
|
||
}
|
||
if(srcw<1 || srch<1)
|
||
return -1; /* no idea what to return if nothing done */
|
||
while(srch--)
|
||
{
|
||
int i = srcw;
|
||
while(i--)
|
||
{
|
||
putpixel(dst, i+dstx, srch+dsty, getpixel(src, i+srcx, srch+srcy));
|
||
}
|
||
}
|
||
|
||
return(0);
|
||
}
|
||
|
||
|
||
// For the 3rd arg, pass either NondefectiveBlit or SDL_BlitSurface.
|
||
static void autoscale_copy_smear_free(SDL_Surface *src, SDL_Surface *dst, int SDLCALL (*blit)(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect))
|
||
{
|
||
SDL_Surface *src1;
|
||
SDL_Rect dest;
|
||
// What to do when in 640x480 mode, and loading an
|
||
// 800x600 (or larger) image!? Scale it. Starters must
|
||
// be scaled too. Keep the pixels square though, filling
|
||
// in the gaps via a smear.
|
||
if(src->w != dst->w || src->h != dst->h)
|
||
{
|
||
if(src->w / (float)dst->w > src->h / (float)dst->h)
|
||
src1 = thumbnail(src, dst->w, src->h*dst->w/src->w, 0);
|
||
else
|
||
src1 = thumbnail(src, src->w*dst->h/src->h, dst->h, 0);
|
||
SDL_FreeSurface(src);
|
||
src = src1;
|
||
}
|
||
|
||
dest.x = (dst->w - src->w) / 2;
|
||
dest.y = (dst->h - src->h) / 2;
|
||
blit(src, NULL, dst, &dest);
|
||
|
||
if(src->w != dst->w)
|
||
{
|
||
// we know that the heights match, and src is narrower
|
||
SDL_Rect sour;
|
||
int i = (dst->w - src->w) / 2;
|
||
sour.w = 1;
|
||
sour.x = 0;
|
||
sour.h = src->h;
|
||
sour.y = 0;
|
||
while(i-- > 0)
|
||
{
|
||
dest.x = i;
|
||
blit(src, &sour, dst, &dest);
|
||
}
|
||
sour.x = src->w - 1;
|
||
i = (dst->w - src->w) / 2 + src->w - 1;
|
||
while(++i < dst->w)
|
||
{
|
||
dest.x = i;
|
||
blit(src, &sour, dst, &dest);
|
||
}
|
||
}
|
||
|
||
if(src->h != dst->h)
|
||
{
|
||
// we know that the widths match, and src is shorter
|
||
SDL_Rect sour;
|
||
int i = (dst->h - src->h) / 2;
|
||
sour.w = src->w;
|
||
sour.x = 0;
|
||
sour.h = 1;
|
||
sour.y = 0;
|
||
while(i-- > 0)
|
||
{
|
||
dest.y = i;
|
||
blit(src, &sour, dst, &dest);
|
||
}
|
||
sour.y = src->h - 1;
|
||
i = (dst->h - src->h) / 2 + src->h - 1;
|
||
while(++i < dst->h)
|
||
{
|
||
dest.y = i;
|
||
blit(src, &sour, dst, &dest);
|
||
}
|
||
}
|
||
|
||
SDL_FreeSurface(src);
|
||
}
|
||
|
||
|
||
static void load_starter_id(char * saved_id)
|
||
{
|
||
char * rname;
|
||
char fname[32];
|
||
FILE * fi;
|
||
|
||
snprintf(fname, sizeof(fname), "saved/%s.dat", saved_id);
|
||
rname = get_fname(fname);
|
||
|
||
starter_id[0] = '\0';
|
||
|
||
fi = fopen(rname, "r");
|
||
if (fi != NULL)
|
||
{
|
||
fgets(starter_id, sizeof(starter_id), fi);
|
||
starter_id[strlen(starter_id) - 1] = '\0';
|
||
|
||
fscanf(fi, "%d", &starter_mirrored);
|
||
fscanf(fi, "%d", &starter_flipped);
|
||
|
||
fclose(fi);
|
||
}
|
||
|
||
free(rname);
|
||
}
|
||
|
||
|
||
|
||
static void load_starter(char * img_id)
|
||
{
|
||
char * dirname;
|
||
char fname[256];
|
||
SDL_Surface * tmp_surf;
|
||
|
||
/* Determine path to starter files: */
|
||
|
||
dirname = strdup(DATA_PREFIX "starters");
|
||
|
||
/* Clear them to NULL first: */
|
||
img_starter = NULL;
|
||
img_starter_bkgd = NULL;
|
||
|
||
/* Load the core image: */
|
||
snprintf(fname, sizeof(fname), "%s/%s.png", dirname, img_id);
|
||
tmp_surf = IMG_Load(fname);
|
||
|
||
if (tmp_surf != NULL)
|
||
{
|
||
img_starter = SDL_DisplayFormatAlpha(tmp_surf);
|
||
SDL_FreeSurface(tmp_surf);
|
||
}
|
||
|
||
if (img_starter != NULL &&
|
||
(img_starter->w != canvas->w || img_starter->h != canvas->h))
|
||
{
|
||
tmp_surf = img_starter;
|
||
|
||
img_starter = SDL_CreateRGBSurface(canvas->flags,
|
||
canvas->w, canvas->h,
|
||
tmp_surf->format->BitsPerPixel,
|
||
tmp_surf->format->Rmask,
|
||
tmp_surf->format->Gmask,
|
||
tmp_surf->format->Bmask,
|
||
tmp_surf->format->Amask);
|
||
|
||
// 3rd arg ignored for RGBA surfaces
|
||
SDL_SetAlpha(tmp_surf, SDL_RLEACCEL, SDL_ALPHA_OPAQUE);
|
||
autoscale_copy_smear_free(tmp_surf,img_starter,NondefectiveBlit);
|
||
SDL_SetAlpha(img_starter, SDL_RLEACCEL|SDL_SRCALPHA, SDL_ALPHA_OPAQUE);
|
||
}
|
||
|
||
/* Try to load the a background image: */
|
||
|
||
/* (JPEG first) */
|
||
snprintf(fname, sizeof(fname), "%s/%s-back.jpeg", dirname, img_id);
|
||
tmp_surf = IMG_Load(fname);
|
||
|
||
/* (Failed? Try PNG next) */
|
||
if (tmp_surf == NULL)
|
||
{
|
||
snprintf(fname, sizeof(fname), "%s/%s-back.png", dirname, img_id);
|
||
tmp_surf = IMG_Load(fname);
|
||
}
|
||
|
||
if (tmp_surf != NULL)
|
||
{
|
||
img_starter_bkgd = SDL_DisplayFormat(tmp_surf);
|
||
SDL_FreeSurface(tmp_surf);
|
||
}
|
||
|
||
if (img_starter_bkgd != NULL &&
|
||
(img_starter_bkgd->w != canvas->w || img_starter_bkgd->h != canvas->h))
|
||
{
|
||
tmp_surf = img_starter_bkgd;
|
||
|
||
img_starter_bkgd = SDL_CreateRGBSurface(SDL_SWSURFACE,
|
||
canvas->w, canvas->h,
|
||
canvas->format->BitsPerPixel,
|
||
canvas->format->Rmask,
|
||
canvas->format->Gmask,
|
||
canvas->format->Bmask,
|
||
0);
|
||
|
||
autoscale_copy_smear_free(tmp_surf,img_starter_bkgd,SDL_BlitSurface);
|
||
}
|
||
|
||
free(dirname);
|
||
}
|
||
|
||
|
||
/* Load current (if any) image: */
|
||
|
||
static void load_current(void)
|
||
{
|
||
SDL_Surface * tmp;
|
||
char * fname;
|
||
char ftmp[1024];
|
||
FILE * fi;
|
||
|
||
/* Determine the current picture's ID: */
|
||
|
||
fname = get_fname("current_id.txt");
|
||
|
||
fi = fopen(fname, "r");
|
||
if (fi == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: Couldn't determine the current image's ID\n"
|
||
"%s\n"
|
||
"The system error that occurred was:\n"
|
||
"%s\n\n", fname, strerror(errno));
|
||
file_id[0] = '\0';
|
||
starter_id[0] = '\0';
|
||
}
|
||
else
|
||
{
|
||
fgets(file_id, sizeof(file_id), fi);
|
||
if (strlen(file_id) > 0)
|
||
{
|
||
file_id[strlen(file_id) - 1] = '\0';
|
||
}
|
||
fclose(fi);
|
||
}
|
||
|
||
free(fname);
|
||
|
||
|
||
/* Load that image: */
|
||
|
||
if (file_id[0] != '\0')
|
||
{
|
||
snprintf(ftmp, sizeof(ftmp), "saved/%s%s", file_id, FNAME_EXTENSION);
|
||
|
||
fname = get_fname(ftmp);
|
||
|
||
#ifdef SAVE_AS_BMP
|
||
tmp = SDL_LoadBMP(fname);
|
||
#else
|
||
tmp = IMG_Load(fname);
|
||
#endif
|
||
|
||
if (tmp == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: Couldn't load any current image.\n"
|
||
"%s\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", fname, SDL_GetError());
|
||
|
||
file_id[0] = '\0';
|
||
starter_id[0] = '\0';
|
||
}
|
||
else
|
||
{
|
||
autoscale_copy_smear_free(tmp,canvas,SDL_BlitSurface);
|
||
load_starter_id(file_id);
|
||
load_starter(starter_id);
|
||
|
||
if (starter_mirrored)
|
||
mirror_starter();
|
||
|
||
if (starter_flipped)
|
||
flip_starter();
|
||
}
|
||
|
||
free(fname);
|
||
}
|
||
}
|
||
|
||
|
||
/* Save the current image to disk: */
|
||
|
||
static void save_current(void)
|
||
{
|
||
char * fname;
|
||
int res;
|
||
FILE * fi;
|
||
|
||
|
||
fname = get_fname("");
|
||
|
||
res = mkdir(fname, 0755);
|
||
|
||
if (res != 0 && errno != EEXIST)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't create user data directory:\n"
|
||
"%s\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n", fname, strerror(errno));
|
||
|
||
draw_tux_text(TUX_OOPS, strerror(errno), 0);
|
||
}
|
||
|
||
free(fname);
|
||
|
||
|
||
fname = get_fname("current_id.txt");
|
||
|
||
fi = fopen(fname, "w");
|
||
if (fi == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't keep track of current image.\n"
|
||
"%s\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n", fname, strerror(errno));
|
||
|
||
draw_tux_text(TUX_OOPS, strerror(errno), 0);
|
||
}
|
||
else
|
||
{
|
||
fprintf(fi, "%s\n", file_id);
|
||
fclose(fi);
|
||
}
|
||
|
||
free(fname);
|
||
}
|
||
|
||
|
||
/* The filename for the current image: */
|
||
|
||
static char * get_fname(const char * const name)
|
||
{
|
||
char f[512];
|
||
const char * tux_settings_dir;
|
||
|
||
|
||
/* Where is the user's data directory?
|
||
This is where their saved files are stored,
|
||
local fonts, brushes and stamps can be found,
|
||
and where the "current_id.txt" file is saved */
|
||
|
||
#ifdef WIN32
|
||
/* Windows predefines "savedir" as:
|
||
"C:\Documents and Settings\%USERNAME%\Application Data\TuxPaint"
|
||
though it may get overridden with "--savedir" option */
|
||
|
||
snprintf(f, sizeof(f), "%s/%s", savedir, name);
|
||
|
||
#elif __BEOS__
|
||
/* BeOS similarly predefines "savedir" as "./userdata"... */
|
||
|
||
if (*name == '\0')
|
||
strcpy(f, savedir);
|
||
else
|
||
snprintf(f, sizeof(f), "%s/%s", savedir, name);
|
||
|
||
#else
|
||
|
||
#ifdef __APPLE__
|
||
/* Macintosh: It's under ~/Library/Application Support/TuxPaint */
|
||
|
||
tux_settings_dir = "Library/Application Support/TuxPaint";
|
||
#else
|
||
/* Linux & Unix: It's under ~/.tuxpaint */
|
||
|
||
tux_settings_dir = ".tuxpaint";
|
||
#endif
|
||
|
||
|
||
/* Put together home directory path + settings directory + filename... */
|
||
|
||
if (savedir == NULL)
|
||
{
|
||
/* Save directory not overridden: */
|
||
|
||
if (getenv("HOME") != NULL)
|
||
{
|
||
if (*name == '\0')
|
||
{
|
||
/* (Some mkdir()'s don't like trailing slashes) */
|
||
|
||
snprintf(f, sizeof(f), "%s/%s", getenv("HOME"), tux_settings_dir);
|
||
}
|
||
else
|
||
{
|
||
snprintf(f, sizeof(f), "%s/%s/%s",
|
||
getenv("HOME"), tux_settings_dir, name);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* WOAH! Don't know where HOME directory is! Last resort, use '.'! */
|
||
|
||
strcpy(f, name);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* User had overridden save directory with "--savedir" option: */
|
||
|
||
if (*name != '\0')
|
||
{
|
||
/* (Some mkdir()'s don't like trailing slashes) */
|
||
|
||
snprintf(f, sizeof(f), "%s/%s", savedir, name);
|
||
}
|
||
else
|
||
{
|
||
snprintf(f, sizeof(f), "%s", savedir);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return strdup(f);
|
||
}
|
||
|
||
|
||
/* Prompt the user with a yes/no question: */
|
||
|
||
static int do_prompt(const char * const text, const char * const btn_yes, const char * const btn_no)
|
||
{
|
||
return(do_prompt_image(text, btn_yes, btn_no, NULL, NULL, NULL));
|
||
}
|
||
|
||
|
||
static int do_prompt_snd(const char * const text, const char * const btn_yes, const char * const btn_no, int snd)
|
||
{
|
||
return(do_prompt_image_flash_snd(text, btn_yes, btn_no, NULL, NULL, NULL, 0, snd));
|
||
}
|
||
|
||
static int do_prompt_image(const char * const text, const char * const btn_yes, const char * const btn_no, SDL_Surface * img1, SDL_Surface * img2, SDL_Surface * img3)
|
||
{
|
||
return(do_prompt_image_snd(text, btn_yes, btn_no, img1, img2, img3, SND_NONE));
|
||
}
|
||
|
||
static int do_prompt_image_snd(const char * const text, const char * const btn_yes, const char * const btn_no, SDL_Surface * img1, SDL_Surface * img2, SDL_Surface * img3, int snd)
|
||
{
|
||
return(do_prompt_image_flash_snd(text, btn_yes, btn_no, img1, img2, img3, 0, snd));
|
||
}
|
||
|
||
static int do_prompt_image_flash(const char * const text, const char * const btn_yes, const char * const btn_no, SDL_Surface * img1, SDL_Surface * img2, SDL_Surface * img3, int animate)
|
||
{
|
||
return(do_prompt_image_flash_snd(text, btn_yes, btn_no, img1, img2, img3, animate, SND_NONE));
|
||
}
|
||
|
||
static int do_prompt_image_flash_snd(const char * const text, const char * const btn_yes, const char * const btn_no, SDL_Surface * img1, SDL_Surface * img2, SDL_Surface * img3, int animate, int snd)
|
||
{
|
||
SDL_Event event;
|
||
SDL_Rect dest;
|
||
int done, ans, w, counter;
|
||
SDL_Color black = {0, 0, 0, 0};
|
||
SDLKey key;
|
||
SDLKey key_y, key_n;
|
||
char *keystr;
|
||
#ifndef NO_PROMPT_SHADOWS
|
||
int i;
|
||
SDL_Surface * alpha_surf;
|
||
#endif
|
||
int img1_w, img2_w, img3_w, max_img_w, img_x, img_y, offset;
|
||
|
||
|
||
/* FIXME: Move elsewhere! Or not?! */
|
||
|
||
keystr = textdir(gettext("Yes"));
|
||
key_y = tolower(keystr[0]);
|
||
free(keystr);
|
||
|
||
keystr = textdir(gettext("No"));
|
||
key_n = tolower(keystr[0]);
|
||
free(keystr);
|
||
|
||
|
||
do_setcursor(cursor_arrow);
|
||
|
||
|
||
/* Move cursor automatically if in keymouse mode: */
|
||
|
||
if (keymouse)
|
||
{
|
||
mouse_x = WINDOW_WIDTH / 2;
|
||
mouse_y = WINDOW_HEIGHT / 2;
|
||
SDL_WarpMouse(mouse_x, mouse_y);
|
||
}
|
||
|
||
|
||
/* Draw button box: */
|
||
|
||
playsound(0, SND_PROMPT, 1);
|
||
playsound(1, snd, 1);
|
||
|
||
for (w = 0; w <= 96; w = w + 4)
|
||
{
|
||
dest.x = 160 + 96 - w + PROMPTOFFSETX;
|
||
dest.y = 94 + 96 - w + PROMPTOFFSETY;
|
||
dest.w = (320 - 96 * 2) + w * 2;
|
||
dest.h = w * 2;
|
||
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
|
||
|
||
SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
|
||
SDL_Delay(10);
|
||
}
|
||
|
||
|
||
#ifndef NO_PROMPT_SHADOWS
|
||
alpha_surf = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
|
||
(320 - 96 * 2) + (w - 4) * 2,
|
||
(w - 4) * 2,
|
||
screen->format->BitsPerPixel,
|
||
screen->format->Rmask,
|
||
screen->format->Gmask,
|
||
screen->format->Bmask,
|
||
screen->format->Amask);
|
||
|
||
if (alpha_surf != NULL)
|
||
{
|
||
SDL_FillRect(alpha_surf, NULL, SDL_MapRGB(alpha_surf->format, 0, 0, 0));
|
||
SDL_SetAlpha(alpha_surf, SDL_SRCALPHA, 64);
|
||
|
||
for (i = 8; i > 0; i = i - 2)
|
||
{
|
||
dest.x = 160 + 96 - (w - 4) + i + PROMPTOFFSETX;
|
||
dest.y = 94 + 96 - (w - 4) + i + PROMPTOFFSETY;
|
||
dest.w = (320 - 96 * 2) + (w - 4) * 2;
|
||
dest.h = (w - 4) * 2;
|
||
|
||
SDL_BlitSurface(alpha_surf, NULL, screen, &dest);
|
||
}
|
||
|
||
SDL_FreeSurface(alpha_surf);
|
||
}
|
||
#endif
|
||
|
||
|
||
w = w - 6;
|
||
|
||
dest.x = 160 + 96 - w + PROMPTOFFSETX;
|
||
dest.y = 94 + 96 - w + PROMPTOFFSETY;
|
||
dest.w = (320 - 96 * 2) + w * 2;
|
||
dest.h = w * 2;
|
||
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
|
||
|
||
|
||
/* If we're showing any images on the right, determine the widest width
|
||
for them: */
|
||
|
||
/* FIXME: This needs to be fixed for right-to-left interfaces! */
|
||
|
||
offset = img1_w = img2_w = img3_w = 0;
|
||
|
||
if (img1 != NULL)
|
||
img1_w = img1->w;
|
||
if (img2 != NULL)
|
||
img2_w = img2->w;
|
||
if (img3 != NULL)
|
||
img3_w = img3->w;
|
||
|
||
max_img_w = max(img1_w, max(img2_w, img3_w));
|
||
|
||
if (max_img_w > 0)
|
||
offset = max_img_w + 8;
|
||
|
||
|
||
/* Draw the question: */
|
||
|
||
wordwrap_text(text, black,
|
||
166 + PROMPTOFFSETX, 100 + PROMPTOFFSETY,
|
||
475 + PROMPTOFFSETX - offset,
|
||
1);
|
||
|
||
|
||
/* Draw the images (if any, and if not animated): */
|
||
|
||
img_x = 457 + PROMPTOFFSETX - offset;
|
||
img_y = 100 + PROMPTOFFSETY + 4;
|
||
|
||
if (img1 != NULL)
|
||
{
|
||
dest.x = img_x + (max_img_w - img1->w) / 2;
|
||
dest.y = img_y;
|
||
|
||
SDL_BlitSurface(img1, NULL, screen, &dest);
|
||
|
||
if (!animate)
|
||
img_y = img_y + img1->h + 4;
|
||
}
|
||
|
||
if (!animate)
|
||
{
|
||
if (img2 != NULL)
|
||
{
|
||
dest.x = img_x + (max_img_w - img2->w) / 2;
|
||
dest.y = img_y;
|
||
|
||
SDL_BlitSurface(img2, NULL, screen, &dest);
|
||
|
||
img_y = img_y + img2->h + 4;
|
||
}
|
||
|
||
if (img3 != NULL)
|
||
{
|
||
dest.x = img_x + (max_img_w - img3->w) / 2;
|
||
dest.y = img_y;
|
||
|
||
SDL_BlitSurface(img3, NULL, screen, &dest);
|
||
|
||
img_y = img_y + img3->h + 4; // unnecessary
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw yes button: */
|
||
|
||
dest.x = 166 + PROMPTOFFSETX;
|
||
dest.y = 178 + PROMPTOFFSETY;
|
||
SDL_BlitSurface(img_yes, NULL, screen, &dest);
|
||
|
||
|
||
/* (Bound to UTF8 domain, so always ask for UTF8 rendering!) */
|
||
|
||
wordwrap_text(btn_yes, black, 166 + PROMPTOFFSETX + 48 + 4,
|
||
183 + PROMPTOFFSETY, 475 + PROMPTOFFSETX, 1);
|
||
|
||
|
||
/* Draw no button: */
|
||
|
||
if (strlen(btn_no) != 0)
|
||
{
|
||
dest.x = 166 + PROMPTOFFSETX;
|
||
dest.y = 230 + PROMPTOFFSETY;
|
||
SDL_BlitSurface(img_no, NULL, screen, &dest);
|
||
|
||
wordwrap_text(btn_no, black,
|
||
166 + PROMPTOFFSETX + 48 + 4, 235 + PROMPTOFFSETY,
|
||
475 + PROMPTOFFSETX, 1);
|
||
}
|
||
|
||
|
||
/* Draw Tux, waiting... */
|
||
|
||
draw_tux_text(TUX_BORED, "", 0);
|
||
|
||
SDL_Flip(screen);
|
||
|
||
done = 0;
|
||
counter = 0;
|
||
ans = 0;
|
||
|
||
do
|
||
{
|
||
while (mySDL_PollEvent(&event))
|
||
{
|
||
if (event.type == SDL_QUIT)
|
||
{
|
||
ans = 0;
|
||
done = 1;
|
||
}
|
||
else if (event.type == SDL_ACTIVEEVENT)
|
||
{
|
||
handle_active(&event);
|
||
}
|
||
else if (event.type == SDL_KEYUP)
|
||
{
|
||
key = event.key.keysym.sym;
|
||
|
||
handle_keymouse(key, SDL_KEYUP);
|
||
}
|
||
else if (event.type == SDL_KEYDOWN)
|
||
{
|
||
key = event.key.keysym.sym;
|
||
|
||
handle_keymouse(key, SDL_KEYDOWN);
|
||
|
||
|
||
/* FIXME: Should use SDLK_{c} instead of '{c}'? How? */
|
||
|
||
if (key == key_y || key == SDLK_RETURN)
|
||
{
|
||
/* Y or ENTER - Yes! */
|
||
|
||
ans = 1;
|
||
done = 1;
|
||
}
|
||
else if (key == key_n || key == SDLK_ESCAPE)
|
||
{
|
||
/* N or ESCAPE - No! */
|
||
|
||
if (strlen(btn_no) != 0)
|
||
{
|
||
ans = 0;
|
||
done = 1;
|
||
}
|
||
else
|
||
{
|
||
if (key == SDLK_ESCAPE)
|
||
{
|
||
/* ESCAPE also simply dismisses if there's no Yes/No
|
||
choice: */
|
||
|
||
ans = 1;
|
||
done = 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
||
valid_click(event.button.button))
|
||
{
|
||
if (event.button.x >= 166 + PROMPTOFFSETX &&
|
||
event.button.x < 166 + PROMPTOFFSETX + 48)
|
||
{
|
||
if (event.button.y >= 178 + PROMPTOFFSETY &&
|
||
event.button.y < 178 + PROMPTOFFSETY + 48)
|
||
{
|
||
ans = 1;
|
||
done = 1;
|
||
}
|
||
else if (strlen(btn_no) != 0 &&
|
||
event.button.y >= 230 + PROMPTOFFSETY &&
|
||
event.button.y < 230 + PROMPTOFFSETY + 48)
|
||
{
|
||
ans = 0;
|
||
done = 1;
|
||
}
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEMOTION)
|
||
{
|
||
if (event.button.x >= 166 + PROMPTOFFSETX &&
|
||
event.button.x < 166 + 48 + PROMPTOFFSETX &&
|
||
((event.button.y >= 178 + PROMPTOFFSETY &&
|
||
event.button.y < 178 + 48 + PROMPTOFFSETY) ||
|
||
(strlen(btn_no) != 0 &&
|
||
event.button.y >= 230 && event.button.y < 230 + 48)))
|
||
{
|
||
do_setcursor(cursor_hand);
|
||
}
|
||
else
|
||
{
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
}
|
||
}
|
||
|
||
SDL_Delay(100);
|
||
|
||
if (animate)
|
||
{
|
||
counter++;
|
||
|
||
if (counter == 5)
|
||
{
|
||
dest.x = img_x + (max_img_w - img2->w) / 2;
|
||
dest.y = img_y;
|
||
|
||
SDL_BlitSurface(img2, NULL, screen, &dest);
|
||
SDL_Flip(screen);
|
||
}
|
||
else if (counter == 10)
|
||
{
|
||
if (img3 != NULL)
|
||
{
|
||
dest.x = img_x + (max_img_w - img3->w) / 2;
|
||
dest.y = img_y;
|
||
|
||
SDL_BlitSurface(img3, NULL, screen, &dest);
|
||
SDL_Flip(screen);
|
||
}
|
||
else
|
||
counter = 15;
|
||
}
|
||
|
||
if (counter == 15)
|
||
{
|
||
dest.x = img_x + (max_img_w - img1->w) / 2;
|
||
dest.y = img_y;
|
||
|
||
SDL_BlitSurface(img1, NULL, screen, &dest);
|
||
SDL_Flip(screen);
|
||
|
||
counter = 0;
|
||
}
|
||
}
|
||
}
|
||
while (!done);
|
||
|
||
|
||
/* FIXME: Sound effect! */
|
||
/* ... */
|
||
|
||
|
||
/* Erase question prompt: */
|
||
|
||
update_canvas(0, 0, canvas->w, canvas->h);
|
||
|
||
return ans;
|
||
}
|
||
|
||
|
||
/* Free memory and prepare to quit: */
|
||
|
||
static void cleanup(void)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < num_stamps; i++)
|
||
{
|
||
#ifndef NOSOUND
|
||
if (stamp_data[i]->ssnd)
|
||
{
|
||
Mix_FreeChunk(stamp_data[i]->ssnd);
|
||
stamp_data[i]->ssnd = NULL;
|
||
}
|
||
#endif
|
||
if (stamp_data[i]->stxt)
|
||
{
|
||
free(stamp_data[i]->stxt);
|
||
stamp_data[i]->stxt = NULL;
|
||
}
|
||
free_surface( &stamp_data[i]->thumbnail );
|
||
|
||
free(stamp_data[i]->stampname);
|
||
free(stamp_data[i]);
|
||
stamp_data[i] = NULL;
|
||
}
|
||
free(stamp_data);
|
||
free_surface( &active_stamp );
|
||
|
||
free_surface_array( img_brushes, num_brushes );
|
||
free_surface_array( img_tools, NUM_TOOLS );
|
||
free_surface_array( img_tool_names, NUM_TOOLS );
|
||
free_surface_array( img_title_names, NUM_TITLES );
|
||
free_surface_array( img_magics, NUM_MAGICS );
|
||
free_surface_array( img_magic_names, NUM_MAGICS );
|
||
free_surface_array( img_shapes, NUM_SHAPES );
|
||
free_surface_array( img_shape_names, NUM_SHAPES );
|
||
free_surface_array( img_tux, NUM_TIP_TUX );
|
||
|
||
free_surface( &img_openlabels_open );
|
||
free_surface( &img_openlabels_erase );
|
||
free_surface( &img_openlabels_back );
|
||
|
||
free_surface( &img_progress );
|
||
|
||
free_surface( &img_yes );
|
||
free_surface( &img_no );
|
||
|
||
free_surface( &img_title_on );
|
||
free_surface( &img_title_off );
|
||
free_surface( &img_title_large_on );
|
||
free_surface( &img_title_large_off );
|
||
|
||
free_surface( &img_open );
|
||
free_surface( &img_erase );
|
||
free_surface( &img_back );
|
||
free_surface( &img_trash );
|
||
|
||
free_surface( &img_dead40x40 );
|
||
|
||
free_surface( &img_printer );
|
||
free_surface( &img_printer_wait );
|
||
free_surface( &img_save_over );
|
||
|
||
free_surface( &img_btn_up );
|
||
free_surface( &img_btn_down );
|
||
free_surface( &img_btn_off );
|
||
|
||
free_surface( &img_cursor_up );
|
||
free_surface( &img_cursor_down );
|
||
|
||
free_surface( &img_cursor_starter_up );
|
||
free_surface( &img_cursor_starter_down );
|
||
|
||
free_surface( &img_scroll_up );
|
||
free_surface( &img_scroll_down );
|
||
free_surface( &img_scroll_up_off );
|
||
free_surface( &img_scroll_down_off );
|
||
|
||
free_surface( &img_grow );
|
||
free_surface( &img_shrink );
|
||
|
||
free_surface( &img_bold );
|
||
free_surface( &img_italic );
|
||
|
||
free_surface( &img_sparkles );
|
||
free_surface( &img_grass );
|
||
|
||
free_surface_array( undo_bufs, NUM_UNDO_BUFS );
|
||
|
||
#ifdef LOW_QUALITY_COLOR_SELECTOR
|
||
free_surface( &img_paintcan );
|
||
#else
|
||
free_surface_array( img_color_btns, NUM_COLORS*2 );
|
||
#endif
|
||
|
||
free_surface( &screen );
|
||
free_surface( &img_starter );
|
||
free_surface( &img_starter_bkgd );
|
||
free_surface( &canvas );
|
||
free_surface( &img_cur_brush );
|
||
|
||
if (medium_font != NULL)
|
||
{
|
||
TTF_CloseFont(medium_font);
|
||
medium_font = NULL;
|
||
}
|
||
|
||
if (small_font != NULL)
|
||
{
|
||
TTF_CloseFont(small_font);
|
||
small_font = NULL;
|
||
}
|
||
|
||
if (large_font != NULL)
|
||
{
|
||
TTF_CloseFont(large_font);
|
||
large_font = NULL;
|
||
}
|
||
|
||
#ifdef FORKED_FONTS
|
||
free(user_font_families); // we'll leak the bodies... oh well
|
||
#else
|
||
for (i = 0; i < num_font_families; i++)
|
||
{
|
||
if (user_font_families[i])
|
||
{
|
||
char ** cpp = user_font_families[i]->filename - 1;
|
||
if (*++cpp)
|
||
free(*cpp);
|
||
if (*++cpp)
|
||
free(*cpp);
|
||
if (*++cpp)
|
||
free(*cpp);
|
||
if (*++cpp)
|
||
free(*cpp);
|
||
if (user_font_families[i]->handle)
|
||
TTF_CloseFont(user_font_families[i]->handle);
|
||
free(user_font_families[i]->directory);
|
||
free(user_font_families[i]->family);
|
||
free(user_font_families[i]);
|
||
user_font_families[i] = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#ifndef NOSOUND
|
||
if (use_sound)
|
||
{
|
||
for (i = 0; i < NUM_SOUNDS; i++)
|
||
{
|
||
if (sounds[i])
|
||
{
|
||
Mix_FreeChunk(sounds[i]);
|
||
sounds[i] = NULL;
|
||
}
|
||
}
|
||
|
||
Mix_CloseAudio();
|
||
}
|
||
#endif
|
||
|
||
|
||
free_cursor(&cursor_hand);
|
||
free_cursor(&cursor_arrow);
|
||
free_cursor(&cursor_watch);
|
||
free_cursor(&cursor_up);
|
||
free_cursor(&cursor_down);
|
||
free_cursor(&cursor_tiny);
|
||
free_cursor(&cursor_crosshair);
|
||
free_cursor(&cursor_brush);
|
||
free_cursor(&cursor_wand);
|
||
free_cursor(&cursor_insertion);
|
||
free_cursor(&cursor_rotate);
|
||
|
||
|
||
/* (Just in case...) */
|
||
|
||
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
||
|
||
|
||
/* Close recording or playback file: */
|
||
|
||
if (demofi != NULL)
|
||
{
|
||
fclose(demofi);
|
||
}
|
||
|
||
|
||
/* If we're using a lockfile, we can 'clear' it when we quit
|
||
(so Tux Paint can be launched again soon, if the user wants to!) */
|
||
|
||
if (ok_to_use_lockfile)
|
||
{
|
||
char * lock_fname;
|
||
time_t zero_time;
|
||
FILE * fi;
|
||
|
||
lock_fname = get_fname("lockfile.dat");
|
||
|
||
zero_time = (time_t) 0;
|
||
|
||
fi = fopen(lock_fname, "w");
|
||
if (fi != NULL)
|
||
{
|
||
/* If we can write to it, do so! */
|
||
|
||
fwrite(&zero_time, sizeof(time_t), 1, fi);
|
||
fclose(fi);
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: I couldn't create the lockfile (%s)\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n", lock_fname, strerror(errno));
|
||
}
|
||
|
||
free(lock_fname);
|
||
}
|
||
|
||
/* Close up! */
|
||
|
||
TTF_Quit();
|
||
SDL_Quit();
|
||
|
||
}
|
||
|
||
|
||
static void free_cursor(SDL_Cursor ** cursor)
|
||
{
|
||
if (*cursor)
|
||
{
|
||
SDL_FreeCursor(*cursor);
|
||
*cursor = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
static void free_surface(SDL_Surface **surface_array)
|
||
{
|
||
if (*surface_array)
|
||
{
|
||
SDL_FreeSurface(*surface_array);
|
||
*surface_array = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
static void free_surface_array(SDL_Surface *surface_array[], int count)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < count; ++i)
|
||
{
|
||
free_surface(&surface_array[i]);
|
||
}
|
||
}
|
||
|
||
|
||
/* Update screen where shape is/was: */
|
||
|
||
// FIXME: unused
|
||
/*
|
||
static void update_shape(int cx, int ox1, int ox2, int cy, int oy1, int oy2, int fix)
|
||
{
|
||
int rx, ry;
|
||
|
||
rx = abs(ox1 - cx);
|
||
if (abs(ox2 - cx) > rx)
|
||
rx = abs(ox2 - cx);
|
||
|
||
ry = abs(oy1 - cy);
|
||
if (abs(oy2 - cy) > ry)
|
||
ry = abs(oy2 - cy);
|
||
|
||
if (fix)
|
||
{
|
||
if (ry > rx)
|
||
rx = ry;
|
||
else
|
||
ry = rx;
|
||
}
|
||
|
||
SDL_UpdateRect(screen, max((cx - rx), 0) + 96, max(cy - ry, 0),
|
||
min((cx + rx) + 96, screen->w),
|
||
min(cy + ry, screen->h));
|
||
}
|
||
*/
|
||
|
||
|
||
/* Draw a shape! */
|
||
|
||
static void do_shape(int cx, int cy, int ox, int oy, int rotn, int use_brush)
|
||
{
|
||
int side, angle_skip, init_ang, rx, ry, rmax, x1, y1, x2, y2, xp, yp,
|
||
old_brush, step;
|
||
float a1, a2, rotn_rad;
|
||
int xx;
|
||
|
||
|
||
/* Determine radius/shape of the shape to draw: */
|
||
|
||
old_brush = 0;
|
||
rx = ox - cx;
|
||
ry = oy - cy;
|
||
|
||
|
||
/* If the shape has a 1:1 ("locked") aspect ratio, use the larger radius: */
|
||
|
||
if (shape_locked[cur_shape])
|
||
{
|
||
if (rx > ry)
|
||
ry = rx;
|
||
else
|
||
rx = ry;
|
||
}
|
||
|
||
|
||
/* Is the shape tiny? Make it SOME size, first! */
|
||
|
||
if (rx < 15 && ry < 15)
|
||
{
|
||
rx = 15;
|
||
ry = 15;
|
||
}
|
||
|
||
|
||
/* Render a default brush: */
|
||
|
||
if (use_brush)
|
||
{
|
||
old_brush = cur_brush;
|
||
cur_brush = 0; /* Kludgy! */
|
||
render_brush();
|
||
}
|
||
|
||
|
||
/* Draw the shape: */
|
||
|
||
angle_skip = 360 / shape_sides[cur_shape];
|
||
|
||
init_ang = shape_init_ang[cur_shape];
|
||
|
||
|
||
step = 1;
|
||
|
||
if (dont_do_xor && !use_brush)
|
||
{
|
||
/* If we're in light outline mode & not drawing the shape with the brush,
|
||
if it has lots of sides (like a circle), reduce the number of sides: */
|
||
|
||
if (shape_sides[cur_shape] > 5)
|
||
step = (shape_sides[cur_shape] / 8);
|
||
}
|
||
|
||
|
||
for (side = 0; side < shape_sides[cur_shape]; side = side + step)
|
||
{
|
||
a1 = (angle_skip * side + init_ang) * M_PI / 180;
|
||
a2 = (angle_skip * (side + 1) + init_ang) * M_PI / 180;
|
||
|
||
x1 = (int) (cos(a1) * rx);
|
||
y1 = (int) (-sin(a1) * ry);
|
||
|
||
x2 = (int) (cos(a2) * rx);
|
||
y2 = (int) (-sin(a2) * ry);
|
||
|
||
|
||
/* Rotate the line: */
|
||
|
||
if (rotn != 0)
|
||
{
|
||
rotn_rad = rotn * M_PI / 180;
|
||
|
||
xp = x1 * cos(rotn_rad) - y1 * sin(rotn_rad);
|
||
yp = x1 * sin(rotn_rad) + y1 * cos(rotn_rad);
|
||
|
||
x1 = xp;
|
||
y1 = yp;
|
||
|
||
xp = x2 * cos(rotn_rad) - y2 * sin(rotn_rad);
|
||
yp = x2 * sin(rotn_rad) + y2 * cos(rotn_rad);
|
||
|
||
x2 = xp;
|
||
y2 = yp;
|
||
}
|
||
|
||
|
||
/* Center the line around the center of the shape: */
|
||
|
||
x1 = x1 + cx;
|
||
y1 = y1 + cy;
|
||
x2 = x2 + cx;
|
||
y2 = y2 + cy;
|
||
|
||
|
||
/* Draw: */
|
||
|
||
if (!use_brush)
|
||
{
|
||
/* (XOR) */
|
||
|
||
line_xor(x1, y1, x2, y2);
|
||
}
|
||
else
|
||
{
|
||
/* Brush */
|
||
|
||
brush_draw(x1, y1, x2, y2, 0);
|
||
}
|
||
}
|
||
|
||
|
||
if (use_brush && shape_filled[cur_shape])
|
||
{
|
||
/* FIXME: In the meantime, we'll do this lame radius-based fill: */
|
||
|
||
for (xx = abs(rx); xx >= 0; xx--)
|
||
{
|
||
for (side = 0; side < shape_sides[cur_shape]; side++)
|
||
{
|
||
a1 = (angle_skip * side + init_ang) * M_PI / 180;
|
||
a2 = (angle_skip * (side + 1) + init_ang) * M_PI / 180;
|
||
|
||
x1 = (int) (cos(a1) * xx);
|
||
y1 = (int) (-sin(a1) * ry);
|
||
|
||
x2 = (int) (cos(a2) * xx);
|
||
y2 = (int) (-sin(a2) * ry);
|
||
|
||
|
||
/* Rotate the line: */
|
||
|
||
if (rotn != 0)
|
||
{
|
||
rotn_rad = rotn * M_PI / 180;
|
||
|
||
xp = x1 * cos(rotn_rad) - y1 * sin(rotn_rad);
|
||
yp = x1 * sin(rotn_rad) + y1 * cos(rotn_rad);
|
||
|
||
x1 = xp;
|
||
y1 = yp;
|
||
|
||
xp = x2 * cos(rotn_rad) - y2 * sin(rotn_rad);
|
||
yp = x2 * sin(rotn_rad) + y2 * cos(rotn_rad);
|
||
|
||
x2 = xp;
|
||
y2 = yp;
|
||
}
|
||
|
||
|
||
/* Center the line around the center of the shape: */
|
||
|
||
x1 = x1 + cx;
|
||
y1 = y1 + cy;
|
||
x2 = x2 + cx;
|
||
y2 = y2 + cy;
|
||
|
||
|
||
/* Draw: */
|
||
|
||
brush_draw(x1, y1, x2, y2, 0);
|
||
}
|
||
|
||
if (xx % 10 == 0)
|
||
update_canvas(0, 0, WINDOW_WIDTH - 96, (48 * 7) + 40 + HEIGHTOFFSET);
|
||
}
|
||
}
|
||
|
||
|
||
/* Update it! */
|
||
|
||
if (use_brush)
|
||
{
|
||
if (abs(rx) > abs(ry))
|
||
rmax = abs(rx) + 20;
|
||
else
|
||
rmax = abs(ry) + 20;
|
||
|
||
update_canvas(cx - rmax, cy - rmax,
|
||
cx + rmax, cy + rmax);
|
||
}
|
||
|
||
|
||
/* Return to normal brush (for paint brush and line tools): */
|
||
|
||
if (use_brush)
|
||
{
|
||
cur_brush = old_brush;
|
||
render_brush();
|
||
}
|
||
}
|
||
|
||
|
||
/* What angle is the mouse away from the center of a shape? */
|
||
|
||
static int rotation(int ctr_x, int ctr_y, int ox, int oy)
|
||
{
|
||
return(atan2(oy - ctr_y, ox - ctr_x) * 180 / M_PI);
|
||
}
|
||
|
||
|
||
/* FIXME: Move elsewhere!!! */
|
||
|
||
#define PROMPT_SAVE_OVER_TXT gettext_noop("Save over the older version of this picture?")
|
||
#define PROMPT_SAVE_OVER_YES gettext_noop("Yes")
|
||
#define PROMPT_SAVE_OVER_NO gettext_noop("No, save a new file")
|
||
|
||
|
||
/* Save the current image: */
|
||
|
||
static int do_save(void)
|
||
{
|
||
int res;
|
||
char * fname;
|
||
char tmp[1024];
|
||
SDL_Surface * thm;
|
||
#ifndef SAVE_AS_BMP
|
||
FILE * fi;
|
||
#endif
|
||
|
||
|
||
/* Was saving completely disabled? */
|
||
|
||
if (disable_save)
|
||
return 0;
|
||
|
||
|
||
if (promptless_save == SAVE_OVER_NO)
|
||
{
|
||
/* Never save over - _always_ save a new file! */
|
||
|
||
get_new_file_id();
|
||
}
|
||
else if (promptless_save == SAVE_OVER_PROMPT)
|
||
{
|
||
/* Saving the same picture? */
|
||
|
||
if (file_id[0] != '\0')
|
||
{
|
||
/* We sure we want to do that? */
|
||
|
||
if (do_prompt_image_snd(PROMPT_SAVE_OVER_TXT,
|
||
PROMPT_SAVE_OVER_YES,
|
||
PROMPT_SAVE_OVER_NO,
|
||
img_save_over, NULL, NULL,
|
||
SND_AREYOUSURE) == 0)
|
||
{
|
||
/* No - Let's save a new picture! */
|
||
|
||
get_new_file_id();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Saving a new picture: */
|
||
|
||
get_new_file_id();
|
||
}
|
||
}
|
||
else if (promptless_save == SAVE_OVER_ALWAYS)
|
||
{
|
||
if (file_id[0] == '\0')
|
||
get_new_file_id();
|
||
}
|
||
|
||
|
||
/* Make sure we have a ~/.tuxpaint directory: */
|
||
|
||
show_progress_bar();
|
||
do_setcursor(cursor_watch);
|
||
|
||
fname = get_fname("");
|
||
|
||
res = mkdir(fname, 0755);
|
||
|
||
if (res != 0 && errno != EEXIST)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't create user data directory:\n"
|
||
"%s\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n", fname, strerror(errno));
|
||
|
||
fprintf(stderr,
|
||
"Cannot save the any pictures! SORRY!\n\n");
|
||
|
||
draw_tux_text(TUX_OOPS, SDL_GetError(), 0);
|
||
|
||
free(fname);
|
||
return 0;
|
||
}
|
||
free(fname);
|
||
|
||
show_progress_bar();
|
||
|
||
|
||
/* Make sure we have a ~/.tuxpaint/saved directory: */
|
||
|
||
fname = get_fname("saved");
|
||
|
||
res = mkdir(fname, 0755);
|
||
|
||
if (res != 0 && errno != EEXIST)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't create user data directory:\n"
|
||
"%s\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n", fname, strerror(errno));
|
||
|
||
fprintf(stderr,
|
||
"Cannot save any pictures! SORRY!\n\n");
|
||
|
||
draw_tux_text(TUX_OOPS, SDL_GetError(), 0);
|
||
|
||
free(fname);
|
||
return 0;
|
||
}
|
||
free(fname);
|
||
|
||
show_progress_bar();
|
||
|
||
|
||
/* Make sure we have a ~/.tuxpaint/saved/.thumbs/ directory: */
|
||
|
||
fname = get_fname("saved/.thumbs");
|
||
|
||
res = mkdir(fname, 0755);
|
||
|
||
if (res != 0 && errno != EEXIST)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't create user data thumbnail directory:\n"
|
||
"%s\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n", fname, strerror(errno));
|
||
|
||
fprintf(stderr,
|
||
"Cannot save any pictures! SORRY!\n\n");
|
||
|
||
draw_tux_text(TUX_OOPS, SDL_GetError(), 0);
|
||
|
||
free(fname);
|
||
return 0;
|
||
}
|
||
free(fname);
|
||
|
||
show_progress_bar();
|
||
|
||
|
||
/* Save the file: */
|
||
|
||
snprintf(tmp, sizeof(tmp), "saved/%s%s", file_id, FNAME_EXTENSION);
|
||
fname = get_fname(tmp);
|
||
debug(fname);
|
||
|
||
#ifdef SAVE_AS_BMP
|
||
if (SDL_SaveBMP(canvas, fname))
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Couldn't save the current image!\n"
|
||
"%s\n"
|
||
"The Simple DirectMedia Layer error that occurred was:\n"
|
||
"%s\n\n", fname, SDL_GetError());
|
||
|
||
draw_tux_text(TUX_OOPS, SDL_GetError(), 0);
|
||
|
||
free(fname);
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
/* Ta-Da! */
|
||
|
||
playsound(0, SND_SAVE, 1);
|
||
draw_tux_text(TUX_DEFAULT, tool_tips[TOOL_SAVE], 1);
|
||
}
|
||
#else
|
||
fi = fopen(fname, "wb");
|
||
if (fi == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Couldn't save the current image!\n"
|
||
"%s\n"
|
||
"The system error that occurred was:\n"
|
||
"%s\n\n",
|
||
fname, strerror(errno));
|
||
|
||
draw_tux_text(TUX_OOPS, strerror(errno), 0);
|
||
}
|
||
else
|
||
{
|
||
if (!do_png_save(fi, fname, canvas))
|
||
{
|
||
free(fname);
|
||
return 0;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
free(fname);
|
||
|
||
show_progress_bar();
|
||
|
||
|
||
/* Save thumbnail, too: */
|
||
|
||
/* (Was thumbnail in old directory, rather than under .thumbs?) */
|
||
|
||
snprintf(tmp, sizeof(tmp), "saved/%s-t%s", file_id, FNAME_EXTENSION);
|
||
fname = get_fname(tmp);
|
||
fi = fopen(fname, "r");
|
||
if (fi != NULL)
|
||
{
|
||
fclose(fi);
|
||
}
|
||
else
|
||
{
|
||
/* No old thumbnail! Save this image's thumbnail in the new place,
|
||
under ".thumbs" */
|
||
|
||
snprintf(tmp, sizeof(tmp), "saved/.thumbs/%s-t%s", file_id, FNAME_EXTENSION);
|
||
fname = get_fname(tmp);
|
||
}
|
||
|
||
debug(fname);
|
||
|
||
thm = thumbnail(canvas, THUMB_W - 20, THUMB_H - 20, 0);
|
||
|
||
fi = fopen(fname, "wb");
|
||
if (fi == NULL)
|
||
{
|
||
fprintf(stderr, "\nError: Couldn't save thumbnail of image!\n"
|
||
"%s\n"
|
||
"The system error that occurred was:\n"
|
||
"%s\n\n",
|
||
fname, strerror(errno));
|
||
}
|
||
else
|
||
{
|
||
do_png_save(fi, fname, thm);
|
||
}
|
||
SDL_FreeSurface(thm);
|
||
|
||
free(fname);
|
||
|
||
|
||
/* Write 'starter' info, if any: */
|
||
|
||
if (starter_id[0] != '\0')
|
||
{
|
||
snprintf(tmp, sizeof(tmp), "saved/%s.dat", file_id);
|
||
fname = get_fname(tmp);
|
||
fi = fopen(fname, "w");
|
||
if (fi != NULL)
|
||
{
|
||
fprintf(fi, "%s\n", starter_id);
|
||
fprintf(fi, "%d %d\n", starter_mirrored, starter_flipped);
|
||
fclose(fi);
|
||
}
|
||
|
||
free(fname);
|
||
}
|
||
|
||
|
||
/* All happy! */
|
||
|
||
playsound(0, SND_SAVE, 1);
|
||
draw_tux_text(TUX_DEFAULT, tool_tips[TOOL_SAVE], 1);
|
||
do_setcursor(cursor_arrow);
|
||
|
||
return 1;
|
||
}
|
||
|
||
|
||
/* Actually save the PNG data to the file stream: */
|
||
static int do_png_save(FILE * fi, const char * const fname, SDL_Surface * surf)
|
||
{
|
||
png_structp png_ptr;
|
||
png_infop info_ptr;
|
||
png_text text_ptr[4];
|
||
unsigned char ** png_rows;
|
||
Uint8 r, g, b;
|
||
int x, y, count;
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int) = getpixels[surf->format->BytesPerPixel];
|
||
|
||
|
||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||
if (png_ptr == NULL)
|
||
{
|
||
fclose(fi);
|
||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
||
|
||
fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
|
||
draw_tux_text(TUX_OOPS, strerror(errno), 0);
|
||
}
|
||
else
|
||
{
|
||
info_ptr = png_create_info_struct(png_ptr);
|
||
if (info_ptr == NULL)
|
||
{
|
||
fclose(fi);
|
||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
||
|
||
fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
|
||
draw_tux_text(TUX_OOPS, strerror(errno), 0);
|
||
}
|
||
else
|
||
{
|
||
if (setjmp(png_jmpbuf(png_ptr)))
|
||
{
|
||
fclose(fi);
|
||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
||
|
||
fprintf(stderr, "\nError: Couldn't save the image!\n%s\n\n", fname);
|
||
draw_tux_text(TUX_OOPS, strerror(errno), 0);
|
||
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
png_init_io(png_ptr, fi);
|
||
|
||
info_ptr->width = surf->w;
|
||
info_ptr->height = surf->h;
|
||
info_ptr->bit_depth = 8;
|
||
info_ptr->color_type = PNG_COLOR_TYPE_RGB;
|
||
info_ptr->interlace_type = 1;
|
||
info_ptr->valid = 0; // will be updated by various png_set_FOO() functions
|
||
|
||
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
|
||
|
||
/* Set headers */
|
||
|
||
count = 0;
|
||
|
||
/*
|
||
if (title != NULL && strlen(title) > 0)
|
||
{
|
||
text_ptr[count].key = "Title";
|
||
text_ptr[count].text = title;
|
||
text_ptr[count].compression = PNG_TEXT_COMPRESSION_NONE;
|
||
count++;
|
||
}
|
||
*/
|
||
|
||
text_ptr[count].key = (png_charp) "Software";
|
||
text_ptr[count].text =
|
||
(png_charp) "Tux Paint " VER_VERSION " (" VER_DATE ")";
|
||
text_ptr[count].compression = PNG_TEXT_COMPRESSION_NONE;
|
||
count++;
|
||
|
||
|
||
png_set_text(png_ptr, info_ptr, text_ptr, count);
|
||
|
||
png_write_info(png_ptr, info_ptr);
|
||
|
||
|
||
|
||
/* Save the picture: */
|
||
|
||
png_rows = malloc(sizeof(char *) * surf->h);
|
||
|
||
for (y = 0; y < surf->h; y++)
|
||
{
|
||
png_rows[y] = malloc(sizeof(char) * 3 * surf->w);
|
||
|
||
for (x = 0; x < surf->w; x++)
|
||
{
|
||
SDL_GetRGB(getpixel(surf, x, y), surf->format, &r, &g, &b);
|
||
|
||
png_rows[y][x * 3 + 0] = r;
|
||
png_rows[y][x * 3 + 1] = g;
|
||
png_rows[y][x * 3 + 2] = b;
|
||
}
|
||
}
|
||
|
||
png_write_image(png_ptr, png_rows);
|
||
|
||
for (y = 0; y < surf->h; y++)
|
||
free(png_rows[y]);
|
||
|
||
free(png_rows);
|
||
|
||
|
||
png_write_end(png_ptr, NULL);
|
||
|
||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||
fclose(fi);
|
||
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
///////////////////////////////////// PostScript printing ///////////
|
||
#ifdef PRINTMETHOD_PS
|
||
|
||
/* Actually save the PostScript data to the file stream: */
|
||
static int do_ps_save(FILE * fi, const char *restrict const fname, SDL_Surface * surf)
|
||
{
|
||
unsigned char *restrict const ps_row = malloc(surf->w * 3);
|
||
int x, y;
|
||
char buf[256];
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int) = getpixels[surf->format->BytesPerPixel];
|
||
time_t t = time(NULL);
|
||
|
||
fprintf(fi, "%%!PS-Adobe-3.0 EPSF-3.0\n"); // probably broken, but close enough maybe
|
||
fprintf(fi, "%%%%Title: (%s)\n", fname);
|
||
strftime(buf, sizeof buf - 1, "%a %b %e %H:%M:%S %Y", localtime(&t));
|
||
fprintf(fi, "%%%%CreationDate: (%s)\n", buf);
|
||
fprintf(fi, "%%%%Creator: (Tux Paint " VER_VERSION ", " VER_DATE ")\n");
|
||
fprintf(fi, "%%%%LanguageLevel: 2\n");
|
||
// fprintf(fi, "%%%%BoundingBox: 72 214 540 578\n"); // doubt we have the needed info
|
||
fprintf(fi, "%%%%DocumentData: Binary\n");
|
||
fprintf(fi, "%%%%EndComments\n");
|
||
fprintf(fi, "\n");
|
||
fprintf(fi, "gsave\n");
|
||
fprintf(fi, "\n");
|
||
// fprintf(fi, "90 rotate\n"); // landscape mode
|
||
fprintf(fi, "%% First, grab the page size.\n");
|
||
fprintf(fi, "gsave\n");
|
||
fprintf(fi, " clippath\n");
|
||
fprintf(fi, " pathbbox\n");
|
||
fprintf(fi, "grestore\n");
|
||
fprintf(fi, "/ury exch def\n");
|
||
fprintf(fi, "/urx exch def\n");
|
||
fprintf(fi, "/lly exch def\n");
|
||
fprintf(fi, "/llx exch def\n");
|
||
fprintf(fi, "\n");
|
||
fprintf(fi, "llx lly translate\n");
|
||
fprintf(fi, "\n");
|
||
fprintf(fi, "/width %u def\n", surf->w);
|
||
fprintf(fi, "/height %u def\n", surf->h);
|
||
fprintf(fi, "width height scale\n");
|
||
fprintf(fi, "\n");
|
||
fprintf(fi, "urx llx sub width div\n");
|
||
fprintf(fi, "ury lly sub height div\n");
|
||
fprintf(fi, "%% now do a 'min' operation\n");
|
||
fprintf(fi, "2 copy gt { exch } if pop\n");
|
||
fprintf(fi, "\n");
|
||
fprintf(fi, "dup scale\n");
|
||
fprintf(fi, "/DeviceRGB setcolorspace\n");
|
||
fprintf(fi, "<<\n");
|
||
fprintf(fi, " /ImageType 1\n");
|
||
fprintf(fi, " /Width width /Height height\n");
|
||
fprintf(fi, " /BitsPerComponent 8\n");
|
||
fprintf(fi, " /ImageMatrix [width 0 0 height neg 0 height]\n");
|
||
fprintf(fi, " /Decode [0 1 0 1 0 1]\n");
|
||
fprintf(fi, " /DataSource currentfile\n");
|
||
fprintf(fi, ">>\n");
|
||
fprintf(fi, "%%%%BeginData: %u Binary Bytes\n", surf->w * surf->h * 3u);
|
||
fprintf(fi, "image\n");
|
||
|
||
/* Save the picture: */
|
||
for (y = 0; y < surf->h; y++)
|
||
{
|
||
for (x = 0; x < surf->w; x++)
|
||
{
|
||
Uint8 r, g, b;
|
||
SDL_GetRGB(getpixel(surf, x, y), surf->format, &r, &g, &b);
|
||
ps_row[x * 3 + 0] = r;
|
||
ps_row[x * 3 + 1] = g;
|
||
ps_row[x * 3 + 2] = b;
|
||
}
|
||
fwrite(ps_row,surf->w,3,fi);
|
||
}
|
||
free(ps_row);
|
||
|
||
fprintf(fi, "\n");
|
||
fprintf(fi, "%%%%EndData\n");
|
||
fprintf(fi, "grestore\n");
|
||
fprintf(fi, "showpage\n");
|
||
fprintf(fi, "%%%%EOF\n");
|
||
|
||
fclose(fi);
|
||
return 1;
|
||
}
|
||
|
||
#endif
|
||
/////////////////////////////////////////////////////////////////
|
||
|
||
/* Pick a new file ID: */
|
||
static void get_new_file_id(void)
|
||
{
|
||
time_t t;
|
||
|
||
t = time(NULL);
|
||
|
||
strftime(file_id, sizeof(file_id), "%Y%m%d%H%M%S", localtime(&t));
|
||
debug(file_id);
|
||
|
||
|
||
/* FIXME: Show thumbnail and prompt for title: */
|
||
}
|
||
|
||
|
||
/* Handle quitting (and prompting to save, if necessary!) */
|
||
|
||
static int do_quit(void)
|
||
{
|
||
int done;
|
||
|
||
done = do_prompt_snd(PROMPT_QUIT_TXT,
|
||
PROMPT_QUIT_YES,
|
||
PROMPT_QUIT_NO,
|
||
SND_AREYOUSURE);
|
||
|
||
if (done && !been_saved && !disable_save)
|
||
{
|
||
if (do_prompt(PROMPT_QUIT_SAVE_TXT,
|
||
PROMPT_QUIT_SAVE_YES,
|
||
PROMPT_QUIT_SAVE_NO))
|
||
{
|
||
if (do_save())
|
||
{
|
||
do_prompt(tool_tips[TOOL_SAVE],
|
||
"OK",
|
||
"");
|
||
}
|
||
else
|
||
{
|
||
/* Couldn't save! Abort quit! */
|
||
|
||
done = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
return(done);
|
||
}
|
||
|
||
|
||
|
||
/* Open a saved image: */
|
||
|
||
#define PLACE_STARTERS_DIR 0
|
||
#define PLACE_SAVED_DIR 1
|
||
#define NUM_PLACES_TO_LOOK 2
|
||
|
||
|
||
void do_open(void)
|
||
{
|
||
SDL_Surface * img, * img1, * img2;
|
||
int things_alloced;
|
||
SDL_Surface * * thumbs = NULL;
|
||
DIR * d;
|
||
struct dirent * f;
|
||
struct dirent2 * fs;
|
||
int place;
|
||
char * dirname[NUM_PLACES_TO_LOOK];
|
||
char * rfname;
|
||
char * * d_names = NULL, * * d_exts = NULL;
|
||
int * d_places;
|
||
FILE * fi;
|
||
char fname[1024];
|
||
char * tmp_fname;
|
||
int num_files, i, done, update_list, want_erase, cur, which,
|
||
num_files_in_dirs, j, res;
|
||
SDL_Rect dest;
|
||
SDL_Event event;
|
||
SDLKey key;
|
||
Uint32 last_click_time;
|
||
int last_click_which, last_click_button;
|
||
int places_to_look;
|
||
|
||
do_setcursor(cursor_watch);
|
||
|
||
/* Allocate some space: */
|
||
|
||
things_alloced = 32;
|
||
|
||
fs = (struct dirent2 *) malloc(sizeof(struct dirent2) * things_alloced);
|
||
|
||
num_files = 0;
|
||
cur = 0;
|
||
which = 0;
|
||
num_files_in_dirs = 0;
|
||
|
||
|
||
/* Open directories of images: */
|
||
|
||
for (places_to_look = 0;
|
||
places_to_look < NUM_PLACES_TO_LOOK;
|
||
places_to_look++)
|
||
{
|
||
if (places_to_look == PLACE_STARTERS_DIR)
|
||
{
|
||
/* Check for coloring-book style 'starter' images first: */
|
||
|
||
dirname[places_to_look] = strdup(DATA_PREFIX "starters");
|
||
}
|
||
else
|
||
{
|
||
/* Then check for saved-images: */
|
||
|
||
dirname[places_to_look] = get_fname("saved");
|
||
}
|
||
|
||
|
||
/* Read directory of images and build thumbnails: */
|
||
|
||
d = opendir(dirname[places_to_look]);
|
||
|
||
if (d != NULL)
|
||
{
|
||
/* Gather list of files (for sorting): */
|
||
|
||
do
|
||
{
|
||
f = readdir(d);
|
||
|
||
if (f != NULL)
|
||
{
|
||
memcpy(&(fs[num_files_in_dirs].f), f, sizeof(struct dirent));
|
||
fs[num_files_in_dirs].place = places_to_look;
|
||
|
||
num_files_in_dirs++;
|
||
|
||
if (num_files_in_dirs >= things_alloced)
|
||
{
|
||
things_alloced = things_alloced + 32;
|
||
fs = (struct dirent2 *) realloc(fs,
|
||
sizeof(struct dirent2) *
|
||
things_alloced);
|
||
}
|
||
}
|
||
}
|
||
while (f != NULL);
|
||
|
||
closedir(d);
|
||
}
|
||
}
|
||
|
||
|
||
/* (Re)allocate space for the information about these files: */
|
||
|
||
thumbs = (SDL_Surface * *) malloc(sizeof(SDL_Surface *) *
|
||
num_files_in_dirs);
|
||
d_places = (int *) malloc(sizeof(int) * num_files_in_dirs);
|
||
d_names = (char * *) malloc(sizeof(char *) * num_files_in_dirs);
|
||
d_exts = (char * *) malloc(sizeof(char *) * num_files_in_dirs);
|
||
|
||
|
||
/* Sort: */
|
||
|
||
qsort(fs, num_files_in_dirs, sizeof(struct dirent2),
|
||
(int(*)(const void *, const void *))compare_dirent2s);
|
||
|
||
|
||
/* Read directory of images and build thumbnails: */
|
||
|
||
for (j = 0; j < num_files_in_dirs; j++)
|
||
{
|
||
f = &(fs[j].f);
|
||
place = fs[j].place;
|
||
|
||
show_progress_bar();
|
||
|
||
if (f != NULL)
|
||
{
|
||
debug(f->d_name);
|
||
|
||
if (strcasestr(f->d_name, "-t.") == NULL &&
|
||
strcasestr(f->d_name, "-back.") == NULL)
|
||
{
|
||
if (strcasestr(f->d_name, FNAME_EXTENSION) != NULL
|
||
#ifndef SAVE_AS_BMP
|
||
/* Support legacy BMP files for load: */
|
||
|
||
|| strcasestr(f->d_name, ".bmp") != NULL
|
||
#endif
|
||
)
|
||
{
|
||
strcpy(fname, f->d_name);
|
||
if (strcasestr(fname, FNAME_EXTENSION) != NULL)
|
||
{
|
||
strcpy(strcasestr(fname, FNAME_EXTENSION), "");
|
||
d_exts[num_files] = strdup(FNAME_EXTENSION);
|
||
}
|
||
|
||
#ifndef SAVE_AS_BMP
|
||
if (strcasestr(fname, ".bmp") != NULL)
|
||
{
|
||
strcpy(strcasestr(fname, ".bmp"), "");
|
||
d_exts[num_files] = strdup(".bmp");
|
||
}
|
||
#endif
|
||
|
||
d_names[num_files] = strdup(fname);
|
||
d_places[num_files] = place;
|
||
|
||
|
||
/* Is it the 'current' file we just loaded?
|
||
We'll make it the current selection! */
|
||
|
||
if (strcmp(d_names[num_files], file_id) == 0)
|
||
{
|
||
which = num_files;
|
||
cur = (which / 4) * 4;
|
||
|
||
/* Center the cursor (useful for when the last item is
|
||
selected first!) */
|
||
|
||
if (cur - 8 >= 0)
|
||
cur = cur - 8;
|
||
else if (cur - 4 >= 0)
|
||
cur = cur - 4;
|
||
}
|
||
|
||
|
||
/* Try to load thumbnail first: */
|
||
|
||
snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png",
|
||
dirname[d_places[num_files]], d_names[num_files]);
|
||
debug(fname);
|
||
img = IMG_Load(fname);
|
||
|
||
if (img == NULL)
|
||
{
|
||
/* No thumbnail in the new location ("saved/.thumbs"),
|
||
try the old locatin ("saved/"): */
|
||
|
||
snprintf(fname, sizeof(fname), "%s/%s-t.png",
|
||
dirname[d_places[num_files]],
|
||
d_names[num_files]);
|
||
debug(fname);
|
||
|
||
img = IMG_Load(fname);
|
||
}
|
||
|
||
if (img != NULL)
|
||
{
|
||
/* Loaded the thumbnail from one or the other location */
|
||
show_progress_bar();
|
||
|
||
img1 = SDL_DisplayFormat(img);
|
||
SDL_FreeSurface(img);
|
||
|
||
// if too big, or too small in both dimensions, rescale it
|
||
// ( for now: using old thumbnail as source for high speed, low quality)
|
||
if (img1->w > THUMB_W-20 || img1->h > THUMB_H-20 || (img1->w < THUMB_W-20 && img1->h < THUMB_H-20) )
|
||
{
|
||
img2 = thumbnail(img1, THUMB_W - 20, THUMB_H - 20, 0);
|
||
SDL_FreeSurface(img1);
|
||
img1 = img2;
|
||
}
|
||
|
||
thumbs[num_files] = img1;
|
||
|
||
if (thumbs[num_files] == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Couldn't create a thumbnail of "
|
||
"saved image!\n"
|
||
"%s\n", fname);
|
||
}
|
||
|
||
num_files++;
|
||
}
|
||
else
|
||
{
|
||
/* No thumbnail - load original: */
|
||
/* (Make sure we have a .../saved/.thumbs/ directory:) */
|
||
|
||
tmp_fname = get_fname("saved/.thumbs");
|
||
|
||
res = mkdir(tmp_fname, 0755);
|
||
|
||
if (res != 0 && errno != EEXIST)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Can't create user data thumbnail directory:\n"
|
||
"%s\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n", tmp_fname, strerror(errno));
|
||
}
|
||
|
||
free(tmp_fname);
|
||
|
||
|
||
img = NULL;
|
||
|
||
if (d_places[num_files] == PLACE_STARTERS_DIR)
|
||
{
|
||
/* Try to load a starter's background image, first!
|
||
If it exists, it should give a better idea of what the
|
||
starter looks like, compared to the overlay image... */
|
||
|
||
/* (Try JPEG first) */
|
||
snprintf(fname, sizeof(fname), "%s/%s-back.jpeg",
|
||
dirname[d_places[num_files]],
|
||
d_names[num_files]);
|
||
|
||
img = IMG_Load(fname);
|
||
|
||
|
||
if (img == NULL)
|
||
{
|
||
/* (Try PNG next) */
|
||
snprintf(fname, sizeof(fname), "%s/%s-back.png",
|
||
dirname[d_places[num_files]],
|
||
d_names[num_files]);
|
||
|
||
img = IMG_Load(fname);
|
||
}
|
||
}
|
||
|
||
|
||
if (img == NULL)
|
||
{
|
||
/* Didn't load a starter background (or didn't try!),
|
||
try loading the actual image... */
|
||
|
||
snprintf(fname, sizeof(fname), "%s/%s",
|
||
dirname[d_places[num_files]], f->d_name);
|
||
debug(fname);
|
||
#ifdef SAVE_AS_BMP
|
||
img = SDL_LoadBMP(fname);
|
||
#else
|
||
img = IMG_Load(fname);
|
||
#endif
|
||
}
|
||
|
||
|
||
show_progress_bar();
|
||
|
||
if (img == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: I can't open one of the saved files!\n"
|
||
"%s\n"
|
||
"The Simple DirectMedia Layer error that "
|
||
"occurred was:\n"
|
||
"%s\n\n",
|
||
fname, SDL_GetError());
|
||
|
||
free(d_names[num_files]);
|
||
free(d_exts[num_files]);
|
||
}
|
||
else
|
||
{
|
||
/* Turn it into a thumbnail: */
|
||
|
||
img1 = SDL_DisplayFormat(img);
|
||
img2 = thumbnail(img1, THUMB_W - 20, THUMB_H - 20, 0);
|
||
SDL_FreeSurface(img1);
|
||
|
||
show_progress_bar();
|
||
|
||
thumbs[num_files] = SDL_DisplayFormat(img2);
|
||
SDL_FreeSurface(img2);
|
||
if (thumbs[num_files] == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Couldn't create a thumbnail of "
|
||
"saved image!\n"
|
||
"%s\n", fname);
|
||
}
|
||
|
||
SDL_FreeSurface(img);
|
||
|
||
show_progress_bar();
|
||
|
||
|
||
/* Let's save this thumbnail, so we don't have to
|
||
create it again next time 'Open' is called: */
|
||
|
||
if (d_places[num_files] == PLACE_SAVED_DIR)
|
||
{
|
||
debug("Saving thumbnail for this one!");
|
||
|
||
snprintf(fname, sizeof(fname), "%s/.thumbs/%s-t.png",
|
||
dirname[d_places[num_files]], d_names[num_files]);
|
||
|
||
fi = fopen(fname, "wb");
|
||
if (fi == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nError: Couldn't save thumbnail of "
|
||
"saved image!\n"
|
||
"%s\n"
|
||
"The error that occurred was:\n"
|
||
"%s\n\n",
|
||
fname, strerror(errno));
|
||
}
|
||
else
|
||
{
|
||
do_png_save(fi, fname, thumbs[num_files]);
|
||
}
|
||
|
||
show_progress_bar();
|
||
}
|
||
|
||
|
||
num_files++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* It was a thumbnail file ("...-t.png") or immutable scene starter's
|
||
overlay layer ("...-front.png") */
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
#ifdef DEBUG
|
||
printf("%d saved files were found!\n", num_files);
|
||
#endif
|
||
|
||
|
||
|
||
if (num_files == 0)
|
||
{
|
||
do_prompt_snd(PROMPT_OPEN_NOFILES_TXT, PROMPT_OPEN_NOFILES_YES, "",
|
||
SND_YOUCANNOT);
|
||
}
|
||
else
|
||
{
|
||
/* Let user choose an image: */
|
||
|
||
char *freeme =
|
||
textdir(gettext_noop("Choose the picture you want, "
|
||
"then click “Open”."));
|
||
draw_tux_text(TUX_BORED,freeme, 1);
|
||
free(freeme);
|
||
|
||
/* NOTE: cur is now set above; if file_id'th file is found, it's
|
||
set to that file's index; otherwise, we default to '0' */
|
||
|
||
update_list = 1;
|
||
want_erase = 0;
|
||
|
||
done = 0;
|
||
|
||
last_click_which = -1;
|
||
last_click_time = 0;
|
||
last_click_button = -1;
|
||
|
||
|
||
do_setcursor(cursor_arrow);
|
||
|
||
|
||
do
|
||
{
|
||
/* Update screen: */
|
||
|
||
if (update_list)
|
||
{
|
||
/* Erase: */
|
||
|
||
dest.x = 96;
|
||
dest.y = 0;
|
||
dest.w = WINDOW_WIDTH - 96 - 96;
|
||
dest.h = 48 * 7 + 40 + HEIGHTOFFSET;
|
||
|
||
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format,
|
||
255, 255, 255));
|
||
|
||
|
||
/* Draw icons: */
|
||
|
||
for (i = cur; i < cur + 16 && i < num_files; i++)
|
||
{
|
||
/* Draw cursor: */
|
||
|
||
dest.x = THUMB_W * ((i - cur) % 4) + 96;
|
||
dest.y = THUMB_H * ((i - cur) / 4) + 24;
|
||
|
||
if (d_places[i] == PLACE_SAVED_DIR)
|
||
{
|
||
if (i == which)
|
||
{
|
||
SDL_BlitSurface(img_cursor_down, NULL, screen, &dest);
|
||
debug(d_names[i]);
|
||
}
|
||
else
|
||
SDL_BlitSurface(img_cursor_up, NULL, screen, &dest);
|
||
}
|
||
else
|
||
{
|
||
if (i == which)
|
||
{
|
||
SDL_BlitSurface(img_cursor_starter_down, NULL, screen, &dest);
|
||
debug(d_names[i]);
|
||
}
|
||
else
|
||
SDL_BlitSurface(img_cursor_starter_up, NULL, screen, &dest);
|
||
}
|
||
|
||
|
||
|
||
dest.x = THUMB_W * ((i - cur) % 4) + 96 + 10 +
|
||
(THUMB_W - 20 - thumbs[i]->w) / 2;
|
||
dest.y = THUMB_H * ((i - cur) / 4) + 24 + 10 +
|
||
(THUMB_H - 20 - thumbs[i]->h) / 2;
|
||
|
||
if (thumbs[i] != NULL)
|
||
SDL_BlitSurface(thumbs[i], NULL, screen, &dest);
|
||
}
|
||
|
||
|
||
/* Draw arrows: */
|
||
|
||
dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
|
||
dest.y = 0;
|
||
|
||
if (cur > 0)
|
||
SDL_BlitSurface(img_scroll_up, NULL, screen, &dest);
|
||
else
|
||
SDL_BlitSurface(img_scroll_up_off, NULL, screen, &dest);
|
||
|
||
dest.x = (WINDOW_WIDTH - img_scroll_up->w) / 2;
|
||
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48;
|
||
|
||
if (cur < num_files - 16)
|
||
SDL_BlitSurface(img_scroll_down, NULL, screen, &dest);
|
||
else
|
||
SDL_BlitSurface(img_scroll_down_off, NULL, screen, &dest);
|
||
|
||
|
||
/* "Open" button: */
|
||
|
||
dest.x = 96;
|
||
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48;
|
||
SDL_BlitSurface(img_open, NULL, screen, &dest);
|
||
|
||
dest.x = 96 + (48 - img_openlabels_open->w) / 2;
|
||
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - img_openlabels_open->h;
|
||
SDL_BlitSurface(img_openlabels_open, NULL, screen, &dest);
|
||
|
||
|
||
/* "Back" button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 96 - 48;
|
||
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48;
|
||
SDL_BlitSurface(img_back, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 96 - 48 + (48 - img_openlabels_back->w) / 2;
|
||
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - img_openlabels_back->h;
|
||
SDL_BlitSurface(img_openlabels_back, NULL, screen, &dest);
|
||
|
||
|
||
/* "Erase" button: */
|
||
|
||
dest.x = WINDOW_WIDTH - 96 - 48 - 48;
|
||
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48;
|
||
|
||
if (d_places[which] != PLACE_STARTERS_DIR)
|
||
SDL_BlitSurface(img_erase, NULL, screen, &dest);
|
||
else
|
||
SDL_BlitSurface(img_btn_off, NULL, screen, &dest);
|
||
|
||
dest.x = WINDOW_WIDTH - 96 - 48 - 48 + (48 - img_openlabels_erase->w) / 2;
|
||
dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - img_openlabels_erase->h;
|
||
SDL_BlitSurface(img_openlabels_erase, NULL, screen, &dest);
|
||
|
||
|
||
SDL_Flip(screen);
|
||
|
||
update_list = 0;
|
||
}
|
||
|
||
|
||
mySDL_WaitEvent(&event);
|
||
|
||
if (event.type == SDL_QUIT)
|
||
{
|
||
done = 1;
|
||
|
||
/* FIXME: Handle SDL_Quit better */
|
||
}
|
||
else if (event.type == SDL_ACTIVEEVENT)
|
||
{
|
||
handle_active(&event);
|
||
}
|
||
else if (event.type == SDL_KEYUP)
|
||
{
|
||
key = event.key.keysym.sym;
|
||
|
||
handle_keymouse(key, SDL_KEYUP);
|
||
}
|
||
else if (event.type == SDL_KEYDOWN)
|
||
{
|
||
key = event.key.keysym.sym;
|
||
|
||
handle_keymouse(key, SDL_KEYDOWN);
|
||
|
||
if (key == SDLK_LEFT)
|
||
{
|
||
if (which > 0)
|
||
{
|
||
which--;
|
||
|
||
if (which < cur)
|
||
cur = cur - 4;
|
||
|
||
update_list = 1;
|
||
}
|
||
}
|
||
else if (key == SDLK_RIGHT)
|
||
{
|
||
if (which < num_files - 1)
|
||
{
|
||
which++;
|
||
|
||
if (which >= cur + 16)
|
||
cur = cur + 4;
|
||
|
||
update_list = 1;
|
||
}
|
||
}
|
||
else if (key == SDLK_UP)
|
||
{
|
||
if (which >= 0)
|
||
{
|
||
which = which - 4;
|
||
|
||
if (which < 0)
|
||
which = 0;
|
||
|
||
if (which < cur)
|
||
cur = cur - 4;
|
||
|
||
update_list = 1;
|
||
}
|
||
}
|
||
else if (key == SDLK_DOWN)
|
||
{
|
||
if (which < num_files)
|
||
{
|
||
which = which + 4;
|
||
|
||
if (which >= num_files)
|
||
which = num_files - 1;
|
||
|
||
if (which >= cur + 16)
|
||
cur = cur + 4;
|
||
|
||
update_list = 1;
|
||
}
|
||
}
|
||
else if (key == SDLK_RETURN || key == SDLK_SPACE)
|
||
{
|
||
/* Open */
|
||
|
||
done = 1;
|
||
playsound(1, SND_CLICK, 1);
|
||
}
|
||
else if (key == SDLK_ESCAPE)
|
||
{
|
||
/* Go back: */
|
||
|
||
which = -1;
|
||
done = 1;
|
||
playsound(1, SND_CLICK, 1);
|
||
}
|
||
else if (key == SDLK_d &&
|
||
(event.key.keysym.mod & KMOD_CTRL) &&
|
||
d_places[which] != PLACE_STARTERS_DIR &&
|
||
!noshortcuts)
|
||
{
|
||
/* Delete! */
|
||
|
||
want_erase = 1;
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEBUTTONDOWN &&
|
||
event.button.button >= 1)
|
||
{
|
||
if (event.button.x >= 96 && event.button.x < WINDOW_WIDTH - 96 &&
|
||
event.button.y >= 24 &&
|
||
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET - 48))
|
||
{
|
||
/* Picked an icon! */
|
||
|
||
which = ((event.button.x - 96) / (THUMB_W) +
|
||
(((event.button.y - 24) / THUMB_H) * 4)) + cur;
|
||
|
||
if (which < num_files)
|
||
{
|
||
playsound(1, SND_BLEEP, 1);
|
||
update_list = 1;
|
||
|
||
|
||
if (which == last_click_which &&
|
||
SDL_GetTicks() < last_click_time + 1000 &&
|
||
event.button.button == last_click_button)
|
||
{
|
||
/* Double-click! */
|
||
|
||
done = 1;
|
||
}
|
||
|
||
last_click_which = which;
|
||
last_click_time = SDL_GetTicks();
|
||
last_click_button = event.button.button;
|
||
}
|
||
}
|
||
else if (event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
|
||
event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2)
|
||
{
|
||
if (event.button.y < 24)
|
||
{
|
||
/* Up scroll button: */
|
||
|
||
if (cur > 0)
|
||
{
|
||
cur = cur - 4;
|
||
update_list = 1;
|
||
playsound(1, SND_SCROLL, 1);
|
||
|
||
if (cur == 0)
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
|
||
if (which > cur + 16)
|
||
which = which - 4;
|
||
}
|
||
else if (event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET - 48) &&
|
||
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET - 24))
|
||
{
|
||
/* Down scroll button: */
|
||
|
||
if (cur < num_files - 16)
|
||
{
|
||
cur = cur + 4;
|
||
update_list = 1;
|
||
playsound(1, SND_SCROLL, 1);
|
||
|
||
if (cur >= num_files - 16)
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
|
||
if (which < cur)
|
||
which = which + 4;
|
||
}
|
||
}
|
||
else if (event.button.x >= 96 && event.button.x < 96 + 48 &&
|
||
event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
||
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET))
|
||
{
|
||
/* Open */
|
||
|
||
done = 1;
|
||
playsound(1, SND_CLICK, 1);
|
||
}
|
||
else if (event.button.x >= (WINDOW_WIDTH - 96 - 48) &&
|
||
event.button.x < (WINDOW_WIDTH - 96) &&
|
||
event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
||
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET))
|
||
{
|
||
/* Back */
|
||
|
||
which = -1;
|
||
done = 1;
|
||
playsound(1, SND_CLICK, 1);
|
||
}
|
||
else if (event.button.x >= (WINDOW_WIDTH - 96 - 48 - 48) &&
|
||
event.button.x < (WINDOW_WIDTH - 48 - 96) &&
|
||
event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
||
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET) &&
|
||
d_places[which] != PLACE_STARTERS_DIR)
|
||
{
|
||
/* Erase */
|
||
|
||
want_erase = 1;
|
||
}
|
||
}
|
||
else if (event.type == SDL_MOUSEMOTION)
|
||
{
|
||
/* Deal with mouse pointer shape! */
|
||
|
||
if (event.button.y < 24 &&
|
||
event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
|
||
event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 &&
|
||
cur > 0)
|
||
{
|
||
/* Scroll up button: */
|
||
|
||
do_setcursor(cursor_up);
|
||
}
|
||
else if (event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET - 48) &&
|
||
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET - 24) &&
|
||
event.button.x >= (WINDOW_WIDTH - img_scroll_up->w) / 2 &&
|
||
event.button.x <= (WINDOW_WIDTH + img_scroll_up->w) / 2 &&
|
||
cur < num_files - 16)
|
||
{
|
||
/* Scroll down button: */
|
||
|
||
do_setcursor(cursor_down);
|
||
}
|
||
else if (((event.button.x >= 96 && event.button.x < 96 + 48) ||
|
||
(event.button.x >= (WINDOW_WIDTH - 96 - 48) &&
|
||
event.button.x < (WINDOW_WIDTH - 96)) ||
|
||
(event.button.x >= (WINDOW_WIDTH - 96 - 48 - 48) &&
|
||
event.button.x < (WINDOW_WIDTH - 48 - 96) &&
|
||
d_places[which] != PLACE_STARTERS_DIR)) &&
|
||
event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
||
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET))
|
||
{
|
||
/* One of the command buttons: */
|
||
|
||
do_setcursor(cursor_hand);
|
||
}
|
||
else if (event.button.x >= 96 && event.button.x < WINDOW_WIDTH - 96 &&
|
||
event.button.y > 24 &&
|
||
event.button.y < (48 * 7 + 40 + HEIGHTOFFSET) - 48 &&
|
||
((((event.button.x - 96) / (THUMB_W) +
|
||
(((event.button.y - 24) / THUMB_H) * 4)) +
|
||
cur) < num_files))
|
||
{
|
||
/* One of the thumbnails: */
|
||
|
||
do_setcursor(cursor_hand);
|
||
}
|
||
else
|
||
{
|
||
/* Unclickable... */
|
||
|
||
do_setcursor(cursor_arrow);
|
||
}
|
||
}
|
||
|
||
|
||
if (want_erase)
|
||
{
|
||
want_erase = 0;
|
||
|
||
if (do_prompt_image_snd(PROMPT_ERASE_TXT,
|
||
PROMPT_ERASE_YES, PROMPT_ERASE_NO,
|
||
thumbs[which],
|
||
img_popup_arrow,
|
||
img_trash,
|
||
SND_AREYOUSURE))
|
||
{
|
||
snprintf(fname, sizeof(fname), "saved/%s%s",
|
||
d_names[which], d_exts[which]);
|
||
|
||
rfname = get_fname(fname);
|
||
debug(rfname);
|
||
|
||
if (unlink(rfname) == 0)
|
||
{
|
||
update_list = 1;
|
||
|
||
|
||
/* Delete the thumbnail, too: */
|
||
|
||
snprintf(fname, sizeof(fname),
|
||
"saved/.thumbs/%s-t.png",
|
||
d_names[which]);
|
||
|
||
free(rfname);
|
||
rfname = get_fname(fname);
|
||
debug(rfname);
|
||
|
||
unlink(rfname);
|
||
|
||
|
||
/* Try deleting old-style thumbnail, too: */
|
||
|
||
unlink(rfname);
|
||
snprintf(fname, sizeof(fname), "saved/%s-t.png",
|
||
d_names[which]);
|
||
|
||
free(rfname);
|
||
rfname = get_fname(fname);
|
||
debug(rfname);
|
||
|
||
unlink(rfname);
|
||
|
||
|
||
/* Delete .dat file, if any: */
|
||
|
||
unlink(rfname);
|
||
snprintf(fname, sizeof(fname), "saved/%s.dat",
|
||
d_names[which]);
|
||
|
||
free(rfname);
|
||
rfname = get_fname(fname);
|
||
debug(rfname);
|
||
|
||
unlink(rfname);
|
||
|
||
|
||
/* Move all other files up a notch: */
|
||
|
||
free(d_names[which]);
|
||
free(d_exts[which]);
|
||
free_surface(&thumbs[which]);
|
||
|
||
thumbs[which] = NULL;
|
||
|
||
for (i = which; i < num_files - 1; i++)
|
||
{
|
||
d_names[i] = d_names[i + 1];
|
||
d_exts[i] = d_exts[i + 1];
|
||
thumbs[i] = thumbs[i + 1];
|
||
d_places[i] = d_places[i + 1];
|
||
}
|
||
|
||
num_files--;
|
||
|
||
|
||
/* Make sure the cursor doesn't go off the end! */
|
||
|
||
if (which >= num_files)
|
||
which = num_files - 1;
|
||
|
||
|
||
/* Scroll up if the cursor goes off top of screen! */
|
||
|
||
if (which < cur && cur >= 4)
|
||
{
|
||
cur = cur - 4;
|
||
update_list = 1;
|
||
}
|
||
|
||
|
||
/* No files to open now? */
|
||
|
||
if (which < 0)
|
||
{
|
||
do_prompt_snd(PROMPT_OPEN_NOFILES_TXT,
|
||
PROMPT_OPEN_NOFILES_YES, "",
|
||
SND_YOUCANNOT);
|
||
done = 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
perror(rfname);
|
||
|
||
do_prompt_snd("CAN'T", "OK", "", SND_YOUCANNOT);
|
||
update_list = 1;
|
||
}
|
||
|
||
free(rfname);
|
||
}
|
||
else
|
||
{
|
||
update_list = 1;
|
||
}
|
||
}
|
||
|
||
}
|
||
while (!done);
|
||
|
||
|
||
/* Load the chosen picture: */
|
||
|
||
if (which != -1)
|
||
{
|
||
/* Save old one first? */
|
||
|
||
if (!been_saved && !disable_save)
|
||
{
|
||
if (do_prompt_image_snd(PROMPT_OPEN_SAVE_TXT,
|
||
PROMPT_OPEN_SAVE_YES,
|
||
PROMPT_OPEN_SAVE_NO,
|
||
img_tools[TOOL_SAVE], NULL, NULL,
|
||
SND_AREYOUSURE))
|
||
{
|
||
do_save();
|
||
}
|
||
}
|
||
|
||
|
||
/* Figure out filename: */
|
||
|
||
snprintf(fname, sizeof(fname), "%s/%s%s",
|
||
dirname[d_places[which]],
|
||
d_names[which], d_exts[which]);
|
||
|
||
#ifdef SAVE_AS_BMP
|
||
img = SDL_LoadBMP(fname);
|
||
#else
|
||
img = IMG_Load(fname);
|
||
#endif
|
||
|
||
if (img == NULL)
|
||
{
|
||
fprintf(stderr,
|
||
"\nWarning: Couldn't load the saved image!\n"
|
||
"%s\n"
|
||
"The Simple DirectMedia Layer error that occurred "
|
||
"was:\n"
|
||
"%s\n\n", fname, SDL_GetError());
|
||
|
||
do_prompt(PROMPT_OPEN_UNOPENABLE_TXT,
|
||
PROMPT_OPEN_UNOPENABLE_YES, "");
|
||
}
|
||
else
|
||
{
|
||
free_surface(&img_starter);
|
||
free_surface(&img_starter_bkgd);
|
||
starter_mirrored = 0;
|
||
starter_flipped = 0;
|
||
|
||
autoscale_copy_smear_free(img,canvas,SDL_BlitSurface);
|
||
|
||
cur_undo = 0;
|
||
oldest_undo = 0;
|
||
newest_undo = 0;
|
||
|
||
if (d_places[which] == PLACE_SAVED_DIR)
|
||
{
|
||
/* Saved image: */
|
||
|
||
been_saved = 1;
|
||
|
||
strcpy(file_id, d_names[which]);
|
||
starter_id[0] = '\0';
|
||
|
||
|
||
/* See if this saved image was based on a 'starter' */
|
||
|
||
load_starter_id(d_names[which]);
|
||
|
||
if (starter_id[0] != '\0')
|
||
{
|
||
load_starter(starter_id);
|
||
|
||
if (starter_mirrored)
|
||
mirror_starter();
|
||
|
||
if (starter_flipped)
|
||
flip_starter();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Immutable 'starter' image;
|
||
we'll need to save a new image when saving...: */
|
||
|
||
been_saved = 1;
|
||
|
||
file_id[0] = '\0';
|
||
strcpy(starter_id, d_names[which]);
|
||
load_starter(starter_id);
|
||
|
||
SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, 255, 255, 255));
|
||
SDL_BlitSurface(img_starter_bkgd, NULL, canvas, NULL);
|
||
SDL_BlitSurface(img_starter, NULL, canvas, NULL);
|
||
}
|
||
|
||
|
||
reset_avail_tools();
|
||
|
||
tool_avail_bak[TOOL_UNDO] = 0;
|
||
tool_avail_bak[TOOL_REDO] = 0;
|
||
}
|
||
}
|
||
|
||
|
||
update_canvas(0, 0, WINDOW_WIDTH - 96 - 96, 48 * 7 + 40 + HEIGHTOFFSET);
|
||
}
|
||
|
||
|
||
/* Clean up: */
|
||
|
||
free_surface_array(thumbs, num_files);
|
||
|
||
free(thumbs);
|
||
|
||
for (i = 0; i < num_files; i++)
|
||
{
|
||
free(d_names[i]);
|
||
free(d_exts[i]);
|
||
}
|
||
|
||
for (i = 0; i < NUM_PLACES_TO_LOOK; i++)
|
||
free(dirname[i]);
|
||
|
||
free(d_names);
|
||
free(d_exts);
|
||
free(d_places);
|
||
}
|
||
|
||
|
||
/* Let sound effects (e.g., "Save" sfx) play out before quitting... */
|
||
|
||
static void wait_for_sfx(void)
|
||
{
|
||
#ifndef NOSOUND
|
||
if (use_sound)
|
||
{
|
||
while (Mix_Playing(-1))
|
||
SDL_Delay(10);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
////////////////////////////////////////////////////////////
|
||
// stamp outline
|
||
#ifndef LOW_QUALITY_STAMP_OUTLINE
|
||
/* XOR-based outline of rubber stamp shapes
|
||
(unused if LOW_QUALITY_STAMP_OUTLINE is #defined) */
|
||
|
||
#if 1
|
||
#define STIPLE_W 5
|
||
#define STIPLE_H 5
|
||
static char stiple[] =
|
||
"84210"
|
||
"10842"
|
||
"42108"
|
||
"08421"
|
||
"21084"
|
||
;
|
||
#endif
|
||
|
||
#if 0
|
||
#define STIPLE_W 4
|
||
#define STIPLE_H 4
|
||
static char stiple[] =
|
||
"8000"
|
||
"0800"
|
||
"0008"
|
||
"0080"
|
||
;
|
||
#endif
|
||
|
||
#if 0
|
||
#define STIPLE_W 12
|
||
#define STIPLE_H 12
|
||
static char stiple[] =
|
||
"808808000000"
|
||
"800000080880"
|
||
"008088080000"
|
||
"808000000808"
|
||
"000080880800"
|
||
"088080000008"
|
||
"000000808808"
|
||
"080880800000"
|
||
"080000008088"
|
||
"000808808000"
|
||
"880800000080"
|
||
"000008088080"
|
||
;
|
||
#endif
|
||
|
||
static unsigned char *stamp_outline_data;
|
||
static int stamp_outline_w, stamp_outline_h;
|
||
|
||
static void update_stamp_xor(void)
|
||
{
|
||
int xx, yy, rx, ry;
|
||
Uint8 dummy;
|
||
SDL_Surface * src;
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int);
|
||
unsigned char *alphabits;
|
||
int new_w;
|
||
int new_h;
|
||
unsigned char *outline;
|
||
unsigned char *old_outline_data;
|
||
|
||
src = active_stamp;
|
||
|
||
// start by scaling
|
||
src = thumbnail(src, CUR_STAMP_W, CUR_STAMP_H, 0);
|
||
|
||
getpixel = getpixels[src->format->BytesPerPixel];
|
||
alphabits = calloc(src->w+4, src->h+4);
|
||
|
||
SDL_LockSurface(src);
|
||
for (yy = 0; yy < src->h; yy++)
|
||
{
|
||
ry = yy;
|
||
for (xx = 0; xx < src->w; xx++)
|
||
{
|
||
rx = xx;
|
||
SDL_GetRGBA(getpixel(src, rx, ry),
|
||
src->format, &dummy, &dummy, &dummy, alphabits + xx+2 + (yy+2)*(src->w+4));
|
||
}
|
||
}
|
||
SDL_UnlockSurface(src);
|
||
|
||
new_w = src->w+4;
|
||
new_h = src->h+4;
|
||
SDL_FreeSurface(src);
|
||
outline = calloc(new_w, new_h);
|
||
|
||
for (yy = 1; yy < new_h-1; yy++)
|
||
{
|
||
for (xx = 1; xx < new_w-1; xx++)
|
||
{
|
||
unsigned char above = 0;
|
||
unsigned char below = 0xff;
|
||
unsigned char tmp;
|
||
|
||
tmp = alphabits[(xx-1) + (yy-1)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
tmp = alphabits[(xx+1) + (yy-1)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
|
||
tmp = alphabits[(xx+0) + (yy-1)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
tmp = alphabits[(xx+0) + (yy+0)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
tmp = alphabits[(xx+1) + (yy+0)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
tmp = alphabits[(xx-1) + (yy+0)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
tmp = alphabits[(xx+0) + (yy+1)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
|
||
tmp = alphabits[(xx-1) + (yy+1)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
tmp = alphabits[(xx+1) + (yy+1)*new_w];
|
||
above |= tmp;
|
||
below &= tmp;
|
||
|
||
outline[xx + yy*new_w] = (above^below)>>7;
|
||
}
|
||
}
|
||
|
||
old_outline_data = stamp_outline_data;
|
||
stamp_outline_data = outline;
|
||
stamp_outline_w = new_w;
|
||
stamp_outline_h = new_h;
|
||
if (old_outline_data)
|
||
free(old_outline_data);
|
||
free(alphabits);
|
||
}
|
||
|
||
static void stamp_xor(int x, int y)
|
||
{
|
||
int xx, yy, sx, sy;
|
||
|
||
SDL_LockSurface(screen);
|
||
for (yy = 0; yy < stamp_outline_h; yy++)
|
||
{
|
||
for (xx = 0; xx < stamp_outline_w; xx++)
|
||
{
|
||
if(!stamp_outline_data[xx + yy*stamp_outline_w])
|
||
continue;
|
||
sx = x + xx - stamp_outline_w/2;
|
||
sy = y + yy - stamp_outline_h/2;
|
||
if (stiple[sx%STIPLE_W + sy%STIPLE_H * STIPLE_W] != '8')
|
||
continue;
|
||
xorpixel(sx, sy);
|
||
}
|
||
}
|
||
SDL_UnlockSurface(screen);
|
||
}
|
||
|
||
#endif
|
||
///////////////////////////////////////////////////
|
||
|
||
static void rgbtohsv(Uint8 r8, Uint8 g8, Uint8 b8, float *h, float *s, float *v)
|
||
{
|
||
float rgb_min, rgb_max, delta, r, g, b;
|
||
|
||
r = (r8 / 255.0);
|
||
g = (g8 / 255.0);
|
||
b = (b8 / 255.0);
|
||
|
||
rgb_min = min(r, min(g, b));
|
||
rgb_max = max(r, max(g, b));
|
||
*v = rgb_max;
|
||
|
||
delta = rgb_max - rgb_min;
|
||
|
||
if (rgb_max == 0)
|
||
{
|
||
/* Black */
|
||
|
||
*s = 0;
|
||
*h = -1;
|
||
}
|
||
else
|
||
{
|
||
*s = delta / rgb_max;
|
||
|
||
if (r == rgb_max)
|
||
*h = (g - b) / delta;
|
||
else if (g == rgb_max)
|
||
*h = 2 + (b - r) / delta; /* between cyan & yellow */
|
||
else
|
||
*h = 4 + (r - g) / delta; /* between magenta & cyan */
|
||
|
||
*h = (*h * 60); /* degrees */
|
||
|
||
if (*h < 0)
|
||
*h = (*h + 360);
|
||
}
|
||
}
|
||
|
||
|
||
static void hsvtorgb(float h, float s, float v, Uint8 *r8, Uint8 *g8, Uint8 *b8)
|
||
{
|
||
int i;
|
||
float f, p, q, t, r, g, b;
|
||
|
||
if (s == 0)
|
||
{
|
||
/* Achromatic (grey) */
|
||
|
||
r = v;
|
||
g = v;
|
||
b = v;
|
||
}
|
||
else
|
||
{
|
||
h = h / 60;
|
||
i = floor(h);
|
||
f = h - i;
|
||
p = v * (1 - s);
|
||
q = v * (1 - s * f);
|
||
t = v * (1 - s * (1 - f));
|
||
|
||
if (i == 0)
|
||
{
|
||
r = v;
|
||
g = t;
|
||
b = p;
|
||
}
|
||
else if (i == 1)
|
||
{
|
||
r = q;
|
||
g = v;
|
||
b = p;
|
||
}
|
||
else if (i == 2)
|
||
{
|
||
r = p;
|
||
g = v;
|
||
b = t;
|
||
}
|
||
else if (i == 3)
|
||
{
|
||
r = p;
|
||
g = q;
|
||
b = v;
|
||
}
|
||
else if (i == 4)
|
||
{
|
||
r = t;
|
||
g = p;
|
||
b = v;
|
||
}
|
||
else
|
||
{
|
||
r = v;
|
||
g = p;
|
||
b = q;
|
||
}
|
||
}
|
||
|
||
|
||
*r8 = (Uint8) (r * 255);
|
||
*g8 = (Uint8) (g * 255);
|
||
*b8 = (Uint8) (b * 255);
|
||
}
|
||
|
||
|
||
static void do_print(void)
|
||
{
|
||
#if !defined(WIN32) && !defined(__BEOS__) && !defined(__APPLE__)
|
||
char * pcmd;
|
||
FILE * pi;
|
||
|
||
/* Linux, Unix, etc. */
|
||
|
||
if (want_alt_printcommand && !fullscreen)
|
||
pcmd = (char *) altprintcommand;
|
||
else
|
||
pcmd = (char *) printcommand;
|
||
|
||
pi = popen(pcmd, "w");
|
||
|
||
if (pi == NULL)
|
||
{
|
||
perror(pcmd);
|
||
}
|
||
else
|
||
{
|
||
#ifdef PRINTMETHOD_PNG_PNM_PS
|
||
if (do_png_save(pi, pcmd, canvas))
|
||
do_prompt_snd(PROMPT_PRINT_TXT, PROMPT_PRINT_YES, "", SND_TUXOK);
|
||
#elif defined(PRINTMETHOD_PNM_PS)
|
||
// nothing here
|
||
#elif defined(PRINTMETHOD_PS)
|
||
if (do_ps_save(pi, pcmd, canvas))
|
||
do_prompt_snd(PROMPT_PRINT_TXT, PROMPT_PRINT_YES, "", SND_TUXOK);
|
||
#else
|
||
#error No print method defined!
|
||
#endif
|
||
}
|
||
#else
|
||
#ifdef WIN32
|
||
/* Win32 */
|
||
|
||
char f[512];
|
||
int show = (want_alt_printcommand && !fullscreen);
|
||
|
||
snprintf(f, sizeof(f), "%s/%s", savedir, "print.cfg");
|
||
|
||
{
|
||
const char *error = SurfacePrint(canvas, use_print_config?f:NULL, show);
|
||
|
||
if ( error ) fprintf(stderr, "%s\n", error);
|
||
}
|
||
#elif defined(__BEOS__)
|
||
/* BeOS */
|
||
|
||
SurfacePrint(canvas);
|
||
#elif defined(__APPLE__)
|
||
/* Mac OS X */
|
||
|
||
int show = (want_alt_printcommand && !fullscreen);
|
||
|
||
const char* error = SurfacePrint (canvas, show);
|
||
|
||
if (error)
|
||
fprintf (stderr, "Cannot print: %s\n", error);
|
||
else
|
||
do_prompt_snd(PROMPT_PRINT_TXT, PROMPT_PRINT_YES, "", SND_TUXOK);
|
||
#endif
|
||
|
||
#endif
|
||
}
|
||
|
||
|
||
static void do_render_cur_text(int do_blit)
|
||
{
|
||
int w, h;
|
||
SDL_Color color = {color_hexes[cur_color][0],
|
||
color_hexes[cur_color][1],
|
||
color_hexes[cur_color][2],
|
||
0};
|
||
SDL_Surface * tmp_surf;
|
||
SDL_Rect dest, src;
|
||
char * str;
|
||
|
||
|
||
/* Keep cursor on the screen! */
|
||
|
||
if (cursor_y > ((48 * 7 + 40 + HEIGHTOFFSET) -
|
||
TTF_FontHeight(getfonthandle(cur_font))))
|
||
{
|
||
cursor_y = ((48 * 7 + 40 + HEIGHTOFFSET) -
|
||
TTF_FontHeight(getfonthandle(cur_font)));
|
||
}
|
||
|
||
|
||
/* Render the text: */
|
||
|
||
if (texttool_len > 0)
|
||
{
|
||
str = uppercase(texttool_str);
|
||
|
||
tmp_surf = render_text(getfonthandle(cur_font), str, color);
|
||
|
||
w = tmp_surf->w;
|
||
h = tmp_surf->h;
|
||
|
||
cursor_textwidth = w;
|
||
|
||
free(str);
|
||
}
|
||
else
|
||
{
|
||
/* FIXME: Do something different! */
|
||
|
||
update_canvas(0, 0, WINDOW_WIDTH - 96, (48 * 7) + 40 + HEIGHTOFFSET);
|
||
cursor_textwidth = 0;
|
||
return;
|
||
}
|
||
|
||
|
||
if (!do_blit)
|
||
{
|
||
/* FIXME: Only delete what's changed! */
|
||
|
||
update_canvas(0, 0, WINDOW_WIDTH - 96, (48 * 7) + 40 + HEIGHTOFFSET);
|
||
|
||
|
||
/* Draw outline around text: */
|
||
|
||
dest.x = cursor_x - 2 + 96;
|
||
dest.y = cursor_y - 2;
|
||
dest.w = w + 4;
|
||
dest.h = h + 4;
|
||
|
||
if (dest.x + dest.w > WINDOW_WIDTH - 96)
|
||
dest.w = WINDOW_WIDTH - 96 - dest.x;
|
||
if (dest.y + dest.h > (48 * 7 + 40 + HEIGHTOFFSET))
|
||
dest.h = (48 * 7 + 40 + HEIGHTOFFSET) - dest.y;
|
||
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(canvas->format, 0, 0, 0));
|
||
|
||
|
||
/* FIXME: This would be nice if it were alpha-blended: */
|
||
|
||
dest.x = cursor_x + 96;
|
||
dest.y = cursor_y;
|
||
dest.w = w;
|
||
dest.h = h;
|
||
|
||
if (dest.x + dest.w > WINDOW_WIDTH - 96)
|
||
dest.w = WINDOW_WIDTH - 96 - dest.x;
|
||
if (dest.y + dest.h > (48 * 7 + 40 + HEIGHTOFFSET))
|
||
dest.h = (48 * 7 + 40 + HEIGHTOFFSET) - dest.y;
|
||
|
||
if ((color_hexes[cur_color][0] +
|
||
color_hexes[cur_color][1] +
|
||
color_hexes[cur_color][2]) >= 384)
|
||
{
|
||
/* Grey background if blit is white!... */
|
||
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(canvas->format, 64, 64, 64));
|
||
}
|
||
else
|
||
{
|
||
/* White background, normally... */
|
||
|
||
SDL_FillRect(screen, &dest,
|
||
SDL_MapRGB(canvas->format, 255, 255, 255));
|
||
}
|
||
}
|
||
|
||
|
||
/* Draw the text itself! */
|
||
|
||
if (tmp_surf != NULL)
|
||
{
|
||
dest.x = cursor_x;
|
||
dest.y = cursor_y;
|
||
|
||
src.x = 0;
|
||
src.y = 0;
|
||
src.w = tmp_surf->w;
|
||
src.h = tmp_surf->h;
|
||
|
||
if (dest.x + src.w > WINDOW_WIDTH - 96 - 96)
|
||
src.w = WINDOW_WIDTH - 96 - 96 - dest.x;
|
||
if (dest.y + src.h > (48 * 7 + 40 + HEIGHTOFFSET))
|
||
src.h = (48 * 7 + 40 + HEIGHTOFFSET) - dest.y;
|
||
|
||
if (do_blit)
|
||
{
|
||
SDL_BlitSurface(tmp_surf, &src, canvas, &dest);
|
||
update_canvas(dest.x, dest.y, dest.x + tmp_surf->w, dest.y + tmp_surf->h);
|
||
}
|
||
else
|
||
{
|
||
dest.x = dest.x + 96;
|
||
SDL_BlitSurface(tmp_surf, &src, screen, &dest);
|
||
}
|
||
}
|
||
|
||
|
||
/* FIXME: Only update what's changed! */
|
||
|
||
SDL_Flip(screen);
|
||
|
||
|
||
if (tmp_surf != NULL)
|
||
SDL_FreeSurface(tmp_surf);
|
||
}
|
||
|
||
|
||
/* Return string as uppercase if that option is set: */
|
||
|
||
#ifdef OLD_UPPERCASE_CODE
|
||
|
||
static char * uppercase(char * str)
|
||
{
|
||
char * ustr;
|
||
int i;
|
||
|
||
ustr = strdup(str);
|
||
|
||
if (only_uppercase)
|
||
{
|
||
for (i = 0; i < strlen(ustr); i++)
|
||
ustr[i] = toupper(ustr[i]);
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
printf(" ORIGINAL: %s\n"
|
||
"UPPERCASE: %s\n\n", str, ustr);
|
||
#endif
|
||
|
||
return (ustr);
|
||
}
|
||
|
||
#else
|
||
|
||
static char * uppercase(char * str)
|
||
{
|
||
unsigned int i, sz;
|
||
wchar_t * dest;
|
||
char * ustr;
|
||
|
||
if (only_uppercase)
|
||
{
|
||
sz = sizeof(wchar_t) * (strlen(str) + 1);
|
||
|
||
dest = (wchar_t *) malloc(sz);
|
||
// FIXME: uppercase chars may need extra bytes
|
||
ustr = malloc(strlen(str) + 1);
|
||
|
||
if (dest != NULL)
|
||
{
|
||
mbstowcs(dest, str, sz);
|
||
|
||
for (i = 0; i < strlen(str); i++)
|
||
{
|
||
dest[i] = towupper(dest[i]);
|
||
}
|
||
|
||
wcstombs(ustr, dest, sizeof(char) * (strlen(str) + 1));
|
||
|
||
free(dest);
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
printf(" ORIGINAL: %s\n"
|
||
"UPPERCASE: %s\n\n", str, ustr);
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
ustr = strdup(str);
|
||
}
|
||
|
||
return(ustr);
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
/* Return string in right-to-left mode, if necessary: */
|
||
|
||
static char * textdir(const char * const str)
|
||
{
|
||
unsigned char * dstr;
|
||
unsigned i, j;
|
||
unsigned char c1, c2, c3, c4;
|
||
|
||
#ifdef DEBUG
|
||
printf("ORIG_DIR: %s\n", str);
|
||
#endif
|
||
|
||
dstr = malloc(strlen(str) + 5);
|
||
|
||
if (need_right_to_left)
|
||
{
|
||
dstr[strlen(str)] = '\0';
|
||
|
||
for (i = 0; i < strlen(str); i++)
|
||
{
|
||
j = (strlen(str) - i - 1);
|
||
|
||
c1 = (unsigned char) str[i + 0];
|
||
c2 = (unsigned char) str[i + 1];
|
||
c3 = (unsigned char) str[i + 2];
|
||
c4 = (unsigned char) str[i + 3];
|
||
|
||
if (c1 < 128) /* 0xxx xxxx - 1 byte */
|
||
{
|
||
dstr[j] = str[i];
|
||
}
|
||
else if ((c1 & 0xE0) == 0xC0) /* 110x xxxx - 2 bytes */
|
||
{
|
||
dstr[j - 1] = c1;
|
||
dstr[j - 0] = c2;
|
||
i = i + 1;
|
||
}
|
||
else if ((c1 & 0xF0) == 0xE0) /* 1110 xxxx - 3 bytes */
|
||
{
|
||
dstr[j - 2] = c1;
|
||
dstr[j - 1] = c2;
|
||
dstr[j - 0] = c3;
|
||
i = i + 2;
|
||
}
|
||
else /* 1111 0xxx - 4 bytes */
|
||
{
|
||
dstr[j - 3] = c1;
|
||
dstr[j - 2] = c2;
|
||
dstr[j - 1] = c3;
|
||
dstr[j - 0] = c4;
|
||
i = i + 3;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
strcpy((char *) dstr, str);
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
printf("L2R_DIR: %s\n", dstr);
|
||
#endif
|
||
|
||
return ((char *) dstr);
|
||
}
|
||
|
||
|
||
/* For flood fill... */
|
||
|
||
static int colors_close(Uint32 c1, Uint32 c2)
|
||
{
|
||
#ifdef LOW_QUALITY_FLOOD_FILL
|
||
return (c1 == c2);
|
||
#else
|
||
Uint8 r1, g1, b1,
|
||
r2, g2, b2;
|
||
|
||
if (c1 == c2)
|
||
{
|
||
/* Get it over with quick, if possible! */
|
||
|
||
return 1;
|
||
}
|
||
else
|
||
{
|
||
double r, g, b;
|
||
SDL_GetRGB(c1, canvas->format, &r1, &g1, &b1);
|
||
SDL_GetRGB(c2, canvas->format, &r2, &g2, &b2);
|
||
|
||
// use distance in linear RGB space
|
||
r = sRGB_to_linear_table[r1] - sRGB_to_linear_table[r2];
|
||
r *= r;
|
||
g = sRGB_to_linear_table[g1] - sRGB_to_linear_table[g2];
|
||
g *= g;
|
||
b = sRGB_to_linear_table[b1] - sRGB_to_linear_table[b2];
|
||
b *= b;
|
||
|
||
// easy to confuse:
|
||
// dark grey, brown, purple
|
||
// light grey, tan
|
||
// red, orange
|
||
return r+g+b < 0.04;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
/* Flood fill! */
|
||
|
||
static void do_flood_fill(int x, int y, Uint32 cur_colr, Uint32 old_colr)
|
||
{
|
||
int fillL, fillR, i, in_line;
|
||
static unsigned char prog_anim;
|
||
Uint32 (*getpixel)(SDL_Surface *, int, int) = getpixels[canvas->format->BytesPerPixel];
|
||
void (*putpixel)(SDL_Surface *, int, int, Uint32) = putpixels[canvas->format->BytesPerPixel];
|
||
|
||
|
||
if (cur_colr == old_colr ||
|
||
colors_close(cur_colr, old_colr))
|
||
return;
|
||
|
||
|
||
fillL = x;
|
||
fillR = x;
|
||
|
||
prog_anim++;
|
||
if ((prog_anim % 4) == 0)
|
||
{
|
||
show_progress_bar();
|
||
playsound(0, SND_BUBBLE, 0);
|
||
}
|
||
|
||
|
||
/* Find left side, filling along the way */
|
||
|
||
in_line = 1;
|
||
|
||
while (in_line)
|
||
{
|
||
putpixel(canvas, fillL, y, cur_colr);
|
||
fillL--;
|
||
|
||
in_line = (fillL < 0) ? 0 : colors_close(getpixel(canvas, fillL, y),
|
||
old_colr);
|
||
}
|
||
|
||
fillL++;
|
||
|
||
|
||
/* Find right side, filling along the way */
|
||
|
||
in_line = 1;
|
||
while (in_line)
|
||
{
|
||
putpixel(canvas, fillR, y, cur_colr);
|
||
fillR++;
|
||
|
||
in_line = (fillR >= canvas->w) ? 0 : colors_close(getpixel(canvas,
|
||
fillR, y),
|
||
old_colr);
|
||
}
|
||
|
||
fillR--;
|
||
|
||
|
||
/* Search top and bottom */
|
||
|
||
for (i = fillL; i <= fillR; i++)
|
||
{
|
||
if (y > 0 && colors_close(getpixel(canvas, i, y - 1), old_colr))
|
||
do_flood_fill(i, y - 1, cur_colr, old_colr);
|
||
|
||
if (y < canvas->h && colors_close(getpixel(canvas, i, y + 1), old_colr))
|
||
do_flood_fill(i, y + 1, cur_colr, old_colr);
|
||
}
|
||
}
|
||
|
||
|
||
/* Scroll Timer */
|
||
|
||
static Uint32 scrolltimer_callback(Uint32 interval, void *param)
|
||
{
|
||
SDL_PushEvent((SDL_Event*)param);
|
||
|
||
return interval;
|
||
}
|
||
|
||
|
||
/* Controls the Text-Timer - interval == 0 removes the timer */
|
||
|
||
static void control_drawtext_timer(Uint32 interval, const char * const text)
|
||
{
|
||
static int activated = 0;
|
||
static SDL_TimerID TimerID = 0;
|
||
static SDL_Event drawtext_event;
|
||
|
||
|
||
/* Remove old timer if any is running */
|
||
|
||
if (activated)
|
||
{
|
||
SDL_RemoveTimer(TimerID);
|
||
activated = 0;
|
||
TimerID = 0;
|
||
}
|
||
|
||
if (interval == 0)
|
||
return;
|
||
|
||
drawtext_event.type = SDL_USEREVENT;
|
||
drawtext_event.user.code = USEREVENT_TEXT_UPDATE;
|
||
drawtext_event.user.data1 = (void*) text;
|
||
|
||
|
||
/* Add new timer */
|
||
|
||
TimerID = SDL_AddTimer(interval, drawtext_callback, (void*) &drawtext_event);
|
||
activated = 1;
|
||
}
|
||
|
||
|
||
/* Drawtext Timer */
|
||
|
||
static Uint32 drawtext_callback(Uint32 interval, void *param)
|
||
{
|
||
(void)interval;
|
||
SDL_PushEvent((SDL_Event*)param);
|
||
|
||
return 0; /* Remove timer */
|
||
}
|
||
|
||
|
||
static void parse_options(FILE * fi)
|
||
{
|
||
char str[256];
|
||
|
||
do
|
||
{
|
||
fgets(str, sizeof(str), fi);
|
||
|
||
strip_trailing_whitespace(str);
|
||
|
||
if (!feof(fi))
|
||
{
|
||
debug(str);
|
||
|
||
|
||
/* FIXME: This should be handled better! */
|
||
/* (e.g., complain on illegal lines, support comments, blanks, etc.) */
|
||
|
||
if (strcmp(str, "fullscreen=yes") == 0)
|
||
{
|
||
fullscreen = 1;
|
||
}
|
||
else if (strcmp(str, "fullscreen=no") == 0 ||
|
||
strcmp(str, "windowed=yes") == 0)
|
||
{
|
||
fullscreen = 0;
|
||
}
|
||
else if (strcmp(str, "nostampcontrols=yes") == 0)
|
||
{
|
||
disable_stamp_controls = 1;
|
||
}
|
||
else if (strcmp(str, "nostampcontrols=no") == 0 ||
|
||
strcmp(str, "stampcontrols=yes") == 0)
|
||
{
|
||
disable_stamp_controls = 0;
|
||
}
|
||
else if (strcmp(str, "mirrorstamps=yes") == 0)
|
||
{
|
||
mirrorstamps = 1;
|
||
}
|
||
else if (strcmp(str, "mirrorstamps=no") == 0 ||
|
||
strcmp(str, "dontmirrorstamps=yes") == 0)
|
||
{
|
||
mirrorstamps = 0;
|
||
}
|
||
else if (strcmp(str, "noshortcuts=yes") == 0)
|
||
{
|
||
noshortcuts = 1;
|
||
}
|
||
else if (strcmp(str, "noshortcuts=no") == 0 ||
|
||
strcmp(str, "shortcuts=yes") == 0)
|
||
{
|
||
noshortcuts = 0;
|
||
}
|
||
else if (!memcmp("windowsize=",str,11))
|
||
{
|
||
char *endp1;
|
||
char *endp2;
|
||
int w,h;
|
||
w = strtoul(str+11, &endp1, 10);
|
||
h = strtoul(endp1+1, &endp2, 10);
|
||
// sanity check it
|
||
if(str+11==endp1 || endp1+1==endp2 || *endp1!='x' || *endp2 || w<500 || h<480 || h>w*3 || w>h*4)
|
||
{
|
||
// Oddly, config files have no error checking.
|
||
//show_usage(stderr, (char *) getfilename(argv[0]));
|
||
//exit(1);
|
||
}
|
||
else
|
||
{
|
||
WINDOW_WIDTH = w;
|
||
WINDOW_HEIGHT = h;
|
||
}
|
||
}
|
||
// to handle old config files
|
||
else if (strcmp(str, "800x600=yes") == 0 ||
|
||
strcmp(str, "windowsize=800x600") == 0)
|
||
{
|
||
WINDOW_WIDTH = 800;
|
||
WINDOW_HEIGHT = 600;
|
||
}
|
||
// also for old config files
|
||
else if (strcmp(str, "800x600=no") == 0 ||
|
||
strcmp(str, "640x480=yes") == 0 ||
|
||
strcmp(str, "windowsize=640x480") == 0)
|
||
{
|
||
WINDOW_WIDTH = 640;
|
||
WINDOW_HEIGHT = 480;
|
||
}
|
||
else if (strcmp(str, "nooutlines=yes") == 0)
|
||
{
|
||
dont_do_xor = 1;
|
||
}
|
||
else if (strcmp(str, "nooutlines=no") == 0 ||
|
||
strcmp(str, "outlines=yes") == 0)
|
||
{
|
||
dont_do_xor = 0;
|
||
}
|
||
else if (strcmp(str, "keyboard=yes") == 0)
|
||
{
|
||
keymouse = 1;
|
||
}
|
||
else if (strcmp(str, "keyboard=no") == 0 ||
|
||
strcmp(str, "mouse=yes") == 0)
|
||
{
|
||
keymouse = 0;
|
||
}
|
||
else if (strcmp(str, "nowheelmouse=yes") == 0)
|
||
{
|
||
wheely = 0;
|
||
}
|
||
else if (strcmp(str, "nowheelmouse=no") == 0 ||
|
||
strcmp(str, "wheelmouse=yes") == 0)
|
||
{
|
||
wheely = 1;
|
||
}
|
||
else if (strcmp(str, "grab=yes") == 0)
|
||
{
|
||
grab_input = 1;
|
||
}
|
||
else if (strcmp(str, "grab=no") == 0 ||
|
||
strcmp(str, "nograb=yes") == 0)
|
||
{
|
||
grab_input = 0;
|
||
}
|
||
else if (strcmp(str, "nofancycursors=yes") == 0)
|
||
{
|
||
no_fancy_cursors = 1;
|
||
}
|
||
else if (strcmp(str, "nofancycursors=no") == 0 ||
|
||
strcmp(str, "fancycursors=yes") == 0)
|
||
{
|
||
no_fancy_cursors = 0;
|
||
}
|
||
else if (strcmp(str, "uppercase=yes") == 0)
|
||
{
|
||
only_uppercase = 1;
|
||
}
|
||
else if (strcmp(str, "uppercase=no") == 0 ||
|
||
strcmp(str, "mixedcase=yes") == 0)
|
||
{
|
||
only_uppercase = 0;
|
||
}
|
||
else if (strcmp(str, "noquit=yes") == 0)
|
||
{
|
||
disable_quit = 1;
|
||
}
|
||
else if (strcmp(str, "noquit=no") == 0 ||
|
||
strcmp(str, "quit=yes") == 0)
|
||
{
|
||
disable_quit = 0;
|
||
}
|
||
else if (strcmp(str, "nosave=yes") == 0)
|
||
{
|
||
disable_save = 1;
|
||
}
|
||
else if (strcmp(str, "nosave=no") == 0 ||
|
||
strcmp(str, "save=yes") == 0)
|
||
{
|
||
disable_save = 0;
|
||
}
|
||
else if (strcmp(str, "noprint=yes") == 0)
|
||
{
|
||
disable_print = 1;
|
||
}
|
||
else if (strcmp(str, "noprint=no") == 0 ||
|
||
strcmp(str, "print=yes") == 0)
|
||
{
|
||
disable_print = 0;
|
||
}
|
||
else if (strcmp(str, "nostamps=yes") == 0)
|
||
{
|
||
dont_load_stamps = 1;
|
||
}
|
||
else if (strcmp(str, "nostamps=no") == 0 ||
|
||
strcmp(str, "stamps=yes") == 0)
|
||
{
|
||
dont_load_stamps = 0;
|
||
}
|
||
else if (strcmp(str, "nosysfonts=yes") == 0 ||
|
||
strcmp(str, "sysfonts=no") == 0)
|
||
{
|
||
no_system_fonts = 1;
|
||
}
|
||
else if (strcmp(str, "nosysfonts=no") == 0 ||
|
||
strcmp(str, "sysfonts=yes") == 0)
|
||
{
|
||
no_system_fonts = 0;
|
||
}
|
||
else if (strcmp(str, "nobuttondistinction=yes") == 0)
|
||
{
|
||
no_button_distinction= 1;
|
||
}
|
||
else if (strcmp(str, "nobuttondistinction=no") == 0 ||
|
||
strcmp(str, "buttondistinction=yes") == 0)
|
||
{
|
||
no_button_distinction= 0;
|
||
}
|
||
else if (strcmp(str, "nosound=yes") == 0)
|
||
{
|
||
use_sound = 0;
|
||
}
|
||
else if (strcmp(str, "nosound=no") == 0 ||
|
||
strcmp(str, "sound=yes") == 0)
|
||
{
|
||
use_sound = 1;
|
||
}
|
||
else if (strcmp(str, "simpleshapes=yes") == 0)
|
||
{
|
||
simple_shapes = 1;
|
||
}
|
||
else if (strcmp(str, "simpleshapes=no") == 0 ||
|
||
strcmp(str, "complexshapes=yes") == 0)
|
||
{
|
||
simple_shapes = 1;
|
||
}
|
||
// Should "locale=" be here as well???
|
||
// Comments welcome ... bill@newbreedsoftware.com
|
||
else if (strstr(str, "lang=") == str)
|
||
{
|
||
set_langstr(str + 5);
|
||
}
|
||
else if (strstr(str, "printdelay=") == str)
|
||
{
|
||
sscanf(str + 11, "%d", &print_delay);
|
||
#ifdef DEBUG
|
||
printf("Print delay set to %d seconds\n", print_delay);
|
||
#endif
|
||
}
|
||
else if (strcmp(str, "printcfg=yes") == 0)
|
||
{
|
||
#ifndef WIN32
|
||
fprintf(stderr, "Note: printcfg option only applies to Windows!\n");
|
||
#endif
|
||
use_print_config = 1;
|
||
}
|
||
else if (strcmp(str, "printcfg=no") == 0 ||
|
||
strcmp(str, "noprintcfg=yes") == 0)
|
||
{
|
||
#ifndef WIN32
|
||
fprintf(stderr, "Note: printcfg option only applies to Windows!\n");
|
||
#endif
|
||
use_print_config = 0;
|
||
}
|
||
else if (strstr(str, "printcommand=") == str)
|
||
{
|
||
printcommand = strdup(str + 13);
|
||
}
|
||
else if (strstr(str, "altprintcommand=") == str)
|
||
{
|
||
altprintcommand = strdup(str + 16);
|
||
}
|
||
else if (strcmp(str, "saveover=yes") == 0)
|
||
{
|
||
promptless_save = SAVE_OVER_ALWAYS;
|
||
}
|
||
else if (strcmp(str, "saveover=ask") == 0)
|
||
{
|
||
/* (Default) */
|
||
|
||
promptless_save = SAVE_OVER_PROMPT;
|
||
}
|
||
else if (strcmp(str, "saveover=new") == 0)
|
||
{
|
||
promptless_save = SAVE_OVER_NO;
|
||
}
|
||
else if (strcmp(str, "altprint=always") == 0)
|
||
{
|
||
alt_print_command_default = ALTPRINT_ALWAYS;
|
||
}
|
||
else if (strcmp(str, "altprint=mod") == 0)
|
||
{
|
||
/* (Default) */
|
||
|
||
alt_print_command_default = ALTPRINT_MOD;
|
||
}
|
||
else if (strcmp(str, "altprint=never") == 0)
|
||
{
|
||
alt_print_command_default = ALTPRINT_NEVER;
|
||
}
|
||
else if (strstr(str, "savedir=") == str)
|
||
{
|
||
savedir = strdup(str + 8);
|
||
remove_slash(savedir);
|
||
|
||
#ifdef DEBUG
|
||
printf("savedir set to: %s\n", savedir);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
while (!feof(fi));
|
||
}
|
||
|
||
|
||
#ifdef DEBUG
|
||
static char * debug_gettext(const char * str)
|
||
{
|
||
if (strcmp(str, dgettext(NULL, str)) == 0)
|
||
{
|
||
printf("NOTRANS: %s\n", str);
|
||
printf("..TRANS: %s\n", dgettext(NULL, str));
|
||
fflush(stdout);
|
||
}
|
||
|
||
return(dgettext(NULL, str));
|
||
}
|
||
#endif
|
||
|
||
|
||
static void do_setcursor(SDL_Cursor * c)
|
||
{
|
||
if (!no_fancy_cursors)
|
||
SDL_SetCursor(c);
|
||
}
|
||
|
||
|
||
static const char * great_str(void)
|
||
{
|
||
return(great_strs[rand() % (sizeof(great_strs) / sizeof(char *))]);
|
||
}
|
||
|
||
|
||
#ifdef DEBUG
|
||
static int charsize(char c)
|
||
{
|
||
Uint16 str[2];
|
||
int w, h;
|
||
|
||
str[0] = c;
|
||
str[1] = '\0';
|
||
|
||
TTF_SizeUNICODE(getfonthandle(cur_font), str, &w, &h);
|
||
|
||
return w;
|
||
}
|
||
#endif
|
||
|
||
static void draw_image_title(int t, SDL_Rect dest)
|
||
{
|
||
SDL_BlitSurface(img_title_on, NULL, screen, &dest);
|
||
|
||
dest.x += (dest.w - img_title_names[t]->w) / 2;
|
||
dest.y += (dest.h - img_title_names[t]->h) / 2;
|
||
SDL_BlitSurface(img_title_names[t], NULL, screen, &dest);
|
||
}
|
||
|
||
|
||
|
||
/* Handle keyboard events to control the mouse: */
|
||
|
||
static void handle_keymouse(SDLKey key, Uint8 updown)
|
||
{
|
||
SDL_Event event;
|
||
|
||
if (keymouse)
|
||
{
|
||
if (key == SDLK_LEFT)
|
||
mousekey_left = updown;
|
||
else if (key == SDLK_RIGHT)
|
||
mousekey_right = updown;
|
||
else if (key == SDLK_UP)
|
||
mousekey_up = updown;
|
||
else if (key == SDLK_DOWN)
|
||
mousekey_down = updown;
|
||
else if (key == SDLK_SPACE)
|
||
{
|
||
if (updown == SDL_KEYDOWN)
|
||
event.type = SDL_MOUSEBUTTONDOWN;
|
||
else
|
||
event.type = SDL_MOUSEBUTTONUP;
|
||
|
||
event.button.x = mouse_x;
|
||
event.button.y = mouse_y;
|
||
event.button.button = 1;
|
||
|
||
SDL_PushEvent(&event);
|
||
}
|
||
|
||
if (mousekey_up == SDL_KEYDOWN && mouse_y > 0)
|
||
mouse_y = mouse_y - 8;
|
||
else if (mousekey_down == SDL_KEYDOWN && mouse_y < WINDOW_HEIGHT - 1)
|
||
mouse_y = mouse_y + 8;
|
||
|
||
if (mousekey_left == SDL_KEYDOWN && mouse_x > 0)
|
||
mouse_x = mouse_x - 8;
|
||
if (mousekey_right == SDL_KEYDOWN && mouse_x < WINDOW_WIDTH - 1)
|
||
mouse_x = mouse_x + 8;
|
||
|
||
SDL_WarpMouse(mouse_x, mouse_y);
|
||
}
|
||
}
|
||
|
||
|
||
/* Unblank screen in fullscreen mode, if needed: */
|
||
|
||
static void handle_active( SDL_Event *event )
|
||
{
|
||
if (event->active.state & SDL_APPACTIVE)
|
||
{
|
||
if (event->active.gain == 1 )
|
||
{
|
||
if ( fullscreen )
|
||
SDL_Flip(screen);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* removes a single '\' or '/' from end of path */
|
||
|
||
static char *remove_slash( char *path )
|
||
{
|
||
int len = strlen(path);
|
||
|
||
if (!len)
|
||
return path;
|
||
|
||
if (path[len-1] == '/' || path[len-1] == '\\')
|
||
path[len-1] = 0;
|
||
|
||
return path;
|
||
}
|
||
|
||
|
||
/* For right-to-left languages, when word-wrapping, we need to
|
||
make sure the text doesn't end up going from bottom-to-top, too! */
|
||
|
||
static void anti_carriage_return(int left, int right, int cur_top, int new_top,
|
||
int cur_bot, int line_width)
|
||
{
|
||
SDL_Rect src, dest;
|
||
|
||
|
||
/* Move current set of text down one line (and right-justify it!): */
|
||
|
||
src.x = left;
|
||
src.y = cur_top;
|
||
src.w = line_width;
|
||
src.h = cur_bot - cur_top;
|
||
|
||
dest.x = right - line_width;
|
||
dest.y = new_top;
|
||
|
||
SDL_BlitSurface(screen, &src, screen, &dest);
|
||
|
||
|
||
/* Clear the top line for new text: */
|
||
|
||
dest.x = left;
|
||
dest.y = cur_top;
|
||
dest.w = right - left;
|
||
dest.h = new_top - cur_top;
|
||
|
||
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
|
||
}
|
||
|
||
|
||
static int mySDL_WaitEvent(SDL_Event *event)
|
||
{
|
||
int ret;
|
||
|
||
|
||
if (playing)
|
||
{
|
||
if (!feof(demofi))
|
||
{
|
||
ret = 1;
|
||
fread(event, sizeof(SDL_Event), 1, demofi);
|
||
}
|
||
else
|
||
{
|
||
/* All done! Back to normal! */
|
||
|
||
printf("(Done playing playback file '%s')\n", playfile);
|
||
|
||
ret = 0;
|
||
playing = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ret = SDL_WaitEvent(event);
|
||
|
||
if (recording)
|
||
{
|
||
fwrite(event, sizeof(SDL_Event), 1, demofi);
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
|
||
static int mySDL_PollEvent(SDL_Event *event)
|
||
{
|
||
int ret;
|
||
|
||
if (playing)
|
||
{
|
||
if (!feof(demofi))
|
||
{
|
||
ret = 1;
|
||
fread(event, sizeof(SDL_Event), 1, demofi);
|
||
}
|
||
else
|
||
{
|
||
/* All done! Back to normal! */
|
||
|
||
printf("(Done playing playback file '%s')\n", playfile);
|
||
|
||
ret = 0;
|
||
playing = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ret = SDL_PollEvent(event);
|
||
|
||
if (recording && ret > 0)
|
||
{
|
||
fwrite(event, sizeof(SDL_Event), 1, demofi);
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
|
||
static SDL_Surface * duplicate_surface(SDL_Surface * orig)
|
||
{
|
||
/*
|
||
Uint32 amask;
|
||
|
||
amask = ~(orig->format->Rmask |
|
||
orig->format->Gmask |
|
||
orig->format->Bmask);
|
||
|
||
return(SDL_CreateRGBSurface(SDL_SWSURFACE,
|
||
orig->w, orig->h,
|
||
orig->format->BitsPerPixel,
|
||
orig->format->Rmask,
|
||
orig->format->Gmask,
|
||
orig->format->Bmask,
|
||
amask));
|
||
*/
|
||
|
||
return(SDL_DisplayFormatAlpha(orig));
|
||
}
|
||
|
||
static void mirror_starter(void)
|
||
{
|
||
SDL_Surface * orig;
|
||
int x;
|
||
SDL_Rect src, dest;
|
||
|
||
|
||
/* Mirror overlay: */
|
||
|
||
orig = img_starter;
|
||
img_starter = duplicate_surface(orig);
|
||
|
||
if (img_starter != NULL)
|
||
{
|
||
for (x = 0; x < orig->w; x++)
|
||
{
|
||
src.x = x;
|
||
src.y = 0;
|
||
src.w = 1;
|
||
src.h = orig->h;
|
||
|
||
dest.x = orig->w - x - 1;
|
||
dest.y = 0;
|
||
|
||
SDL_BlitSurface(orig, &src, img_starter, &dest);
|
||
}
|
||
|
||
SDL_FreeSurface(orig);
|
||
}
|
||
else
|
||
{
|
||
img_starter = orig;
|
||
}
|
||
|
||
|
||
/* Mirror background: */
|
||
|
||
if (img_starter_bkgd != NULL)
|
||
{
|
||
orig = img_starter_bkgd;
|
||
img_starter_bkgd = duplicate_surface(orig);
|
||
|
||
if (img_starter_bkgd != NULL)
|
||
{
|
||
for (x = 0; x < orig->w; x++)
|
||
{
|
||
src.x = x;
|
||
src.y = 0;
|
||
src.w = 1;
|
||
src.h = orig->h;
|
||
|
||
dest.x = orig->w - x - 1;
|
||
dest.y = 0;
|
||
|
||
SDL_BlitSurface(orig, &src, img_starter_bkgd, &dest);
|
||
}
|
||
|
||
SDL_FreeSurface(orig);
|
||
}
|
||
else
|
||
{
|
||
img_starter_bkgd = orig;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
static void flip_starter(void)
|
||
{
|
||
SDL_Surface * orig;
|
||
int y;
|
||
SDL_Rect src, dest;
|
||
|
||
|
||
/* Flip overlay: */
|
||
|
||
orig = img_starter;
|
||
img_starter = duplicate_surface(orig);
|
||
|
||
if (img_starter != NULL)
|
||
{
|
||
for (y = 0; y < orig->h; y++)
|
||
{
|
||
src.x = 0;
|
||
src.y = y;
|
||
src.w = orig->w;
|
||
src.h = 1;
|
||
|
||
dest.x = 0;
|
||
dest.y = orig->h - y - 1;
|
||
|
||
SDL_BlitSurface(orig, &src, img_starter, &dest);
|
||
}
|
||
|
||
SDL_FreeSurface(orig);
|
||
}
|
||
else
|
||
{
|
||
img_starter = orig;
|
||
}
|
||
|
||
|
||
/* Flip background: */
|
||
|
||
if (img_starter_bkgd != NULL)
|
||
{
|
||
orig = img_starter_bkgd;
|
||
img_starter_bkgd = duplicate_surface(orig);
|
||
|
||
if (img_starter_bkgd != NULL)
|
||
{
|
||
for (y = 0; y < orig->h; y++)
|
||
{
|
||
src.x = 0;
|
||
src.y = y;
|
||
src.w = orig->w;
|
||
src.h = 1;
|
||
|
||
dest.x = 0;
|
||
dest.y = orig->h - y - 1;
|
||
|
||
SDL_BlitSurface(orig, &src, img_starter_bkgd, &dest);
|
||
}
|
||
|
||
SDL_FreeSurface(orig);
|
||
}
|
||
else
|
||
{
|
||
img_starter_bkgd = orig;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
int valid_click(Uint8 button)
|
||
{
|
||
if (button == 1 ||
|
||
((button == 2 || button == 3) && no_button_distinction))
|
||
return(1);
|
||
else
|
||
return(0);
|
||
}
|