URL
https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk
Subversion Repositories openrisc_me
Compare Revisions
- This comparison shows the changes necessary to convert path
/openrisc/trunk/rtos/ecos-2.0/packages/services/gfx/mw/v2_0/src/engine
- from Rev 27 to Rev 174
- ↔ Reverse comparison
Rev 27 → Rev 174
/devkbd.c
0,0 → 1,51
/* |
* Copyright (c) 1999 Greg Haerr <greg@censoft.com> |
* |
* Device-independent keyboard routines |
*/ |
#include "device.h" |
|
/* |
* Open the keyboard. |
*/ |
int |
GdOpenKeyboard(void) |
{ |
int fd; |
|
if ((fd = kbddev.Open(&kbddev)) == -1) |
return -1; |
|
/* possible -2 return means no kbd*/ |
return fd; |
} |
|
/* |
* Close the keyboard. |
*/ |
void |
GdCloseKeyboard(void) |
{ |
kbddev.Close(); |
} |
|
/* |
* Return the possible modifiers for the keyboard. |
*/ |
void |
GdGetModifierInfo(MWKEYMOD *modifiers, MWKEYMOD *curmodifiers) |
{ |
kbddev.GetModifierInfo(modifiers, curmodifiers); |
} |
|
/* |
* This reads one keystroke from the keyboard, and the current state of |
* the mode keys (ALT, SHIFT, CTRL). Returns -1 on error, 0 if no data |
* is ready, 1 if keypress, 2 if keyrelease. |
* This is a non-blocking call. Returns -2 if ESC pressed. |
*/ |
int |
GdReadKeyboard(MWKEY *buf, MWKEYMOD *modifiers, MWSCANCODE *scancode) |
{ |
return kbddev.Read(buf, modifiers, scancode); |
} |
/devpal4.c
0,0 → 1,29
/* |
* Copyright (c) 1999 Greg Haerr <greg@censoft.com> |
* |
* 4bpp (16 color) standard palette definition |
*/ |
#include "device.h" |
|
/* |
* Standard palette for 16 color systems. |
*/ |
MWPALENTRY mwstdpal4[16] = { |
/* 16 EGA colors, arranged in VGA standard palette order*/ |
RGBDEF( 0 , 0 , 0 ), /* black*/ |
RGBDEF( 0 , 0 , 128 ), /* blue*/ |
RGBDEF( 0 , 128, 0 ), /* green*/ |
RGBDEF( 0 , 128, 128 ), /* cyan*/ |
RGBDEF( 128, 0 , 0 ), /* red*/ |
RGBDEF( 128, 0 , 128 ), /* magenta*/ |
RGBDEF( 128, 64 , 0 ), /* adjusted brown*/ |
RGBDEF( 192, 192, 192 ), /* ltgray*/ |
RGBDEF( 128, 128, 128 ), /* gray*/ |
RGBDEF( 0 , 0 , 255 ), /* ltblue*/ |
RGBDEF( 0 , 255, 0 ), /* ltgreen*/ |
RGBDEF( 0 , 255, 255 ), /* ltcyan*/ |
RGBDEF( 255, 0 , 0 ), /* ltred*/ |
RGBDEF( 255, 0 , 255 ), /* ltmagenta*/ |
RGBDEF( 255, 255, 0 ), /* yellow*/ |
RGBDEF( 255, 255, 255 ), /* white*/ |
}; |
/devfont.caching.c
0,0 → 1,2921
/* |
* Copyright (c) 2000 Greg Haerr <greg@censoft.com> |
* T1lib Adobe type1 routines contributed by Vidar Hokstad |
* Freetype TrueType routines contributed by Martin Jolicoeur |
* Han Zi Ku routines contributed by Tanghao and Jauming |
* |
* Device-independent font and text drawing routines |
* |
* These routines do the necessary range checking, clipping, and cursor |
* overwriting checks, and then call the lower level device dependent |
* routines to actually do the drawing. The lower level routines are |
* only called when it is known that all the pixels to be drawn are |
* within the device area and are visible. |
*/ |
/*#define NDEBUG*/ |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <assert.h> |
#include <string.h> |
|
#include "device.h" |
#if (UNIX | DOS_DJGPP) |
#define strcmpi strcasecmp |
#endif |
|
#if HAVE_T1LIB_SUPPORT |
#include <t1lib.h> |
#define T1LIB_USE_AA_HIGH |
|
typedef struct { |
PMWFONTPROCS fontprocs; /* common hdr*/ |
MWCOORD fontsize; |
int fontrotation; |
int fontattr; |
|
int fontid; /* t1lib stuff*/ |
} MWT1LIBFONT, *PMWT1LIBFONT; |
|
static int t1lib_init(PSD psd); |
static PMWT1LIBFONT t1lib_createfont(const char *name, MWCOORD height,int attr); |
static void t1lib_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags); |
static MWBOOL t1lib_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo); |
static void t1lib_gettextsize(PMWFONT pfont, const void *text, int cc, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase); |
static void t1lib_destroyfont(PMWFONT pfont); |
|
/* handling routines for MWT1LIBFONT*/ |
static MWFONTPROCS t1lib_procs = { |
MWTF_ASCII, /* routines expect ascii*/ |
t1lib_getfontinfo, |
t1lib_gettextsize, |
NULL, /* gettextbits*/ |
t1lib_destroyfont, |
t1lib_drawtext, |
NULL, /* setfontsize*/ |
NULL, /* setfontrotation*/ |
NULL, /* setfontattr*/ |
}; |
#endif |
|
#ifdef T1LIB_USE_AA_HIGH |
typedef unsigned long OUTPIXELVAL; |
#else |
typedef MWPIXELVAL OUTPIXELVAL; |
#endif |
|
#if HAVE_FREETYPE_SUPPORT |
#include <freetype/freetype.h> |
#include <freetype/ftxkern.h> |
#include <freetype/ftnameid.h> |
#include <freetype/ftxcmap.h> |
#include <freetype/ftxwidth.h> |
#include <math.h> |
|
/************/ |
|
typedef struct xTT_Glyph_Cache_ { |
|
TT_Error error; |
TT_Glyph glyph; |
TT_UShort index; |
TT_UShort flags; |
|
struct xTT_Glyph_Cache_ * l; |
struct xTT_Glyph_Cache_ * r; |
|
} xTT_Glyph_Cache; |
|
typedef struct xTT_Instance_Object_ { |
|
TT_Face face; |
TT_Instance instance; |
|
xTT_Glyph_Cache * head; |
|
} xTT_Instance_Object; |
|
typedef struct xTT_Instance_ { |
|
xTT_Instance_Object * instance; |
|
} xTT_Instance; |
|
typedef struct xTT_Outline_Object_ { |
|
TT_Outline outline; |
TT_BBox bbox; |
|
} xTT_Outline_Object; |
|
typedef struct xTT_Outline_ { |
|
xTT_Outline_Object * outline; |
|
} xTT_Outline; |
|
typedef struct xTT_Glyph_Object_ { |
|
TT_Glyph * glyph; |
|
xTT_Outline_Object outline; |
|
} xTT_Glyph_Object; |
|
typedef struct xTT_Glyph_ { |
|
xTT_Glyph_Object * glyph; |
|
} xTT_Glyph; |
|
TT_Error |
xTT_Glyph_Cache_Find ( xTT_Instance instance, |
xTT_Glyph glyph, |
TT_UShort glyphIndex, |
TT_UShort loadFlags ) |
{ |
xTT_Glyph_Cache * node; |
xTT_Glyph_Cache ** inode = &instance.instance->head; |
|
int miss = 0; |
|
while ( 1 ) |
{ |
if (*inode == 0) |
{ |
miss = 1; |
|
node = *inode = calloc(1,sizeof(**inode)); |
|
if (node == 0) |
return TT_Err_Out_Of_Memory; |
|
node->error = TT_New_Glyph(instance.instance->face,&(node->glyph)); |
|
if (node->error == 0) |
node->error = TT_Load_Glyph(instance.instance->instance, |
node->glyph, |
(node->index = glyphIndex), |
(node->flags = loadFlags)); |
|
if (node->error != 0) |
TT_Done_Glyph(node->glyph); |
} |
else |
{ |
node = *inode; |
} |
|
if (glyphIndex < node->index) |
inode = &node->l; |
else if (glyphIndex > node->index) |
inode = &node->r; |
else if (loadFlags < node->flags) |
inode = &node->l; |
else if (loadFlags > node->flags) |
inode = &node->r; |
else |
{ |
static int count [] = { 0, 0 }; |
++count[miss]; |
printf("\r(%s | hit %d | miss %d)",__TIME__,count[0],count[1]); |
glyph.glyph->glyph = &node->glyph; |
return node->error; |
} |
} |
} |
|
void |
xTT_Glyph_Cache_Free ( xTT_Glyph_Cache ** pnode ) |
{ |
xTT_Glyph_Cache * node; |
|
if (pnode == 0) |
return; |
|
node = *pnode; |
|
if (node == 0) |
return; |
|
if (node->l) |
xTT_Glyph_Cache_Free(&node->l); |
|
if (node->r) |
xTT_Glyph_Cache_Free(&node->r); |
|
TT_Done_Glyph(node->glyph); |
free(node); |
|
*pnode = 0; |
} |
|
TT_Error |
xTT_New_Instance ( TT_Face face, |
xTT_Instance * instance ) |
{ |
instance->instance = calloc(1,sizeof(*instance->instance)); |
if (instance->instance == 0) |
return TT_Err_Out_Of_Memory; |
|
instance->instance->face = face; |
instance->instance->head = 0; |
|
return TT_New_Instance(face,&instance->instance->instance); |
} |
|
TT_Error |
xTT_Done_Instance ( xTT_Instance instance ) |
{ |
TT_Error error; |
|
xTT_Glyph_Cache_Free(&instance.instance->head); |
error = TT_Done_Instance(instance.instance->instance); |
|
free(instance.instance); |
|
return error; |
} |
|
TT_Error |
xTT_Get_Instance_Metrics ( xTT_Instance instance, |
TT_Instance_Metrics * imetrics ) |
{ |
return TT_Get_Instance_Metrics(instance.instance->instance,imetrics); |
} |
|
TT_Error |
xTT_Set_Instance_CharSize ( xTT_Instance instance, |
TT_F26Dot6 charsize ) |
{ |
xTT_Glyph_Cache_Free(&instance.instance->head); |
return TT_Set_Instance_CharSize(instance.instance->instance,charsize); |
} |
|
TT_Error |
xTT_Set_Instance_PixelSizes ( xTT_Instance instance, |
TT_UShort pixelWidth, |
TT_UShort pixelHeight, |
TT_F26Dot6 pointSize ) |
{ |
xTT_Glyph_Cache_Free(&instance.instance->head); |
return TT_Set_Instance_PixelSizes(instance.instance->instance, |
pixelWidth, |
pixelHeight, |
pointSize); |
} |
|
TT_Error |
xTT_Set_Instance_Transform_Flags ( xTT_Instance instance, |
TT_Bool rotated, |
TT_Bool stretched ) |
{ |
xTT_Glyph_Cache_Free(&instance.instance->head); |
return TT_Set_Instance_Resolutions(instance.instance->instance, |
rotated, |
stretched); |
} |
|
TT_Error |
xTT_Set_Instance_Resolutions ( xTT_Instance instance, |
TT_UShort xResolution, |
TT_UShort yResolution ) |
{ |
xTT_Glyph_Cache_Free(&instance.instance->head); |
return TT_Set_Instance_Resolutions(instance.instance->instance, |
xResolution, |
yResolution); |
} |
|
TT_Error |
xTT_New_Glyph ( TT_Face face, |
xTT_Glyph * glyph ) |
{ |
glyph->glyph = calloc(1,sizeof(*glyph->glyph)); |
if (glyph->glyph == 0) |
return TT_Err_Out_Of_Memory; |
return 0; |
} |
|
TT_Error |
xTT_Done_Glyph ( xTT_Glyph glyph ) |
{ |
free(glyph.glyph); |
return 0; |
} |
|
TT_Error |
xTT_Load_Glyph ( xTT_Instance instance, |
xTT_Glyph glyph, |
TT_UShort glyphIndex, |
TT_UShort loadFlags ) |
{ |
TT_Error error; |
error = xTT_Glyph_Cache_Find(instance,glyph,glyphIndex,loadFlags); |
TT_Get_Glyph_Outline(*glyph.glyph->glyph, |
&glyph.glyph->outline.outline); |
TT_Get_Outline_BBox(&glyph.glyph->outline.outline, |
&glyph.glyph->outline.bbox); |
return error; |
} |
|
TT_Error |
xTT_Get_Glyph_Metrics ( xTT_Glyph glyph, |
TT_Glyph_Metrics * metrics ) |
{ |
return TT_Get_Glyph_Metrics(*glyph.glyph->glyph,metrics); |
} |
|
TT_Error |
xTT_Get_Glyph_Outline ( xTT_Glyph glyph, |
xTT_Outline * outline ) |
{ |
outline->outline = &glyph.glyph->outline; |
return 0; |
} |
|
TT_Error |
xTT_Get_Outline_BBox ( xTT_Outline * outline, |
TT_BBox * bbox ) |
{ |
*bbox = outline->outline->bbox; |
return 0; |
} |
|
void |
xTT_Transform_Outline ( xTT_Outline * outline, |
TT_Matrix * matrix ) |
{ |
TT_Transform_Outline(&outline->outline->outline,matrix); |
TT_Get_Outline_BBox(&outline->outline->outline, |
&outline->outline->bbox); |
} |
|
TT_Error |
xTT_Get_Glyph_Bitmap ( xTT_Glyph glyph, |
TT_Raster_Map * bitmap, |
TT_F26Dot6 xOffset, |
TT_F26Dot6 yOffset ) |
{ |
return TT_Get_Glyph_Bitmap(*glyph.glyph->glyph, |
bitmap, |
xOffset, |
yOffset); |
} |
|
TT_Error |
xTT_Get_Glyph_Pixmap ( xTT_Glyph glyph, |
TT_Raster_Map * pixmap, |
TT_F26Dot6 xOffset, |
TT_F26Dot6 yOffset ) |
{ |
return TT_Get_Glyph_Pixmap(*glyph.glyph->glyph, |
pixmap, |
xOffset, |
yOffset); |
} |
|
#ifndef xTT_ALIAS |
#define xTT_ALIAS 1 |
#endif |
|
#if xTT_ALIAS |
|
#define TT_Instance xTT_Instance |
#define TT_Outline xTT_Outline |
#define TT_Glyph xTT_Glyph |
|
#define TT_New_Instance xTT_New_Instance |
#define TT_Done_Instance xTT_Done_Instance |
|
#define TT_Get_Instance_Metrics xTT_Get_Instance_Metrics |
#define TT_Set_Instance_CharSize xTT_Set_Instance_CharSize |
#define TT_Set_Instance_PixelSizes xTT_Set_Instance_PixelSizes |
#define TT_Set_Instance_Transform_Flags xTT_Set_Instance_Transform_Flags |
#define TT_Set_Instance_Resolutions xTT_Set_Instance_Resolutions |
|
#define TT_New_Glyph xTT_New_Glyph |
#define TT_Done_Glyph xTT_Done_Glyph |
|
#define TT_Load_Glyph xTT_Load_Glyph |
#define TT_Get_Glyph_Metrics xTT_Get_Glyph_Metrics |
#define TT_Get_Glyph_Outline xTT_Get_Glyph_Outline |
#define TT_Get_Glyph_Bitmap xTT_Get_Glyph_Bitmap |
#define TT_Get_Glyph_Pixmap xTT_Get_Glyph_Pixmap |
|
#define TT_Get_Outline_BBox xTT_Get_Outline_BBox |
#define TT_Transform_Outline xTT_Transform_Outline |
|
#endif |
|
/************/ |
|
#ifndef FREETYPE_FONT_DIR |
#define FREETYPE_FONT_DIR "/usr/local/microwin/fonts" |
#endif |
|
#if TT_FREETYPE_MAJOR != 1 | TT_FREETYPE_MINOR < 3 |
#error "You must link with freetype lib version 1.3.x +, and not freetype 2. \ |
Download it at http://www.freetype.org or http://microwindows.org" |
#endif |
|
#ifndef MWFREETYPEFONT_CACHE |
#define MWFREETYPEFONT_CACHE 1 |
#endif |
|
#if MWFREETYPEFONT_CACHE |
|
#ifndef MWFREETYPEFONT_CACHEBITMAP |
#define MWFREETYPEFONT_CACHEBITMAP 1 |
#endif |
|
typedef struct mwfreetypefontcache { |
unsigned curchar; |
void * buffer; |
#if MWFREETYPEFONT_CACHEBITMAP |
MWPIXELVAL fg; |
MWPIXELVAL bg; |
MWBOOL usebg; |
void * bitmap; |
#endif |
struct mwfreetypefontcache * l; |
struct mwfreetypefontcache * r; |
} MWFREETYPEFONTCACHE; |
|
#endif |
|
typedef struct { |
|
PMWFONTPROCS fontprocs; /* common hdr*/ |
MWCOORD fontsize; |
int fontrotation; |
int fontattr; |
|
TT_Face face; /* freetype stuff*/ |
TT_Instance instance; |
TT_CharMap char_map; |
TT_Kerning directory; |
TT_Matrix matrix; |
TT_Glyph glyph; |
MWBOOL can_kern; |
short last_glyph_code; |
short last_pen_pos; |
|
#if MWFREETYPEFONT_CACHE |
MWFREETYPEFONTCACHE * glyph_cache; |
#endif |
|
} MWFREETYPEFONT, *PMWFREETYPEFONT; |
|
static int freetype_init(PSD psd); |
static PMWFREETYPEFONT freetype_createfont(const char *name, MWCOORD height, |
int attr); |
static MWBOOL freetype_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo); |
static void freetype_gettextsize(PMWFONT pfont, const void *text, |
int cc, MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase); |
static void freetype_destroyfont(PMWFONT pfont); |
static void freetype_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags); |
static void freetype_setfontsize(PMWFONT pfont, MWCOORD fontsize); |
static void freetype_setfontrotation(PMWFONT pfont, int tenthdegrees); |
|
/* handling routines for MWFREETYPEFONT*/ |
static MWFONTPROCS freetype_procs = { |
MWTF_UC16, /* routines expect unicode 16*/ |
freetype_getfontinfo, |
freetype_gettextsize, |
NULL, /* gettextbits*/ |
freetype_destroyfont, |
freetype_drawtext, |
freetype_setfontsize, |
freetype_setfontrotation, |
NULL, /* setfontattr*/ |
}; |
|
static TT_Engine engine; /* THE ONLY freetype engine */ |
#endif /* HAVE_FREETYPE_SUPPORT*/ |
|
#if HAVE_HZK_SUPPORT |
/* |
* 12x12 and 16x16 ascii and chinese fonts |
* Big5 and GB2312 encodings supported |
*/ |
#define MAX_PATH 256 |
typedef struct { |
int width; |
int height; |
int size; |
unsigned long use_count; |
char * pFont; |
char file[MAX_PATH + 1]; |
} HZKFONT; |
|
static int use_big5=1; |
static HZKFONT CFont[2]; /* font cache*/ |
static HZKFONT AFont[2]; /* font cache*/ |
|
/*jmt: moved inside MWHZKFONT*/ |
static int afont_width = 8; |
static int cfont_width = 16; |
static int font_height = 16; |
static char *afont_address; |
static char *cfont_address; |
|
typedef struct { |
PMWFONTPROCS fontprocs; /* common hdr*/ |
MWCOORD fontsize; |
int fontrotation; |
int fontattr; |
|
HZKFONT CFont; /* hzkfont stuff */ |
HZKFONT AFont; |
int afont_width; |
int cfont_width; |
int font_height; |
char *afont_address; |
char *cfont_address; |
} MWHZKFONT, *PMWHZKFONT; |
|
static int hzk_init(PSD psd); |
static PMWHZKFONT hzk_createfont(const char *name, MWCOORD height,int fontattr); |
static MWBOOL hzk_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo); |
static void hzk_gettextsize(PMWFONT pfont, const void *text, |
int cc, MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase); |
/*static void hzk_gettextbits(PMWFONT pfont, int ch, IMAGEBITS *retmap, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase);*/ |
static void hzk_destroyfont(PMWFONT pfont); |
static void hzk_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags); |
static void hzk_setfontsize(PMWFONT pfont, MWCOORD fontsize); |
/*static void hzk_setfontrotation(PMWFONT pfont, int tenthdegrees);*/ |
|
/* handling routines for MWHZKFONT*/ |
static MWFONTPROCS hzk_procs = { |
MWTF_ASCII, /* routines expect ASCII*/ |
hzk_getfontinfo, |
hzk_gettextsize, |
NULL, /* hzk_gettextbits*/ |
hzk_destroyfont, |
hzk_drawtext, |
hzk_setfontsize, |
NULL, /* setfontrotation*/ |
NULL, /* setfontattr*/ |
}; |
|
static int |
UC16_to_GB(const unsigned char *uc16, int cc, unsigned char *ascii); |
#endif /* HAVE_HZK_SUPPORT*/ |
|
static PMWFONT gr_pfont; /* current font*/ |
|
/* temp extern decls*/ |
extern MWPIXELVAL gr_foreground; |
extern MWPIXELVAL gr_background; |
extern MWBOOL gr_usebg; |
|
static int utf8_to_utf16(const unsigned char *utf8, int cc, |
unsigned short *unicode16); |
|
#if FONTMAPPER |
/* entry point for font selection*/ |
int select_font(const PMWLOGFONT plogfont, char *physname); |
#endif |
|
/* |
* Set the font for future calls. |
*/ |
PMWFONT |
GdSetFont(PMWFONT pfont) |
{ |
PMWFONT oldfont = gr_pfont; |
gr_pfont = pfont; |
return oldfont; |
} |
|
/* |
* Select a font, based on various parameters. |
* If plogfont is specified, name and height parms are ignored |
* and instead used from MWLOGFONT. |
* |
* If height is 0, return builtin font from passed name. |
* Otherwise find builtin font best match based on height. |
*/ |
PMWFONT |
GdCreateFont(PSD psd, const char *name, MWCOORD height, |
const PMWLOGFONT plogfont) |
{ |
int i; |
int fontht; |
int fontno; |
int fontclass; |
int fontattr = 0; |
PMWFONT pfont; |
PMWCOREFONT pf = psd->builtin_fonts; |
MWFONTINFO fontinfo; |
MWSCREENINFO scrinfo; |
char fontname[128]; |
|
GdGetScreenInfo(psd, &scrinfo); |
|
/* if plogfont not specified, use name and height*/ |
if (!plogfont) { |
if (!name) |
name = MWFONT_SYSTEM_VAR; |
strcpy(fontname, name); |
fontclass = MWLF_CLASS_ANY; |
} else { |
#if FONTMAPPER |
/* otherwise, use MWLOGFONT name and height*/ |
fontclass = select_font(plogfont, fontname); |
#else |
if (!name) |
name = MWFONT_SYSTEM_VAR; |
strcpy(fontname, name); |
fontclass = MWLF_CLASS_ANY; |
#endif |
height = plogfont->lfHeight; |
if (plogfont->lfUnderline) |
fontattr = MWTF_UNDERLINE; |
} |
height = abs(height); |
|
if (!fontclass) |
goto first; |
|
/* use builtin screen fonts, FONT_xxx, if height is 0 */ |
if (height == 0 || fontclass == MWLF_CLASS_ANY || |
fontclass == MWLF_CLASS_BUILTIN) { |
for(i = 0; i < scrinfo.fonts; ++i) { |
if(!strcmpi(pf[i].name, fontname)) { |
pf[i].fontsize = pf[i].cfont->height; |
pf[i].fontattr = fontattr; |
return (PMWFONT)&pf[i]; |
} |
} |
|
/* return first builtin font*/ |
if (height == 0 || fontclass == MWLF_CLASS_BUILTIN) |
goto first; |
} |
|
#if HAVE_HZK_SUPPORT |
/* Make sure the library is initialized */ |
if (hzk_init(psd)) { |
pfont = (PMWFONT)hzk_createfont(name, height, fontattr); |
if(pfont) |
return pfont; |
printf("hzk_createfont: %s not found\n", name); |
} |
#endif |
|
#if HAVE_FREETYPE_SUPPORT |
if (fontclass == MWLF_CLASS_ANY || fontclass == MWLF_CLASS_FREETYPE) { |
if (freetype_init(psd)) { |
/* auto antialias for height > 14 for kaffe*/ |
if (plogfont && plogfont->lfHeight > 14 && |
plogfont->lfQuality) |
fontattr |= MWTF_ANTIALIAS; |
|
pfont = (PMWFONT)freetype_createfont(fontname, height, |
fontattr); |
if(pfont) { |
/* temp kaffe kluge*/ |
pfont->fontattr |= FS_FREETYPE; |
return pfont; |
} |
DPRINTF("freetype_createfont: %s,%d not found\n", |
fontname, height); |
} |
} |
#endif |
|
#if HAVE_T1LIB_SUPPORT |
if (fontclass == MWLF_CLASS_ANY || fontclass == MWLF_CLASS_T1LIB) { |
if (t1lib_init(psd)) { |
pfont = (PMWFONT)t1lib_createfont(fontname, height, |
fontattr); |
if(pfont) |
return pfont; |
DPRINTF("t1lib_createfont: %s,%d not found\n", |
fontname, height); |
} |
} |
#endif |
|
/* find builtin font closest in height*/ |
if(height != 0) { |
fontno = 0; |
height = abs(height); |
fontht = MAX_MWCOORD; |
for(i = 0; i < scrinfo.fonts; ++i) { |
pfont = (PMWFONT)&pf[i]; |
GdGetFontInfo(pfont, &fontinfo); |
if(fontht > abs(height-fontinfo.height)) { |
fontno = i; |
fontht = abs(height-fontinfo.height); |
} |
} |
pf[fontno].fontsize = pf[fontno].cfont->height; |
pf[fontno].fontattr = fontattr; |
return (PMWFONT)&pf[fontno]; |
} |
|
first: |
/* Return first builtin font*/ |
pf->fontsize = pf->cfont->height; |
pf->fontattr = fontattr; |
return (PMWFONT)&pf[0]; |
} |
|
/* Set the font size for the passed font*/ |
MWCOORD |
GdSetFontSize(PMWFONT pfont, MWCOORD fontsize) |
{ |
MWCOORD oldfontsize = pfont->fontsize; |
|
pfont->fontsize = fontsize; |
|
if (pfont->fontprocs->SetFontSize) |
pfont->fontprocs->SetFontSize(pfont, fontsize); |
|
return oldfontsize; |
} |
|
/* Set the font rotation angle in tenths of degrees for the passed font*/ |
int |
GdSetFontRotation(PMWFONT pfont, int tenthdegrees) |
{ |
MWCOORD oldrotation = pfont->fontrotation; |
|
pfont->fontrotation = tenthdegrees; |
|
if (pfont->fontprocs->SetFontRotation) |
pfont->fontprocs->SetFontRotation(pfont, tenthdegrees); |
|
return oldrotation; |
} |
|
/* |
* Set/reset font attributes (MWTF_KERNING, MWTF_ANTIALIAS) |
* for the passed font. |
*/ |
int |
GdSetFontAttr(PMWFONT pfont, int setflags, int clrflags) |
{ |
MWCOORD oldattr = pfont->fontattr; |
|
pfont->fontattr &= ~clrflags; |
pfont->fontattr |= setflags; |
|
if (pfont->fontprocs->SetFontAttr) |
pfont->fontprocs->SetFontAttr(pfont, setflags, clrflags); |
|
return oldattr; |
} |
|
/* Unload and deallocate font*/ |
void |
GdDestroyFont(PMWFONT pfont) |
{ |
if (pfont->fontprocs->DestroyFont) |
pfont->fontprocs->DestroyFont(pfont); |
} |
|
/* Return information about a specified font*/ |
MWBOOL |
GdGetFontInfo(PMWFONT pfont, PMWFONTINFO pfontinfo) |
{ |
if(!pfont || !pfont->fontprocs->GetFontInfo) |
return FALSE; |
|
return pfont->fontprocs->GetFontInfo(pfont, pfontinfo); |
} |
|
/* |
* Convert from one encoding to another |
* Input cc and returned cc is character count, not bytes |
* Return < 0 on error or can't translate |
*/ |
int |
GdConvertEncoding(const void *istr, int iflags, int cc, void *ostr, int oflags) |
{ |
const unsigned char *istr8; |
const unsigned short *istr16; |
const unsigned long *istr32; |
unsigned char *ostr8; |
unsigned short *ostr16; |
unsigned long *ostr32; |
unsigned int ch; |
int icc; |
unsigned short buf16[512]; |
|
iflags &= MWTF_PACKMASK; |
oflags &= MWTF_PACKMASK; |
|
/* allow -1 for len with ascii*/ |
if(cc == -1 && (iflags == MWTF_ASCII)) |
cc = strlen((char *)istr); |
|
/* first check for utf8 input encoding*/ |
if(iflags == MWTF_UTF8) { |
/* we've only got uc16 now so convert to uc16...*/ |
cc = utf8_to_utf16((unsigned char *)istr, cc, |
oflags==MWTF_UC16?(unsigned short*) ostr: buf16); |
|
if(oflags == MWTF_UC16 || cc < 0) |
return cc; |
|
/* will decode again to requested format (probably ascii)*/ |
iflags = MWTF_UC16; |
istr = buf16; |
} |
|
#if HAVE_HZK_SUPPORT |
if(iflags == MWTF_UC16 && oflags == MWTF_ASCII) { |
/* only support uc16 convert to ascii now...*/ |
cc = UC16_to_GB( istr, cc, ostr); |
return cc; |
} |
#endif |
|
icc = cc; |
istr8 = istr; |
istr16 = istr; |
istr32 = istr; |
ostr8 = ostr; |
ostr16 = ostr; |
ostr32 = ostr; |
|
/* Convert between formats. Note that there's no error |
* checking here yet. |
*/ |
while(--icc >= 0) { |
switch(iflags) { |
default: |
ch = *istr8++; |
break; |
case MWTF_UC16: |
ch = *istr16++; |
break; |
case MWTF_UC32: |
ch = *istr32++; |
} |
switch(oflags) { |
default: |
*ostr8++ = (unsigned char)ch; |
break; |
case MWTF_UC16: |
*ostr16++ = (unsigned short)ch; |
break; |
case MWTF_UC32: |
*ostr32++ = ch; |
} |
} |
return cc; |
} |
|
/* Get the width and height of passed text string in the passed font*/ |
void |
GdGetTextSize(PMWFONT pfont, const void *str, int cc, MWCOORD *pwidth, |
MWCOORD *pheight, MWCOORD *pbase, int flags) |
{ |
const void * text; |
unsigned long buf[256]; |
int defencoding = pfont->fontprocs->encoding; |
|
/* convert encoding if required*/ |
if((flags & MWTF_PACKMASK) != defencoding) { |
cc = GdConvertEncoding(str, flags, cc, buf, defencoding); |
flags &= ~MWTF_PACKMASK; |
flags |= defencoding; |
text = buf; |
} else text = str; |
|
if(cc == -1 && (flags & MWTF_PACKMASK) == MWTF_ASCII) |
cc = strlen((char *)str); |
|
if(cc <= 0 || !pfont->fontprocs->GetTextSize) { |
*pwidth = *pheight = *pbase = 0; |
return; |
} |
|
/* calc height and width of string*/ |
pfont->fontprocs->GetTextSize(pfont, text, cc, pwidth, pheight, pbase); |
} |
|
/* Draw a text string at a specifed coordinates in the foreground color |
* (and possibly the background color), applying clipping if necessary. |
* The background color is only drawn if the gr_usebg flag is set. |
* Use the current font. |
*/ |
void |
GdText(PSD psd, MWCOORD x, MWCOORD y, const void *str, int cc, int flags) |
{ |
const void * text; |
unsigned long buf[256]; |
int defencoding = gr_pfont->fontprocs->encoding; |
|
/* convert encoding if required*/ |
if((flags & MWTF_PACKMASK) != defencoding) { |
cc = GdConvertEncoding(str, flags, cc, buf, defencoding); |
flags &= ~MWTF_PACKMASK; |
flags |= defencoding; |
text = buf; |
} else text = str; |
|
if(cc == -1 && (flags & MWTF_PACKMASK) == MWTF_ASCII) |
cc = strlen((char *)str); |
|
if(cc <= 0 || !gr_pfont->fontprocs->DrawText) |
return; |
|
/* draw text string*/ |
gr_pfont->fontprocs->DrawText(gr_pfont, psd, x, y, text, cc, flags); |
} |
|
/* |
* Draw ascii text using COREFONT type font. |
*/ |
void |
corefont_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags) |
{ |
const unsigned char *str = text; |
MWCOORD width; /* width of text area */ |
MWCOORD height; /* height of text area */ |
MWCOORD base; /* baseline of text*/ |
MWCOORD startx, starty; |
/* bitmap for characters */ |
MWIMAGEBITS bitmap[MAX_CHAR_HEIGHT*MAX_CHAR_WIDTH/MWIMAGE_BITSPERIMAGE]; |
|
pfont->fontprocs->GetTextSize(pfont, str, cc, &width, &height, &base); |
|
if(flags & MWTF_BASELINE) |
y -= base; |
else if(flags & MWTF_BOTTOM) |
y -= (height - 1); |
startx = x; |
starty = y + base; |
|
switch (GdClipArea(psd, x, y, x + width - 1, y + height - 1)) { |
case CLIP_VISIBLE: |
/* |
* For size considerations, there's no low-level text |
* draw, so we've got to draw all text |
* with per-point clipping for the time being |
if (gr_usebg) |
psd->FillRect(psd, x, y, x + width - 1, y + height - 1, |
gr_background); |
psd->DrawText(psd, x, y, str, cc, gr_foreground, pfont); |
GdFixCursor(psd); |
return; |
*/ |
break; |
|
case CLIP_INVISIBLE: |
return; |
} |
|
/* Get the bitmap for each character individually, and then display |
* them using clipping for each one. |
*/ |
while (--cc >= 0 && x < psd->xvirtres) { |
unsigned int ch = *str++; |
#if HAVE_BIG5_SUPPORT |
/* chinese big5 decoding*/ |
if (ch >= 0xA1 && ch <= 0xF9 && cc >= 1 && |
((*str >= 0x40 && *str <= 0x7E) || |
(*str >= 0xA1 && *str <= 0xFE)) ) { |
ch = (ch << 8) | *str++; |
--cc; |
} |
#endif |
#if HAVE_GB2312_SUPPORT |
/* chinese gb2312 decoding*/ |
if (ch >= 0xA1 && ch < 0xF8 && cc >= 1 && |
*str >= 0xA1 && *str < 0xFF) { |
ch = (ch << 8) | *str++; |
--cc; |
} |
#endif |
pfont->fontprocs->GetTextBits(pfont, ch, bitmap, &width, |
&height, &base); |
|
/* note: change to bitmap*/ |
GdBitmap(psd, x, y, width, height, bitmap); |
x += width; |
} |
|
if (pfont->fontattr & MWTF_UNDERLINE) |
GdLine(psd, startx, starty, x, starty, FALSE); |
|
GdFixCursor(psd); |
} |
|
#if HAVE_T1LIB_SUPPORT | HAVE_FREETYPE_SUPPORT |
/* |
* Produce blend table from src and dst based on passed alpha table |
* Used because we don't quite yet have GdArea with alphablending, |
* so we pre-blend fg/bg colors for fade effect. |
*/ |
static void |
alphablend(PSD psd, OUTPIXELVAL *out, MWPIXELVAL src, MWPIXELVAL dst, |
unsigned char *alpha, int count) |
{ |
unsigned int a, d; |
unsigned char r, g, b; |
MWCOLORVAL palsrc, paldst; |
extern MWPALENTRY gr_palette[256]; |
|
while (--count >= 0) { |
a = *alpha++; |
|
#define BITS(pixel,shift,mask) (((pixel)>>shift)&(mask)) |
if(a == 0) |
*out++ = dst; |
else if(a == 255) |
*out++ = src; |
else |
switch(psd->pixtype) { |
case MWPF_TRUECOLOR0888: |
case MWPF_TRUECOLOR888: |
d = BITS(dst, 16, 0xff); |
r = (unsigned char)(((BITS(src, 16, 0xff) - d)*a)>>8) + d; |
d = BITS(dst, 8, 0xff); |
g = (unsigned char)(((BITS(src, 8, 0xff) - d)*a)>>8) + d; |
d = BITS(dst, 0, 0xff); |
b = (unsigned char)(((BITS(src, 0, 0xff) - d)*a)>>8) + d; |
*out++ = (r << 16) | (g << 8) | b; |
break; |
|
case MWPF_TRUECOLOR565: |
d = BITS(dst, 11, 0x1f); |
r = (unsigned char)(((BITS(src, 11, 0x1f) - d)*a)>>8) + d; |
d = BITS(dst, 5, 0x3f); |
g = (unsigned char)(((BITS(src, 5, 0x3f) - d)*a)>>8) + d; |
d = BITS(dst, 0, 0x1f); |
b = (unsigned char)(((BITS(src, 0, 0x1f) - d)*a)>>8) + d; |
*out++ = (r << 11) | (g << 5) | b; |
break; |
|
case MWPF_TRUECOLOR555: |
d = BITS(dst, 10, 0x1f); |
r = (unsigned char)(((BITS(src, 10, 0x1f) - d)*a)>>8) + d; |
d = BITS(dst, 5, 0x1f); |
g = (unsigned char)(((BITS(src, 5, 0x1f) - d)*a)>>8) + d; |
d = BITS(dst, 0, 0x1f); |
b = (unsigned char)(((BITS(src, 0, 0x1f) - d)*a)>>8) + d; |
*out++ = (r << 10) | (g << 5) | b; |
break; |
|
case MWPF_TRUECOLOR332: |
d = BITS(dst, 5, 0x07); |
r = (unsigned char)(((BITS(src, 5, 0x07) - d)*a)>>8) + d; |
d = BITS(dst, 2, 0x07); |
g = (unsigned char)(((BITS(src, 2, 0x07) - d)*a)>>8) + d; |
d = BITS(dst, 0, 0x03); |
b = (unsigned char)(((BITS(src, 0, 0x03) - d)*a)>>8) + d; |
*out++ = (r << 5) | (g << 2) | b; |
break; |
|
case MWPF_PALETTE: |
/* reverse lookup palette entry for blend ;-)*/ |
palsrc = GETPALENTRY(gr_palette, src); |
paldst = GETPALENTRY(gr_palette, dst); |
d = REDVALUE(paldst); |
r = (unsigned char)(((REDVALUE(palsrc) - d)*a)>>8) + d; |
d = GREENVALUE(paldst); |
g = (unsigned char)(((GREENVALUE(palsrc) - d)*a)>>8) + d; |
d = BLUEVALUE(paldst); |
b = (unsigned char)(((BLUEVALUE(palsrc) - d)*a)>>8) + d; |
*out++ = GdFindNearestColor(gr_palette, (int)psd->ncolors, |
MWRGB(r, g, b)); |
break; |
} |
} |
} |
#endif /*HAVE_T1LIB_SUPPORT | HAVE_FREETYPE_SUPPORT*/ |
|
#if HAVE_T1LIB_SUPPORT |
/* contributed by Vidar Hokstad*/ |
|
static int |
t1lib_init(PSD psd) |
{ |
static int inited = 0; |
|
if (inited) |
return 1; |
|
T1_SetBitmapPad(8); |
if (!T1_InitLib(0)) |
return 0; |
#ifdef T1LIB_USE_AA_HIGH |
T1_AASetLevel(T1_AA_HIGH); |
#else |
T1_AASetLevel(T1_AA_LOW); |
#endif |
#if 0 |
/* kluge: this is required if 16bpp drawarea driver is used*/ |
if(psd->bpp == 16) |
T1_AASetBitsPerPixel(16); |
else |
#endif |
T1_AASetBitsPerPixel(sizeof(MWPIXELVAL)*8); |
|
inited = 1; |
return 1; |
} |
|
static PMWT1LIBFONT |
t1lib_createfont(const char *name, MWCOORD height, int attr) |
{ |
PMWT1LIBFONT pf; |
int id; |
char * p; |
char buf[256]; |
|
/* match name against t1 font id's from t1 font database*/ |
for(id=0; id<T1_Get_no_fonts(); ++id) { |
strncpy(buf, T1_GetFontFileName(id), sizeof(buf)); |
|
/* remove extension*/ |
for(p=buf; *p; ++p) { |
if(*p == '.') { |
*p = 0; |
break; |
} |
} |
|
if(!strcmpi(name, buf)) { |
/* allocate font structure*/ |
pf = (PMWT1LIBFONT)calloc(sizeof(MWT1LIBFONT), 1); |
if (!pf) |
return NULL; |
pf->fontprocs = &t1lib_procs; |
GdSetFontSize((PMWFONT)pf, height); |
GdSetFontRotation((PMWFONT)pf, 0); |
GdSetFontAttr((PMWFONT)pf, attr, 0); |
pf->fontid = id; |
return pf; |
} |
} |
return NULL; |
} |
|
/* |
* Draw ascii text string using T1LIB type font |
*/ |
static void |
t1lib_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags) |
{ |
PMWT1LIBFONT pf = (PMWT1LIBFONT)pfont; |
const unsigned char *str = text; |
MWCOORD width; /* width of text area */ |
MWCOORD height; /* height of text area */ |
MWCOORD underliney; |
GLYPH * g; /* T1lib glyph structure. Memory handling by T1lib */ |
#ifdef T1LIB_USE_AA_HIGH |
OUTPIXELVAL gvals[17]; |
|
/* Blending array for antialiasing. The steeper the values increase |
* near the end, the sharper the characters look, but also more jagged |
*/ |
static unsigned char blend[17] = { |
0x00, 0x00, 0x04, 0x0c, 0x10, 0x14, 0x18, 0x20, |
0x30, 0x38, 0x40, 0x50, 0x70, 0x80, 0xa0, 0xc0, 0xff |
}; |
#else |
OUTPIXELVAL gvals[5]; |
static unsigned char blend[5] = { 0x00, 0x44, 0x88, 0xcc, 0xff }; |
#endif |
|
/* Check if we should throw out some fonts */ |
|
if (pf->fontattr&MWTF_ANTIALIAS) { |
#ifdef T1LIB_USE_AA_HIGH |
alphablend(psd, gvals, gr_foreground, gr_background, blend, 17); |
T1_AAHSetGrayValues(gvals); |
#else |
alphablend(psd, gvals, gr_foreground, gr_background, blend, 5); |
T1_AASetGrayValues(gvals[0],gvals[1],gvals[2],gvals[3],gvals[4]); |
#endif |
g = T1_AASetString(pf->fontid,(char *)str,cc,0, |
(pf->fontattr&MWTF_KERNING)? T1_KERNING: 0, |
pf->fontsize * 1.0, 0); |
|
if (g && g->bits) { |
/*MWPIXELVAL save = gr_background;*/ |
width = g->metrics.rightSideBearing - g->metrics.leftSideBearing; |
height = g->metrics.ascent - g->metrics.descent; |
|
if(flags & MWTF_BASELINE) |
y -= g->metrics.ascent; |
else if(flags & MWTF_BOTTOM) |
y -= (height - 1); |
underliney = y + g->metrics.ascent; |
|
/* FIXME: Looks damn ugly if usebg is false. |
* Will be handled when using alphablending in GdArea... |
*/ |
/* clipping handled in GdArea*/ |
/*FIXME kluge for transparency*/ |
/*gr_background = gr_foreground + 1;*/ |
/*gr_usebg = 0;*/ |
GdArea(psd,x,y, width, height, g->bits, MWPF_PIXELVAL); |
/*gr_background = save;*/ |
|
if (pf->fontattr & MWTF_UNDERLINE) |
GdLine(psd, x, underliney, x+width, underliney, FALSE); |
|
} |
} else { |
/* Do non-aa drawing */ |
g = T1_SetString(pf->fontid,(char *)str,cc,0, |
(pf->fontattr&MWTF_KERNING)? T1_KERNING: 0, |
pf->fontsize * 1.0, 0); |
|
if (g && g->bits) { |
unsigned char * b; |
int xoff; |
int maxy; |
int xmod; |
|
/* I'm sure this sorry excuse for a bitmap rendering routine can |
* be optimized quite a bit ;) |
*/ |
width = g->metrics.rightSideBearing - g->metrics.leftSideBearing; |
height = g->metrics.ascent - g->metrics.descent; |
|
if(flags & MWTF_BASELINE) |
y -= g->metrics.ascent; |
else if(flags & MWTF_BOTTOM) |
y -= (height - 1); |
underliney = y + g->metrics.ascent; |
|
b = g->bits; |
maxy = y + height; |
|
/* if ((x + width) > psd->xvirtres) { |
xmod = (x + width - psd->xvirtres + 7) >> 3; |
width = width + x + width - psd->xvirtres; |
} else xmod = 0; |
*/ |
xmod = 0; |
while (y < maxy) { |
unsigned char data; |
xoff = 0; |
while (xoff < width ) { |
if (!(xoff % 8)) { |
data = *b; |
b++; |
} |
|
if (GdClipPoint(psd, x+xoff,y)) { |
if (gr_usebg) { |
psd->DrawPixel(psd,x+xoff,y, |
data & (1 << (xoff % 8)) ? |
gr_foreground : gr_background); |
} else if (data & (1 << (xoff % 8))) { |
psd->DrawPixel(psd,x+xoff,y, gr_foreground); |
} |
} |
xoff++; |
} |
b += xmod; |
y++; |
} |
if (pf->fontattr & MWTF_UNDERLINE) |
GdLine(psd, x, underliney, x+xoff, underliney, FALSE); |
} |
} |
|
if (g && g->bits) { |
/* Save some memory */ |
free(g->bits); |
g->bits = 0; /* Make sure T1lib doesnt try to free it again */ |
} |
|
GdFixCursor(psd); |
} |
|
static MWBOOL |
t1lib_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo) |
{ |
int i; |
MWCOORD width, height, baseline; |
|
/* FIXME, guess all sizes*/ |
GdGetTextSize(pfont, "A", 1, &width, &height, &baseline, MWTF_ASCII); |
pfontinfo->height = height; |
pfontinfo->maxwidth = width; |
pfontinfo->baseline = 0; |
pfontinfo->firstchar = 32; |
pfontinfo->lastchar = 255; |
pfontinfo->fixed = TRUE; |
for(i=0; i<256; ++i) |
pfontinfo->widths[i] = width; |
return TRUE; |
} |
|
/* Get the width and height of passed text string in the current font*/ |
static void |
t1lib_gettextsize(PMWFONT pfont, const void *text, int cc, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase) |
{ |
PMWT1LIBFONT pf = (PMWT1LIBFONT)pfont; |
const unsigned char * str = text; |
GLYPH * g; |
|
g = T1_SetString(pf->fontid, (char *)str, cc, 0, |
(pf->fontattr&MWTF_KERNING)? T1_KERNING: 0, pf->fontsize * 1.0, 0); |
*pwidth = g->metrics.rightSideBearing - g->metrics.leftSideBearing; |
*pheight = g->metrics.ascent - g->metrics.descent; |
if(g && g->bits) { |
free(g->bits); |
g->bits = 0; |
} |
#if 0 |
BBox b; |
|
/* FIXME. Something is *VERY* wrong here */ |
b = T1_GetStringBBox(pf->fontid, str, cc, 0, (pf->fontattr&MWTF_KERNING)?T1_KERNING:0); |
|
DPRINTF("b.urx = %d, b.llx = %d\n",b.urx, b.llx); |
DPRINTF("b.ury = %d, b.lly = %d\n",b.ury, b.lly); |
*pwidth = (b.urx - b.llx); |
*pheight = (b.lly - b.ury); |
#endif |
} |
|
static void |
t1lib_destroyfont(PMWFONT pfont) |
{ |
PMWT1LIBFONT pf = (PMWT1LIBFONT)pfont; |
|
T1_DeleteAllSizes(pf->fontid); |
free(pf); |
} |
|
#endif /* HAVE_T1LIB_SUPPORT*/ |
|
#if HAVE_FREETYPE_SUPPORT |
static OUTPIXELVAL gray_palette[5]; |
|
static int |
freetype_init(PSD psd) |
{ |
static int inited = 0; |
|
if (inited) |
return 1; |
|
/* Init freetype library */ |
if (TT_Init_FreeType (&engine) != TT_Err_Ok) { |
return 0; |
} |
|
/* Init kerning extension */ |
if (TT_Init_Kerning_Extension (engine) != TT_Err_Ok) |
return 0; |
|
inited = 1; |
return 1; |
} |
|
static PMWFREETYPEFONT |
freetype_createfont(const char *name, MWCOORD height, int attr) |
{ |
PMWFREETYPEFONT pf; |
unsigned short i, n; |
unsigned short platform, encoding; |
TT_Face_Properties properties; |
char * p; |
char fontname[128]; |
|
/* check for pathname prefix*/ |
if (strchr(name, '/') != NULL) |
strcpy(fontname, name); |
else { |
strcpy(fontname, FREETYPE_FONT_DIR); |
strcat(fontname, "/"); |
strcat(fontname, name); |
} |
|
/* check for extension*/ |
if ((p = strrchr(fontname, '.')) == NULL || |
strcmp(p, ".ttf") != 0) { |
strcat(fontname, ".ttf"); |
} |
|
/* allocate font structure*/ |
pf = (PMWFREETYPEFONT)calloc(sizeof(MWFREETYPEFONT), 1); |
if (!pf) |
return NULL; |
pf->fontprocs = &freetype_procs; |
|
/* Load face */ |
if (TT_Open_Face (engine, fontname, &pf->face) != TT_Err_Ok) |
goto out; |
|
/* Load first kerning table */ |
pf->can_kern = TRUE; |
if (TT_Load_Kerning_Table (pf->face, 0) != TT_Err_Ok) |
pf->can_kern = FALSE; |
else { |
if (TT_Get_Kerning_Directory (pf->face, &pf->directory) |
!= TT_Err_Ok) |
pf->can_kern = FALSE; |
else { |
/* Support only version 0 kerning table ... */ |
if ((pf->directory.version != 0) || |
(pf->directory.nTables <= 0) || |
(pf->directory.tables->loaded != 1) || |
(pf->directory.tables->version != 0) || |
(pf->directory.tables->t.kern0.nPairs <= 0)) |
pf->can_kern = FALSE; |
} |
} |
|
/* get face properties and allocate preload arrays */ |
TT_Get_Face_Properties (pf->face, &properties); |
|
#if 0 |
/* |
* Use header information for ascent and descent |
* to compute scaled ascent/descent for current font height. |
*/ |
h = properties.os2->sTypoAscender - properties.os2->sTypoDescender |
+ properties.os2->sTypoLineGap; |
ascent = properties.os2->sTypoAscender |
+ properties.os2->sTypoLineGap/2; |
pf->ascent = (ascent * height + h/2) / h; |
pf->descent = height - pf->ascent; |
#endif |
/* Create a glyph container */ |
if (TT_New_Glyph (pf->face, &pf->glyph) != TT_Err_Ok) |
goto out; |
|
/* create instance */ |
if (TT_New_Instance (pf->face, &pf->instance) != TT_Err_Ok) |
goto out; |
|
/* Set the instance resolution */ |
if (TT_Set_Instance_Resolutions (pf->instance, 96, 96) != TT_Err_Ok) |
goto out; |
|
/* Look for a Unicode charmap: Windows flavor of Apple flavor only */ |
n = properties.num_CharMaps; |
|
for (i = 0; i < n; i++) { |
TT_Get_CharMap_ID (pf->face, i, &platform, &encoding); |
if (((platform == TT_PLATFORM_MICROSOFT) && |
(encoding == TT_MS_ID_UNICODE_CS)) || |
((platform == TT_PLATFORM_APPLE_UNICODE) && |
(encoding == TT_APPLE_ID_DEFAULT))) |
{ |
TT_Get_CharMap (pf->face, i, &pf->char_map); |
i = n + 1; |
} |
} |
if (i == n) { |
DPRINTF("freetype_createfont: no unicode map table\n"); |
goto out; |
} |
|
GdSetFontSize((PMWFONT)pf, height); |
GdSetFontRotation((PMWFONT)pf, 0); |
GdSetFontAttr((PMWFONT)pf, attr, 0); |
|
return pf; |
|
out: |
free(pf); |
return NULL; |
} |
|
static int |
compute_kernval(PMWFREETYPEFONT pf, short current_glyph_code) |
{ |
int i = 0; |
int kernval; |
int nPairs = pf->directory.tables->t.kern0.nPairs; |
TT_Kern_0_Pair *pair = pf->directory.tables->t.kern0.pairs; |
|
if (pf->last_glyph_code != -1) { |
while ((pair->left != pf->last_glyph_code) |
&& (pair->right != current_glyph_code)) |
{ |
pair++; |
i++; |
if (i == nPairs) |
break; |
} |
|
if (i == nPairs) |
kernval = 0; |
else |
/* We round the value (hence the +32) */ |
kernval = (pair->value + 32) & -64; |
} else |
kernval = 0; |
|
return kernval; |
} |
|
static TT_UShort |
Get_Glyph_Width(PMWFREETYPEFONT pf, TT_UShort glyph_index) |
{ |
TT_Glyph_Metrics metrics; |
|
if (TT_Load_Glyph ( pf->instance, pf->glyph, |
TT_Char_Index (pf->char_map,glyph_index), |
TTLOAD_SCALE_GLYPH|TTLOAD_HINT_GLYPH) != TT_Err_Ok) |
{ |
/* Try to load default glyph: index 0 */ |
if (TT_Load_Glyph ( pf->instance, pf->glyph, 0, |
TTLOAD_SCALE_GLYPH|TTLOAD_HINT_GLYPH) != TT_Err_Ok) |
return 0; |
} |
|
TT_Get_Glyph_Metrics (pf->glyph, &metrics); |
return((metrics.advance & 0xFFFFFFC0) >> 6); |
} |
|
static MWFREETYPEFONTCACHE * |
find_cache_glyph ( PMWFREETYPEFONT pf, unsigned curchar ) |
{ |
MWFREETYPEFONTCACHE ** inode = &pf->glyph_cache; |
|
while (1) |
{ |
if (*inode == 0) |
{ |
*inode = calloc(sizeof(MWFREETYPEFONTCACHE),1); |
(*inode)->curchar = curchar; |
} |
|
if (curchar < (*inode)->curchar) |
inode = &(*inode)->l; |
else if (curchar > (*inode)->curchar) |
inode = &(*inode)->r; |
else |
return *inode; |
} |
} |
|
static void |
free_cache_glyph ( MWFREETYPEFONTCACHE * node ) |
{ |
if (node->l != 0) |
free_cache_glyph(node->l); |
|
if (node->r != 0) |
free_cache_glyph(node->r); |
|
free(node); |
} |
|
/* Render a single glyph*/ |
static void |
drawchar(PMWFREETYPEFONT pf, PSD psd, unsigned curchar, int x_offset, |
int y_offset) |
{ |
TT_F26Dot6 xmin, ymin, xmax, ymax, x, y, z; |
unsigned char *src, *srcptr; |
MWPIXELVAL *dst, *dstptr; |
MWPIXELVAL *bitmap; |
int size, width, height; |
TT_Outline outline; |
TT_BBox bbox; |
TT_Raster_Map Raster; |
TT_Error error; |
/*MWPIXELVAL save;*/ |
MWFREETYPEFONTCACHE * cache; |
|
/* we begin by grid-fitting the bounding box */ |
TT_Get_Glyph_Outline (pf->glyph, &outline); |
TT_Get_Outline_BBox (&outline, &bbox); |
|
xmin = (bbox.xMin & -64) >> 6; |
ymin = (bbox.yMin & -64) >> 6; |
xmax = ((bbox.xMax + 63) & -64) >> 6; |
ymax = ((bbox.yMax + 63) & -64) >> 6; |
width = xmax - xmin; |
height = ymax - ymin; |
size = width * height; |
|
/* now re-allocate the raster bitmap */ |
Raster.rows = height; |
Raster.width = width; |
|
if (pf->fontattr&MWTF_ANTIALIAS) |
Raster.cols = (Raster.width + 3) & -4; /* pad to 32-bits */ |
else |
Raster.cols = (Raster.width + 7) & -8; /* pad to 64-bits ??? */ |
|
cache = find_cache_glyph(pf,curchar); |
/* SDR: if cache is 0 here, something really really bad happened */ |
|
Raster.flow = TT_Flow_Up; |
Raster.size = Raster.rows * Raster.cols; |
Raster.bitmap = cache->buffer; |
|
if (Raster.bitmap == 0) |
{ |
Raster.bitmap = malloc (Raster.size); |
|
memset (Raster.bitmap, 0, Raster.size); |
|
/* now render the glyph in the small pixmap */ |
|
/* IMPORTANT NOTE: the offset parameters passed to the function */ |
/* TT_Get_Glyph_Bitmap() must be integer pixel values, i.e., */ |
/* multiples of 64. HINTING WILL BE RUINED IF THIS ISN'T THE CASE! */ |
/* This is why we _did_ grid-fit the bounding box, especially xmin */ |
/* and ymin. */ |
|
if (!(pf->fontattr&MWTF_ANTIALIAS)) |
error = TT_Get_Glyph_Bitmap (pf->glyph, &Raster, |
-xmin * 64, -ymin * 64); |
else |
error = TT_Get_Glyph_Pixmap (pf->glyph, &Raster, |
-xmin * 64, -ymin * 64); |
|
if (error) { |
free (Raster.bitmap); |
return; |
} |
|
cache->buffer = Raster.bitmap; |
} |
|
#if MWFREETYPEFONT_CACHEBITMAP |
if ((memcmp(&gr_foreground,&cache->fg,sizeof(gr_foreground)) != 0) || |
(memcmp(&gr_background,&cache->bg,sizeof(gr_background)) != 0) || |
(gr_usebg != cache->usebg)) |
{ |
if (cache->bitmap) |
{ |
free(cache->bitmap); |
cache->bitmap = 0; |
} |
} |
|
bitmap = cache->bitmap; |
#else |
bitmap = 0; |
#endif |
|
if (bitmap == 0) |
{ |
bitmap = malloc (size * sizeof (MWPIXELVAL)); |
memset (bitmap, 0, size * sizeof (MWPIXELVAL)); |
|
src = (char *) Raster.bitmap; |
dst = bitmap + (size - width); |
|
for (y = ymin; y < ymax; y++) |
{ |
srcptr = src; |
dstptr = dst; |
|
for (x = xmin; x < xmax; x++) |
{ |
if (pf->fontattr&MWTF_ANTIALIAS) |
*dstptr++ = gray_palette[(int) *srcptr]; |
else |
{ |
for ( z=0; |
z <= ((xmax-x-1) < 7 ? (xmax-x-1) : 7); |
z++ ) |
*dstptr++ = ((*srcptr << z) & 0x80) ? gr_foreground : gr_background; |
x += 7; |
} |
|
srcptr++; |
} |
|
src += Raster.cols; |
dst -= width; |
} |
#if MWFREETYPEFONT_CACHEBITMAP |
cache->fg = gr_foreground; |
cache->bg = gr_background; |
cache->usebg = gr_usebg; |
cache->bitmap = bitmap; |
#endif |
} |
|
/* Now draw the bitmap ... */ |
GdArea(psd, x_offset + xmin, y_offset - (ymin + height), width, height, |
bitmap, MWPF_PIXELVAL); |
|
#if !MWFREETYPEFONT_CACHEBITMAP |
free(bitmap); |
#endif |
} |
|
/* |
* Draw unicode 16 text string using FREETYPE type font |
*/ |
static void |
freetype_drawtext(PMWFONT pfont, PSD psd, MWCOORD ax, MWCOORD ay, |
const void *text, int cc, int flags) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
const unsigned short * str = text; |
TT_F26Dot6 x = ax, y = ay; |
TT_Pos vec_x, vec_y; |
int i; |
TT_F26Dot6 startx, starty; |
TT_Outline outline; |
TT_UShort curchar; |
TT_Glyph_Metrics metrics; |
TT_Face_Properties properties; |
TT_Instance_Metrics imetrics; |
TT_F26Dot6 ascent, descent; |
static unsigned char blend[5] = { 0x00, 0x44, 0x88, 0xcc, 0xff }; |
static unsigned char virtual_palette[5] = { 0, 1, 2, 3, 4 }; |
|
pf->last_glyph_code = -1; /* reset kerning*/ |
pf->last_pen_pos = -32767; |
|
/* |
* Compute instance ascent & descent values |
* in fractional units (1/64th pixel) |
*/ |
TT_Get_Face_Properties (pf->face, &properties); |
TT_Get_Instance_Metrics(pf->instance, &imetrics); |
|
ascent = ((properties.horizontal->Ascender * imetrics.y_scale)/0x10000); |
descent = ((properties.horizontal->Descender*imetrics.y_scale)/0x10000); |
|
/* |
* Offset the starting point if necessary, |
* FreeType always aligns at baseline |
*/ |
if (flags&MWTF_BOTTOM) { |
vec_x = 0; |
vec_y = descent; |
TT_Transform_Vector(&vec_x, &vec_y,&pf->matrix); |
x -= vec_x / 64; |
y += vec_y / 64; |
} else if (flags&MWTF_TOP) { |
vec_x = 0; |
vec_y = ascent; |
TT_Transform_Vector(&vec_x, &vec_y,&pf->matrix); |
x -= vec_x / 64; |
y += vec_y / 64; |
} |
|
/* Set the "graylevels" */ |
if (pf->fontattr&MWTF_ANTIALIAS) { |
TT_Set_Raster_Gray_Palette (engine, virtual_palette); |
|
alphablend(psd, gray_palette, gr_foreground, gr_background, |
blend, 5); |
} |
|
startx = x; |
starty = y; |
for (i = 0; i < cc; i++) { |
curchar = TT_Char_Index (pf->char_map, str[i]); |
|
if (TT_Load_Glyph (pf->instance, pf->glyph, curchar, |
TTLOAD_DEFAULT) != TT_Err_Ok) |
continue; |
|
if (pf->fontrotation) { |
TT_Get_Glyph_Outline (pf->glyph, &outline); |
TT_Transform_Outline (&outline, &pf->matrix); |
} |
|
TT_Get_Glyph_Metrics (pf->glyph, &metrics); |
|
if ((pf->fontattr&MWTF_KERNING) && pf->can_kern) { |
if (pf->fontrotation) { |
vec_x = compute_kernval(pf, curchar); |
vec_y = 0; |
TT_Transform_Vector(&vec_x, &vec_y,&pf->matrix); |
|
x += vec_x / 64; |
y -= vec_y / 64; |
} else |
x += compute_kernval(pf, curchar) / 64; |
} |
|
drawchar(pf, psd, curchar, x, y); |
|
if (pf->fontrotation) { |
vec_x = metrics.advance; |
vec_y = 0; |
TT_Transform_Vector (&vec_x, &vec_y, &pf->matrix); |
|
x += vec_x / 64; |
y -= vec_y / 64; |
} else { |
x += metrics.advance / 64; |
|
/* Kerning point syndrome avoidance */ |
if (pf->last_pen_pos > x) |
x = pf->last_pen_pos; |
pf->last_pen_pos = x; |
} |
|
pf->last_glyph_code = curchar; |
} |
|
if (pf->fontattr & MWTF_UNDERLINE) |
GdLine(psd, startx, starty, x, y, FALSE); |
} |
|
/* |
* Return information about a specified font. |
*/ |
static MWBOOL |
freetype_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo) |
{ |
int i; |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
TT_Face_Properties properties; |
TT_Instance_Metrics imetrics; |
TT_UShort last_glyph_index; |
|
TT_Get_Face_Properties (pf->face, &properties); |
TT_Get_Instance_Metrics(pf->instance, &imetrics); |
|
/* Fill up the fields */ |
pfontinfo->height = (((properties.horizontal->Ascender * \ |
imetrics.y_scale)/ 0x10000) >> 6) - |
(((properties.horizontal->Descender * \ |
imetrics.y_scale)/ 0x10000) >> 6); |
pfontinfo->maxwidth = ((properties.horizontal->xMax_Extent * \ |
imetrics.x_scale)/ 0x10000) >> 6; |
pfontinfo->baseline = ((properties.horizontal->Ascender * \ |
imetrics.y_scale)/ 0x10000) >> 6; |
pfontinfo->firstchar = TT_CharMap_First(pf->char_map, NULL); |
pfontinfo->lastchar = TT_CharMap_Last(pf->char_map, NULL); |
pfontinfo->fixed = properties.postscript->isFixedPitch; |
|
last_glyph_index = properties.num_Glyphs > 255 ? 255: properties.num_Glyphs-1; |
|
/* Doesn't work ... don't know why ....*/ |
#if 0 |
if (TT_Get_Face_Widths( pf->face, 0, |
last_glyph_index, widths, NULL ) != TT_Err_Ok) { |
return TRUE; |
} |
|
for(i=0; i<=last_glyph_index; i++) |
DPRINTF("widths[%d]: %d\n", i, widths[i]); |
#endif |
|
/* Get glyphs widths */ |
for(i=0; i<=last_glyph_index; i++) |
pfontinfo->widths[i] = Get_Glyph_Width(pf, i); |
|
#if 0 |
DPRINTF("x_ppem: %d\ny_ppem: %d\nx_scale: %d\ny_scale: %d\n\ |
x_resolution: %d\ny_resolution: %d\n", |
imetrics.x_ppem, imetrics.y_ppem, imetrics.x_scale, \ |
imetrics.y_scale, imetrics.x_resolution, imetrics.y_resolution); |
|
DPRINTF("Ascender: %d\nDescender: %d\nxMax_Extent: %d\n\ |
Mac Style Say Italic?: %d\nMac Style Say Bold?: %d\n\ |
sTypoAscender: %d\nsTypoDescender: %d\nusWinAscent: %d\n\ |
usWinDescent: %d\nusFirstCharIndex: %d\nusLastCharIndex: %d\n\ |
OS2 Say Italic?: %d\nOS2 Say Bold?: %d\nOS2 Say monospaced?: %d\n\ |
Postscript Say monospaced?: %d\n",\ |
properties.horizontal->Ascender, |
properties.horizontal->Descender, |
properties.horizontal->xMax_Extent, |
(properties.header->Mac_Style & 0x2)?1:0, |
(properties.header->Mac_Style & 0x1)?1:0, |
properties.os2->sTypoAscender, |
properties.os2->sTypoDescender, |
properties.os2->usWinAscent, |
properties.os2->usWinDescent, |
properties.os2->usFirstCharIndex, |
properties.os2->usLastCharIndex, |
(properties.os2->fsSelection & 0x1)?1:0, |
(properties.os2->fsSelection & 0x20)?1:0, |
properties.postscript->isFixedPitch, |
(properties.os2->panose[3] == 9)?1:0); |
#endif |
|
return TRUE; |
} |
|
static void |
freetype_gettextsize(PMWFONT pfont, const void *text, int cc, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
const unsigned short * str = text; |
TT_F26Dot6 x = 0; |
int i; |
TT_UShort curchar; |
TT_Glyph_Metrics metrics; |
TT_Face_Properties properties; |
TT_Instance_Metrics imetrics; |
|
TT_Get_Face_Properties (pf->face, &properties); |
TT_Get_Instance_Metrics(pf->instance, &imetrics); |
|
pf->last_glyph_code = -1; /* reset kerning*/ |
pf->last_pen_pos = -32767; |
|
for (i = 0; i < cc; i++) { |
curchar = TT_Char_Index (pf->char_map, str[i]); |
|
if (TT_Load_Glyph (pf->instance, pf->glyph, curchar, |
TTLOAD_SCALE_GLYPH|TTLOAD_HINT_GLYPH) != TT_Err_Ok) |
continue; |
|
TT_Get_Glyph_Metrics (pf->glyph, &metrics); |
|
if ((pf->fontattr&MWTF_KERNING) && pf->can_kern) { |
x += compute_kernval(pf, curchar) / 64; |
} |
|
x += metrics.advance / 64; |
|
/* Kerning point syndrome avoidance */ |
if (pf->last_pen_pos > x) |
x = pf->last_pen_pos; |
pf->last_pen_pos = x; |
|
pf->last_glyph_code = curchar; |
} |
|
*pwidth = x; |
*pheight = (((properties.horizontal->Ascender * |
imetrics.y_scale)/ 0x10000) >> 6) - |
(((properties.horizontal->Descender * |
imetrics.y_scale)/ 0x10000) >> 6); |
/* FIXME: is it what's required ?? */ |
*pbase = (((-properties.horizontal->Descender) * |
imetrics.y_scale)/ 0x10000) >> 6; |
} |
|
static void |
freetype_destroyfont(PMWFONT pfont) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
|
TT_Close_Face(pf->face); |
|
/*----------*/ |
|
if (pf->glyph_cache != 0) |
free_cache_glyph(pf->glyph_cache); |
|
/*----------*/ |
|
free(pf); |
} |
|
static void |
freetype_setfontsize(PMWFONT pfont, MWCOORD fontsize) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
|
pf->fontsize = fontsize; |
|
/* We want real pixel sizes ... not points ...*/ |
TT_Set_Instance_PixelSizes( pf->instance, pf->fontsize, |
pf->fontsize, pf->fontsize * 64 ); |
#if 0 |
/* set charsize (convert to points for freetype)*/ |
TT_Set_Instance_CharSize (pf->instance, |
((pf->fontsize * 72 + 96/2) / 96) * 64); |
#endif |
} |
|
static void |
freetype_setfontrotation(PMWFONT pfont, int tenthdegrees) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
float angle; |
|
pf->fontrotation = tenthdegrees; |
|
/* Build the rotation matrix with the given angle */ |
TT_Set_Instance_Transform_Flags (pf->instance, TRUE, FALSE); |
|
angle = pf->fontrotation * M_PI / 1800; |
pf->matrix.yy = (TT_Fixed) (cos (angle) * (1 << 16)); |
pf->matrix.yx = (TT_Fixed) (sin (angle) * (1 << 16)); |
pf->matrix.xx = pf->matrix.yy; |
pf->matrix.xy = -pf->matrix.yx; |
} |
|
#endif /* HAVE_FREETYPE_SUPPORT*/ |
|
/* UTF-8 to UTF-16 conversion. Surrogates are handeled properly, e.g. |
* a single 4-byte UTF-8 character is encoded into a surrogate pair. |
* On the other hand, if the UTF-8 string contains surrogate values, this |
* is considered an error and returned as such. |
* |
* The destination array must be able to hold as many Unicode-16 characters |
* as there are ASCII characters in the UTF-8 string. This in case all UTF-8 |
* characters are ASCII characters. No more will be needed. |
* |
* Copyright (c) 2000 Morten Rolland, Screen Media |
*/ |
static int |
utf8_to_utf16(const unsigned char *utf8, int cc, unsigned short *unicode16) |
{ |
int count = 0; |
unsigned char c0, c1; |
unsigned long scalar; |
|
while(--cc >= 0) { |
c0 = *utf8++; |
/*DPRINTF("Trying: %02x\n",c0);*/ |
|
if ( c0 < 0x80 ) { |
/* Plain ASCII character, simple translation :-) */ |
*unicode16++ = c0; |
count++; |
continue; |
} |
|
if ( (c0 & 0xc0) == 0x80 ) |
/* Illegal; starts with 10xxxxxx */ |
return -1; |
|
/* c0 must be 11xxxxxx if we get here => at least 2 bytes */ |
scalar = c0; |
if(--cc < 0) |
return -1; |
c1 = *utf8++; |
/*DPRINTF("c1=%02x\n",c1);*/ |
if ( (c1 & 0xc0) != 0x80 ) |
/* Bad byte */ |
return -1; |
scalar <<= 6; |
scalar |= (c1 & 0x3f); |
|
if ( !(c0 & 0x20) ) { |
/* Two bytes UTF-8 */ |
if ( scalar < 0x80 ) |
return -1; /* Overlong encoding */ |
*unicode16++ = scalar & 0x7ff; |
count++; |
continue; |
} |
|
/* c0 must be 111xxxxx if we get here => at least 3 bytes */ |
if(--cc < 0) |
return -1; |
c1 = *utf8++; |
/*DPRINTF("c1=%02x\n",c1);*/ |
if ( (c1 & 0xc0) != 0x80 ) |
/* Bad byte */ |
return -1; |
scalar <<= 6; |
scalar |= (c1 & 0x3f); |
|
if ( !(c0 & 0x10) ) { |
/*DPRINTF("####\n");*/ |
/* Three bytes UTF-8 */ |
if ( scalar < 0x800 ) |
return -1; /* Overlong encoding */ |
if ( scalar >= 0xd800 && scalar < 0xe000 ) |
return -1; /* UTF-16 high/low halfs */ |
*unicode16++ = scalar & 0xffff; |
count++; |
continue; |
} |
|
/* c0 must be 1111xxxx if we get here => at least 4 bytes */ |
c1 = *utf8++; |
if(--cc < 0) |
return -1; |
/*DPRINTF("c1=%02x\n",c1);*/ |
if ( (c1 & 0xc0) != 0x80 ) |
/* Bad byte */ |
return -1; |
scalar <<= 6; |
scalar |= (c1 & 0x3f); |
|
if ( !(c0 & 0x08) ) { |
/* Four bytes UTF-8, needs encoding as surrogates */ |
if ( scalar < 0x10000 ) |
return -1; /* Overlong encoding */ |
scalar -= 0x10000; |
*unicode16++ = ((scalar >> 10) & 0x3ff) + 0xd800; |
*unicode16++ = (scalar & 0x3ff) + 0xdc00; |
count += 2; |
continue; |
} |
|
return -1; /* No support for more than four byte UTF-8 */ |
} |
return count; |
} |
|
#if HAVE_HZK_SUPPORT |
|
/* UniCode-16 (MWTF_UC16) to GB(MWTF_ASCII) Chinese Characters conversion. |
* a single 2-byte UC16 character is encoded into a surrogate pair. |
* return -1 ,if error; |
* The destination array must be able to hold as many |
* as there are Unicode-16 characters. |
* |
* Copyright (c) 2000 Tang Hao (TownHall)(tang_hao@263.net). |
*/ |
static int |
UC16_to_GB(const unsigned char *uc16, int cc, unsigned char *ascii) |
{ |
FILE* fp; |
char buffer[256]; |
unsigned char *uc16p; |
int i=0,j=0, k; |
unsigned char *filebuffer; |
unsigned short *uc16pp,*table; |
unsigned short uc16px; |
int length=31504; |
|
if (use_big5) |
length=54840; |
|
uc16p=(unsigned char *) uc16; |
uc16pp=(unsigned short *) uc16; |
|
strcpy(buffer,HZK_FONT_DIR); |
if (use_big5) |
strcat(buffer,"/BG2UBG.KU"); |
else |
strcat(buffer,"/UGB2GB.KU"); |
if(!(fp = fopen(buffer, "rb"))) |
{ |
fprintf (stderr, "Error.\nThe %s file can not be found!\n",buffer); |
return -1; |
} |
|
filebuffer= (unsigned char *)malloc ( length); |
|
if(fread(filebuffer, sizeof(char),length, fp) < length) { |
fprintf (stderr, "Error in reading ugb2gb.ku file!\n"); |
fclose(fp); |
return -1; |
} |
fclose(fp); |
|
if (use_big5) |
{ |
table=(unsigned short *)filebuffer; |
while(1) |
{ |
if(j>=cc) |
{ |
ascii[i]=0; |
break; |
} |
uc16px=*uc16pp; |
if((uc16px)<=0x00ff) |
{ |
ascii[i]=(char)(*uc16pp); |
i++; |
} |
else |
{ |
ascii[i]=0xa1; ascii[i+1]=0x40; |
for (k=0; k<13710; k++) |
{ |
if (*(table+(k*2+1))==(uc16px)) |
{ |
ascii[i]=(char)((*(table+(k*2)) & 0xff00) >> 8); |
ascii[i+1]=(char)(*(table+(k*2)) & 0x00ff); |
break; |
} |
} |
i+=2; |
} |
uc16pp++; j++; |
} |
} |
else |
{ |
while(1) |
{ |
if(j>=cc) |
{ |
ascii[i]=0; |
break; |
} |
if((*((uc16p)+j)==0)&&(*((uc16p)+j+1)==0)) |
{ |
ascii[i]=0; |
break; |
} |
else |
{ |
if(*((uc16p)+j+1)==0) |
{ |
ascii[i]=*((uc16p)+j); |
i++; |
j+=2; |
} |
else |
{ |
|
{ |
int p1=0,p2=length-4,p; |
unsigned int c1,c2,c,d; |
c1=((unsigned int )filebuffer[p1])*0x100+(filebuffer[p1+1]); |
c2=((unsigned int )filebuffer[p2])*0x100+(filebuffer[p2+1]); |
d=((unsigned int )*((uc16p)+j))*0x100+*((uc16p)+j+1); |
if(c1==d) |
{ |
ascii[i]=filebuffer[p1+2]; |
ascii[i+1]=filebuffer[p1+3]; |
goto findit; |
} |
if(c2==d) |
{ |
ascii[i]=filebuffer[p2+2]; |
ascii[i+1]=filebuffer[p2+3]; |
goto findit; |
} |
while(1) |
{ |
p=(((p2-p1)/2+p1)>>2)<<2; |
c=((unsigned int )filebuffer[p])*0x100+(filebuffer[p+1]); |
if(d==c) /*find it*/ |
{ |
ascii[i]=filebuffer[p+2]; |
ascii[i+1]=filebuffer[p+3]; |
break; |
} |
else if(p2<=p1+4) /*can't find.*/ |
{ |
ascii[i]='.';/*((uc16p)+j);*/ |
ascii[i+1]='.';/*((uc16p)+j+1);*/ |
break; |
} |
else if(d<c) |
{ |
p2=p; |
c2=c; |
} |
else |
{ |
p1=p; |
c1=c; |
} |
} |
} |
findit: |
i+=2; |
j+=2; |
} |
} |
} |
} |
free(filebuffer); |
|
return i; |
} |
|
/************************** functions definition ******************************/ |
|
static int hzk_id( PMWHZKFONT pf ) |
{ |
switch(pf->font_height) |
{ |
case 12: |
return 0; |
case 16: default: |
return 1; |
} |
} |
|
/* This function get Chinese font info from etc file.*/ |
static MWBOOL GetCFontInfo( PMWHZKFONT pf ) |
{ |
int charset; |
|
if (use_big5) |
charset=(13094+408); |
else |
charset=8178; |
|
CFont[hzk_id(pf)].width = pf->cfont_width; |
pf->CFont.width = pf->cfont_width; |
|
CFont[hzk_id(pf)].height = pf->font_height; |
pf->CFont.height = pf->font_height; |
|
CFont[hzk_id(pf)].size = ((pf->CFont.width + 7) / 8) * |
pf->CFont.height * charset; |
pf->CFont.size = ((pf->CFont.width + 7) / 8) * pf->CFont.height * charset; |
|
if(pf->CFont.size < charset * 8) |
return FALSE; |
|
strcpy(CFont[hzk_id(pf)].file,HZK_FONT_DIR); |
strcpy(pf->CFont.file,HZK_FONT_DIR); |
|
if(pf->font_height==16) |
{ |
strcat(CFont[hzk_id(pf)].file,"/hzk16"); |
strcat(pf->CFont.file,"/hzk16"); |
} |
else |
{ |
strcat(CFont[hzk_id(pf)].file,"/hzk12"); |
strcat(pf->CFont.file,"/hzk12"); |
} |
|
if (use_big5) |
{ |
CFont[hzk_id(pf)].file[strlen(pf->CFont.file)-3]+=use_big5; |
pf->CFont.file[strlen(pf->CFont.file)-3]+=use_big5; |
} |
|
return TRUE; |
} |
|
/* This function get ASCII font info from etc file.*/ |
static MWBOOL GetAFontInfo( PMWHZKFONT pf ) |
{ |
AFont[hzk_id(pf)].width = pf->afont_width; |
pf->AFont.width = pf->afont_width; |
|
AFont[hzk_id(pf)].height = pf->font_height; |
pf->AFont.height = pf->font_height; |
|
AFont[hzk_id(pf)].size = ((pf->AFont.width + 7) / 8) * |
pf->AFont.height * 255; |
pf->AFont.size = ((pf->AFont.width + 7) / 8) * pf->AFont.height * 255; |
|
if(pf->AFont.size < 255 * 8) |
return FALSE; |
|
strcpy(AFont[hzk_id(pf)].file,HZK_FONT_DIR); |
strcpy(pf->AFont.file,HZK_FONT_DIR); |
|
if(pf->font_height==16) |
{ |
strcat(AFont[hzk_id(pf)].file,"/asc16"); |
strcat(pf->AFont.file,"/asc16"); |
} |
else |
{ |
strcat(AFont[hzk_id(pf)].file,"/asc12"); |
strcat(pf->AFont.file,"/asc12"); |
} |
return TRUE; |
} |
|
/* This function load system font into memory.*/ |
static MWBOOL LoadFont( PMWHZKFONT pf ) |
{ |
FILE* fp; |
|
if(!GetCFontInfo(pf)) |
{ |
fprintf (stderr, "Get Chinese HZK font info failure!\n"); |
return FALSE; |
} |
if(CFont[hzk_id(pf)].pFont == NULL)/*check font cache*/ |
{ |
|
|
/* Allocate system memory for Chinese font.*/ |
if( !(CFont[hzk_id(pf)].pFont = (char *)malloc(pf->CFont.size)) ) |
{ |
fprintf (stderr, "Allocate memory for Chinese HZK font failure.\n"); |
return FALSE; |
} |
|
/* Open font file and read information to the system memory.*/ |
fprintf (stderr, "Loading Chinese HZK font from file(%s)..." ,pf->CFont.file); |
if(!(fp = fopen(CFont[hzk_id(pf)].file, "rb"))) |
{ |
fprintf (stderr, "Error.\nThe Chinese HZK font file can not be found!\n"); |
return FALSE; |
} |
if(fread(CFont[hzk_id(pf)].pFont, sizeof(char), pf->CFont.size, fp) < pf->CFont.size) |
{ |
fprintf (stderr, "Error in reading Chinese HZK font file!\n"); |
fclose(fp); |
return FALSE; |
} |
|
fclose(fp); |
|
CFont[hzk_id(pf)].use_count=0; |
|
fprintf (stderr, "done.\n" ); |
|
} |
cfont_address = CFont[hzk_id(pf)].pFont; |
pf->cfont_address = CFont[hzk_id(pf)].pFont; |
pf->CFont.pFont = CFont[hzk_id(pf)].pFont; |
|
CFont[hzk_id(pf)].use_count++; |
|
if(!GetAFontInfo(pf)) |
{ |
fprintf (stderr, "Get ASCII HZK font info failure!\n"); |
return FALSE; |
} |
if(AFont[hzk_id(pf)].pFont == NULL)/*check font cache*/ |
{ |
|
|
/* Allocate system memory for ASCII font.*/ |
if( !(AFont[hzk_id(pf)].pFont = (char *)malloc(pf->AFont.size)) ) |
{ |
fprintf (stderr, "Allocate memory for ASCII HZK font failure.\n"); |
free(CFont[hzk_id(pf)].pFont); |
CFont[hzk_id(pf)].pFont = NULL; |
return FALSE; |
} |
|
/* Load ASCII font information to the near memory.*/ |
fprintf (stderr, "Loading ASCII HZK font..." ); |
if(!(fp = fopen(AFont[hzk_id(pf)].file, "rb"))) |
{ |
fprintf (stderr, "Error.\nThe ASCII HZK font file can not be found!\n"); |
return FALSE; |
} |
if(fread(AFont[hzk_id(pf)].pFont, sizeof(char), pf->AFont.size, fp) < pf->AFont.size) |
{ |
fprintf (stderr, "Error in reading ASCII HZK font file!\n"); |
fclose(fp); |
return FALSE; |
} |
|
fclose(fp); |
|
AFont[hzk_id(pf)].use_count=0; |
|
fprintf (stderr, "done.\n" ); |
|
} |
afont_address = AFont[hzk_id(pf)].pFont; |
pf->afont_address = AFont[hzk_id(pf)].pFont; |
pf->AFont.pFont = AFont[hzk_id(pf)].pFont; |
|
AFont[hzk_id(pf)].use_count++; |
|
return TRUE; |
} |
|
/* This function unload system font from memory.*/ |
static void UnloadFont( PMWHZKFONT pf ) |
{ |
CFont[hzk_id(pf)].use_count--; |
AFont[hzk_id(pf)].use_count--; |
|
if (!CFont[hzk_id(pf)].use_count) |
{ |
free(pf->CFont.pFont); |
free(pf->AFont.pFont); |
|
CFont[hzk_id(pf)].pFont = NULL; |
AFont[hzk_id(pf)].pFont = NULL; |
} |
} |
|
static int |
hzk_init(PSD psd) |
{ |
/* FIXME: *.KU file should be opened and |
* read in here...*/ |
return 1; |
} |
|
static PMWHZKFONT |
hzk_createfont(const char *name, MWCOORD height, int attr) |
{ |
PMWHZKFONT pf; |
|
if(strcmp(name,"HZKFONT")!=0 && strcmp(name,"HZXFONT")!=0) |
return FALSE; |
|
printf("hzk_createfont(%s,%d)\n",name,height); |
|
use_big5=name[2]-'K'; |
|
/* allocate font structure*/ |
pf = (PMWHZKFONT)calloc(sizeof(MWHZKFONT), 1); |
if (!pf) |
return NULL; |
pf->fontprocs = &hzk_procs; |
|
pf->fontsize=height; |
/*GdSetFontSize((PMWFONT)pf, height);*/ |
GdSetFontRotation((PMWFONT)pf, 0); |
GdSetFontAttr((PMWFONT)pf, attr, 0); |
|
if(height==12) |
{ |
afont_width = 6; |
cfont_width = 12; |
font_height = 12; |
|
pf->afont_width = 6; |
pf->cfont_width = 12; |
pf->font_height = 12; |
} |
else |
{ |
afont_width = 8; |
cfont_width = 16; |
font_height = 16; |
|
pf->afont_width = 8; |
pf->cfont_width = 16; |
pf->font_height = 16; |
} |
|
/* Load the font library to the system memory.*/ |
if(!LoadFont(pf)) |
return FALSE; |
|
return pf; |
} |
|
int IsBig5(int i) |
{ |
if ((i>=0xa140 && i<=0xa3bf) || /* a140-a3bf(!a3e0) */ |
(i>=0xa440 && i<=0xc67e) || /* a440-c67e */ |
(i>=0xc6a1 && i<=0xc8d3) || /* c6a1-c8d3(!c8fe) */ |
(i>=0xc940 && i<=0xf9fe)) /* c940-f9fe */ |
return 1; |
else |
return 0; |
} |
|
/* |
* following several function is used in hzk_drawtext |
*/ |
|
static int getnextchar(char* s, unsigned char* cc) |
{ |
if( s[0] == '\0') return 0; |
|
cc[0] = (unsigned char)(*s); |
cc[1] = (unsigned char)(*(s + 1)); |
|
if (use_big5) |
{ |
if( IsBig5( (int) ( (cc[0] << 8) + cc[1]) ) ) |
return 1; |
} |
else |
{ |
if( ((unsigned char)cc[0] > 0xa0) && |
((unsigned char)cc[1] > 0xa0) ) |
return 1; |
} |
|
cc[1] = '\0'; |
|
return 1; |
} |
|
static void |
expandcchar(PMWHZKFONT pf, int bg, int fg, unsigned char* c, MWPIXELVAL* bitmap) |
{ |
int i=0; |
int c1, c2, seq; |
int x,y; |
unsigned char *font; |
int b = 0; /* keep gcc happy with b = 0 - MW */ |
|
int pixelsize; |
pixelsize=sizeof(MWPIXELVAL); |
|
c1 = c[0]; |
c2 = c[1]; |
if (use_big5) |
{ |
seq=0; |
/*ladd=loby-(if(loby<127)?64:98)*/ |
c2-=(c2<127?64:98); |
|
/*hadd=(hiby-164)*157*/ |
if (c1>=0xa4)/*standard font*/ |
{ |
seq=(((c1-164)*157)+c2); |
if (seq>=5809) seq-=408; |
} |
|
/*hadd=(hiby-161)*157*/ |
if (c1<=0xa3)/*special font*/ |
seq=(((c1-161)*157)+c2)+13094; |
} |
else |
seq=((c1 - 161)*94 + c2 - 161); |
|
font = pf->cfont_address + ((seq) * |
(pf->font_height * ((pf->cfont_width + 7) / 8))); |
|
for (y = 0; y < pf->font_height; y++) |
for (x = 0; x < pf->cfont_width; x++) |
{ |
if (x % 8 == 0) |
b = *font++; |
|
if (b & (128 >> (x % 8))) /* pixel */ |
bitmap[i++]=fg; |
else |
bitmap[i++]=bg; |
} |
} |
|
static void expandchar(PMWHZKFONT pf, int bg, int fg, int c, MWPIXELVAL* bitmap) |
{ |
int i=0; |
int x,y; |
unsigned char *font; |
int pixelsize; |
int b = 0; /* keep gcc happy with b = 0 - MW */ |
|
pixelsize=sizeof(MWPIXELVAL); |
|
font = pf->afont_address + c * (pf->font_height * |
((pf->afont_width + 7) / 8)); |
|
for (y = 0; y < pf->font_height; y++) |
for (x = 0; x < pf->afont_width; x++) |
{ |
if (x % 8 == 0) |
b = *font++; |
if (b & (128 >> (x % 8))) /* pixel */ |
bitmap[i++]=fg; |
else |
bitmap[i++]=bg; |
} |
} |
|
/* |
* Draw ASCII text string using HZK type font |
*/ |
static void |
hzk_drawtext(PMWFONT pfont, PSD psd, MWCOORD ax, MWCOORD ay, |
const void *text, int cc, int flags) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
|
unsigned char c[2]; |
MWPIXELVAL *bitmap; |
unsigned char s1[3]; |
char *s,*sbegin; |
|
s=(char *)text; |
|
if(cc==1) |
{ |
s1[0]=*((unsigned char*)text); |
s1[1]=0x0; |
s1[2]=0x0; |
s=s1; |
} |
|
sbegin=s; |
bitmap = (MWPIXELVAL *)ALLOCA(pf->cfont_width * pf->font_height * |
sizeof(MWPIXELVAL)); |
|
while( getnextchar(s, c) ) |
{ |
if( c[1] != '\0') |
{ |
expandcchar(pf, gr_background,gr_foreground, |
c, bitmap); |
/* Now draw the bitmap ... */ |
|
if (flags&MWTF_TOP) |
GdArea(psd,ax, ay, pf->cfont_width, |
pf->font_height, bitmap, MWPF_PIXELVAL); |
else |
GdArea(psd,ax, ay-pf->font_height+2, |
pf->cfont_width, pf->font_height, |
bitmap, MWPF_PIXELVAL); |
|
s += 2; |
ax += pf->cfont_width; |
} |
else |
{ |
expandchar(pf, gr_background,gr_foreground, |
c[0], bitmap); |
/* Now draw the bitmap ... */ |
|
if (flags&MWTF_TOP) |
GdArea(psd,ax, ay, pf->afont_width, |
pf->font_height, bitmap, MWPF_PIXELVAL); |
else |
GdArea(psd,ax, ay-pf->font_height+2, |
pf->afont_width, pf->font_height, |
bitmap, MWPF_PIXELVAL); |
|
s += 1; |
ax += pf->afont_width; |
} |
|
if(s>=sbegin+cc)break; |
} |
|
FREEA(bitmap); |
} |
|
/* |
* Return information about a specified font. |
*/ |
static MWBOOL |
hzk_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
|
int i; |
|
pfontinfo->height = pf->font_height; |
pfontinfo->maxwidth = pf->cfont_width; |
pfontinfo->baseline = pf->font_height-2; |
pfontinfo->firstchar = 0; |
pfontinfo->lastchar = 0; |
pfontinfo->fixed = TRUE; |
|
for(i=0; i<=256; i++) |
pfontinfo->widths[i] = pf->afont_width; |
|
return TRUE; |
} |
|
static void |
hzk_gettextsize(PMWFONT pfont, const void *text, int cc, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
|
unsigned char c[2]; |
char *s,*sbegin; |
unsigned char s1[3]; |
|
int ax=0; |
|
|
|
s=(char *)text; |
if(cc==0) |
{ |
*pwidth = 0; |
*pheight = pf->font_height; |
*pbase = pf->font_height-2; |
|
} |
if(cc==1) |
{ |
s1[0]=*((unsigned char*)text); |
s1[1]=0x0; |
s1[2]=0x0; |
s=s1; |
} |
sbegin=s; |
while( getnextchar(s, c) ) |
{ |
if( c[1] != '\0') |
{ |
s += 2; |
ax += pf->cfont_width; |
} |
else |
{ |
s += 1; |
ax += pf->afont_width; |
} |
if(s>=sbegin+cc)break; |
/* fprintf(stderr,"s=%x,sbegin=%x,cc=%x\n",s,sbegin,cc);*/ |
|
} |
/* fprintf(stderr,"ax=%d,\n",ax);*/ |
|
*pwidth = ax; |
*pheight = pf->font_height; |
*pbase = pf->font_height-2; |
} |
|
static void |
hzk_destroyfont(PMWFONT pfont) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
|
UnloadFont(pf); |
free(pf); |
} |
|
static void |
hzk_setfontsize(PMWFONT pfont, MWCOORD fontsize) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
|
/*jmt: hzk_setfontsize not supported*/ |
/*jmt: & pf->fontsize can't be changed*/ |
/*jmt: because of hzk_id() :p*/ |
pf->fontsize=pf->font_height; |
} |
#endif /* HAVE_HZK_SUPPORT*/ |
|
/* FIXME: this routine should work for all font renderers...*/ |
int |
GdGetTextSizeEx(PMWFONT pfont, const void *str, int cc,int nMaxExtent, |
int* lpnFit, int* alpDx,MWCOORD *pwidth,MWCOORD *pheight, |
MWCOORD *pbase, int flags) |
{ |
#ifdef HAVE_FREETYPE_SUPPORT |
unsigned short buf[256]; |
unsigned short* text; |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
int defencoding = pf->fontprocs->encoding; |
int x = 0; |
int i; |
TT_UShort curchar; |
TT_Glyph_Metrics metrics; |
TT_Face_Properties properties; |
TT_Instance_Metrics imetrics; |
|
if ((cc<0)||(!str)) |
{ |
*pwidth = *pheight = *pbase = 0; |
return 0; |
} |
/* convert encoding if required*/ |
if((flags & MWTF_PACKMASK) != defencoding) |
{ |
cc = GdConvertEncoding(str, flags, cc, buf, defencoding); |
flags &= ~MWTF_PACKMASK; |
flags |= defencoding; |
text=buf; |
} else text =(unsigned short*)str; |
if(cc <= 0) |
{ |
*pwidth = *pheight = *pbase = 0; |
return 0; |
} |
|
TT_Get_Face_Properties (pf->face, &properties); |
TT_Get_Instance_Metrics(pf->instance, &imetrics); |
|
pf->last_glyph_code = -1; /* reset kerning*/ |
pf->last_pen_pos = -32767; |
if (lpnFit) |
*lpnFit=-1; |
for (i = 0; i < cc; i++) |
{ |
curchar = TT_Char_Index (pf->char_map,text[i]); |
|
if (TT_Load_Glyph (pf->instance, pf->glyph, curchar, |
TTLOAD_SCALE_GLYPH|TTLOAD_HINT_GLYPH) != TT_Err_Ok) |
{ |
printf("Unable to load glyph with index=%d\n",curchar); |
return 0; |
} |
TT_Get_Glyph_Metrics (pf->glyph, &metrics); |
if ((pf->fontattr&MWTF_KERNING) && pf->can_kern) |
{ |
x += compute_kernval(pf, curchar) / 64; |
} |
x += metrics.advance / 64; |
if((lpnFit)&&(alpDx)) |
{ |
if (x<=nMaxExtent) |
alpDx[i]=x; |
else |
if (*lpnFit==-1) |
(*lpnFit)=i; |
} |
/* Kerning point syndrome avoidance */ |
if (pf->last_pen_pos > x) |
x = pf->last_pen_pos; |
pf->last_pen_pos = x; |
pf->last_glyph_code = curchar; |
} |
if ((lpnFit)&&(*lpnFit==-1)) |
*lpnFit=cc; |
*pwidth = x; |
*pheight = (((properties.horizontal->Ascender * |
imetrics.y_scale)/ 0x10000) >> 6) - |
(((properties.horizontal->Descender * |
imetrics.y_scale)/ 0x10000) >> 6); |
/*FIXME: is it what's required ??*/ |
if (pbase) |
*pbase = (((-properties.horizontal->Descender) * |
imetrics.y_scale)/ 0x10000) >> 6; |
return 1; |
#else /* HAVE_FREETYPE_SUPPORT*/ |
*pwidth = *pheight = *pbase = 0; |
return 0; |
#endif |
} |
/devopen.c
0,0 → 1,529
/* |
* Copyright (c) 1999, 2000, 2001 Greg Haerr <greg@censoft.com> |
* Portions Copyright (c) 1991 David I. Bell |
* Permission is granted to use, distribute, or modify this source, |
* provided that this copyright notice remains intact. |
* |
* Device-independent mid level screen device init routines |
* |
* These routines implement the smallest Microwindows engine level |
* interface to the screen driver. By setting the NOFONTSORCLIPPING |
* config option, only these routines will be included, which can |
* be used to generate a low-level interface to the screen drivers |
* without dragging in any other GdXXX routines. |
*/ |
#include <stdio.h> |
#include <stdlib.h> |
#include "device.h" |
#include "swap.h" |
|
#if MSDOS | ELKS |
#define NOSTDPAL8 |
#endif |
|
/* |
* The following define can change depending on the window manager |
* usage of colors and layout of the 8bpp palette devpal8.c. |
* Color entries below this value won't be overwritten by user |
* programs or bitmap display conversion tables. |
*/ |
#define FIRSTUSERPALENTRY 24 /* first writable pal entry over 16 color*/ |
|
MWPIXELVAL gr_foreground; /* current foreground color */ |
MWPIXELVAL gr_background; /* current background color */ |
MWBOOL gr_usebg; /* TRUE if background drawn in pixmaps */ |
int gr_mode = MWMODE_COPY; /* drawing mode */ |
/*static*/ MWPALENTRY gr_palette[256]; /* current palette*/ |
/*static*/ int gr_firstuserpalentry;/* first user-changable palette entry*/ |
/*static*/ int gr_nextpalentry; /* next available palette entry*/ |
static int gr_pixtype; /* screen pixel format*/ |
static long gr_ncolors; /* screen # colors*/ |
|
/* |
* Open low level graphics driver |
*/ |
PSD |
GdOpenScreen(void) |
{ |
PSD psd; |
MWPALENTRY * stdpal; |
MWSCREENINFO sinfo; |
|
psd = scrdev.Open(&scrdev); |
if (!psd) |
return NULL; |
GdGetScreenInfo(psd, &sinfo); |
gr_pixtype = sinfo.pixtype; |
gr_ncolors = sinfo.ncolors; |
|
/* assume no user changable palette entries*/ |
gr_firstuserpalentry = (int)psd->ncolors; |
|
/* set palette according to system colors and devpalX.c*/ |
switch((int)psd->ncolors) { |
|
#if !defined(NOSTDPAL1) /* don't require stdpal1 if not needed */ |
case 2: /* 1bpp*/ |
{ |
extern MWPALENTRY mwstdpal1[2]; |
stdpal = mwstdpal1; |
} |
break; |
#endif |
|
#if !defined(NOSTDPAL2) /* don't require stdpal2 if not needed */ |
case 4: /* 2bpp*/ |
{ |
extern MWPALENTRY mwstdpal2[4]; |
stdpal = mwstdpal2; |
} |
break; |
#endif |
|
#if !defined(NOSTDPAL4) |
/* don't require stdpal4 if not needed */ |
case 8: /* 3bpp - not fully supported*/ |
case 16: /* 4bpp*/ |
{ |
extern MWPALENTRY mwstdpal4[16]; |
stdpal = mwstdpal4; |
} |
break; |
#endif |
|
#if !defined(NOSTDPAL8) /* don't require large stdpal8 if not needed */ |
case 256: /* 8bpp*/ |
{ |
extern MWPALENTRY mwstdpal8[256]; |
#if xxxALPHABLEND |
/* don't change uniform palette if alpha blending*/ |
gr_firstuserpalentry = 256; |
#else |
/* start after last system-reserved color*/ |
gr_firstuserpalentry = FIRSTUSERPALENTRY; |
#endif |
stdpal = mwstdpal8; |
} |
break; |
#endif /* !defined(NOSTDPAL8)*/ |
|
default: /* truecolor*/ |
/* no palette*/ |
gr_firstuserpalentry = 0; |
stdpal = NULL; |
} |
|
/* reset next user palette entry, write hardware palette*/ |
GdResetPalette(); |
GdSetPalette(psd, 0, (int)psd->ncolors, stdpal); |
#if xxxALPHABLEND |
/* one-time create alpha lookup table for 8bpp systems (takes ~1 sec)*/ |
if(psd->ncolors == 256) |
init_alpha_lookup(); |
#endif |
|
#if !NOFONTSORCLIPPING |
/* init local vars*/ |
GdSetMode(MWMODE_COPY); |
GdSetForeground(GdFindColor(MWRGB(255, 255, 255))); /* WHITE*/ |
GdSetBackground(GdFindColor(MWRGB(0, 0, 0))); /* BLACK*/ |
GdSetUseBackground(TRUE); |
GdSetFont(GdCreateFont(psd, MWFONT_SYSTEM_VAR, 0, NULL)); |
#if DYNAMICREGIONS |
GdSetClipRegion(psd, |
GdAllocRectRegion(0, 0, psd->xvirtres, psd->yvirtres)); |
#else |
GdSetClipRects(psd, 0, NULL); |
#endif /* DYNAMICREGIONS*/ |
#endif /* NOFONTSORCLIPPING*/ |
|
/* fill black (actually fill to first palette entry or truecolor 0*/ |
psd->FillRect(psd, 0, 0, psd->xvirtres-1, psd->yvirtres-1, 0); |
return psd; |
} |
|
/* |
* Close low level graphics driver |
*/ |
void |
GdCloseScreen(PSD psd) |
{ |
psd->Close(psd); |
} |
|
/* Set dynamic screen portrait mode, return new mode*/ |
int |
GdSetPortraitMode(PSD psd, int portraitmode) |
{ |
/* set portrait mode if supported*/ |
if (psd->SetPortrait) |
psd->SetPortrait(psd, portraitmode); |
return psd->portrait; |
} |
|
/* |
* Return about the screen. |
*/ |
void |
GdGetScreenInfo(PSD psd, PMWSCREENINFO psi) |
{ |
psd->GetScreenInfo(psd, psi); |
GdGetButtonInfo(&psi->buttons); |
GdGetModifierInfo(&psi->modifiers, NULL); |
GdGetCursorPos(&psi->xpos, &psi->ypos); |
} |
|
/* reset palette to empty except for system colors*/ |
void |
GdResetPalette(void) |
{ |
/* note: when palette entries are changed, all |
* windows may need to be redrawn |
*/ |
gr_nextpalentry = gr_firstuserpalentry; |
} |
|
/* set the system palette section to the passed palette entries*/ |
void |
GdSetPalette(PSD psd, int first, int count, MWPALENTRY *palette) |
{ |
int i; |
|
/* no palette management needed if running truecolor*/ |
if(psd->pixtype != MWPF_PALETTE) |
return; |
|
/* bounds check against # of device color entries*/ |
if(first + count > (int)psd->ncolors) |
count = (int)psd->ncolors - first; |
if(count >= 0 && first < (int)psd->ncolors) { |
psd->SetPalette(psd, first, count, palette); |
|
/* copy palette for GdFind*Color*/ |
for(i=0; i<count; ++i) |
gr_palette[i+first] = palette[i]; |
} |
} |
|
/* get system palette entries, return count*/ |
int |
GdGetPalette(PSD psd, int first, int count, MWPALENTRY *palette) |
{ |
int i; |
|
/* no palette if running truecolor*/ |
if(psd->pixtype != MWPF_PALETTE) |
return 0; |
|
/* bounds check against # of device color entries*/ |
if(first + count > (int)psd->ncolors) |
if( (count = (int)psd->ncolors - first) <= 0) |
return 0; |
|
for(i=0; i<count; ++i) |
*palette++ = gr_palette[i+first]; |
|
return count; |
} |
|
/* |
* Convert a palette-independent value to a hardware color |
*/ |
MWPIXELVAL |
GdFindColor(MWCOLORVAL c) |
{ |
/* |
* Handle truecolor displays. Note that the MWF_PALINDEX |
* bit is ignored when running truecolor drivers. |
*/ |
switch(gr_pixtype) { |
case MWPF_TRUECOLOR0888: |
case MWPF_TRUECOLOR888: |
/* create 24 bit 8/8/8 pixel (0x00RRGGBB) from RGB colorval*/ |
/*RGB2PIXEL888(REDVALUE(c), GREENVALUE(c), BLUEVALUE(c))*/ |
return COLOR2PIXEL888(c); |
|
case MWPF_TRUECOLOR565: |
/* create 16 bit 5/6/5 format pixel from RGB colorval*/ |
/*RGB2PIXEL565(REDVALUE(c), GREENVALUE(c), BLUEVALUE(c))*/ |
return COLOR2PIXEL565(c); |
|
case MWPF_TRUECOLOR555: |
/* create 16 bit 5/5/5 format pixel from RGB colorval*/ |
/*RGB2PIXEL555(REDVALUE(c), GREENVALUE(c), BLUEVALUE(c))*/ |
return COLOR2PIXEL555(c); |
|
case MWPF_TRUECOLOR332: |
/* create 8 bit 3/3/2 format pixel from RGB colorval*/ |
/*RGB2PIXEL332(REDVALUE(c), GREENVALUE(c), BLUEVALUE(c))*/ |
return COLOR2PIXEL332(c); |
} |
|
/* case MWPF_PALETTE: must be running 1, 2, 4 or 8 bit palette*/ |
|
/* |
* Check if color is a palette index. Note that the index |
* isn't error checked against the system palette, for speed. |
*/ |
if(c & MWF_PALINDEX) |
return (c & 0xff); |
|
/* search palette for closest match*/ |
return GdFindNearestColor(gr_palette, (int)gr_ncolors, c); |
} |
|
/* |
* Search a palette to find the nearest color requested. |
* Uses a weighted squares comparison. |
*/ |
MWPIXELVAL |
GdFindNearestColor(MWPALENTRY *pal, int size, MWCOLORVAL cr) |
{ |
MWPALENTRY * rgb; |
int r, g, b; |
int R, G, B; |
long diff = 0x7fffffffL; |
long sq; |
int best = 0; |
|
r = REDVALUE(cr); |
g = GREENVALUE(cr); |
b = BLUEVALUE(cr); |
for(rgb=pal; diff && rgb < &pal[size]; ++rgb) { |
R = rgb->r - r; |
G = rgb->g - g; |
B = rgb->b - b; |
#if 1 |
/* speedy linear distance method*/ |
sq = abs(R) + abs(G) + abs(B); |
#else |
/* slower distance-cubed with luminance adjustment*/ |
/* gray is .30R + .59G + .11B*/ |
/* = (R*77 + G*151 + B*28)/256*/ |
sq = (long)R*R*30*30 + (long)G*G*59*59 + (long)B*B*11*11; |
#endif |
|
if(sq < diff) { |
best = rgb - pal; |
if((diff = sq) == 0) |
return best; |
} |
} |
return best; |
} |
|
#if !VXWORKS |
#include <unistd.h> |
#include <fcntl.h> |
/* |
* Create .bmp file from framebuffer data |
* |
* 1, 4, 8, 16, 24 and 32 bpp supported |
*/ |
#define BI_RGB 0L |
#define BI_RLE8 1L |
#define BI_RLE4 2L |
#define BI_BITFIELDS 3L |
|
typedef unsigned char BYTE; |
typedef unsigned short WORD; |
typedef unsigned long DWORD; |
typedef long LONG; |
|
#pragma pack(1) |
/* windows style bmp*/ |
typedef struct { |
/* BITMAPFILEHEADER*/ |
BYTE bfType[2]; |
DWORD bfSize; |
WORD bfReserved1; |
WORD bfReserved2; |
DWORD bfOffBits; |
/* BITMAPINFOHEADER*/ |
DWORD BiSize; |
LONG BiWidth; |
LONG BiHeight; |
WORD BiPlanes; |
WORD BiBitCount; |
DWORD BiCompression; |
DWORD BiSizeImage; |
LONG BiXpelsPerMeter; |
LONG BiYpelsPerMeter; |
DWORD BiClrUsed; |
DWORD BiClrImportant; |
} BMPHEAD; |
#pragma pack() |
|
/* r/g/b masks for non-palette bitmaps*/ |
#define RMASK332 0xe0 |
#define GMASK332 0x1c |
#define BMASK332 0x03 |
#define RMASK555 0x7c00 |
#define GMASK555 0x03e0 |
#define BMASK555 0x001f |
#define RMASK565 0xf800 |
#define GMASK565 0x07e0 |
#define BMASK565 0x001f |
#define RMASK888 0xff0000 |
#define GMASK888 0x00ff00 |
#define BMASK888 0x0000ff |
|
#if defined(HAVE_FILEIO) |
static void |
putsw(unsigned long dw, FILE *ofp) |
{ |
/* little-endian storage of shortword*/ |
putc((unsigned char)dw, ofp); |
dw >>= 8; |
putc((unsigned char)dw, ofp); |
} |
|
static void |
putdw(unsigned long dw, FILE *ofp) |
{ |
/* little-endian storage of longword*/ |
putc((unsigned char)dw, ofp); |
dw >>= 8; |
putc((unsigned char)dw, ofp); |
dw >>= 8; |
putc((unsigned char)dw, ofp); |
dw >>= 8; |
putc((unsigned char)dw, ofp); |
} |
#endif /* HAVE_FILEIO*/ |
|
/* create .bmp file from framebuffer data*/ |
int |
GdCaptureScreen(char *path) |
{ |
#if defined(HAVE_FILEIO) |
int ifd, i, j; |
FILE * ofp; |
int cx, cy, extra, bpp, bytespp, ncolors, sizecolortable; |
unsigned long rmask, gmask, bmask; |
unsigned char *cptr; |
unsigned short *sptr; |
unsigned long *lptr; |
BMPHEAD bmp; |
unsigned char buf[2048*4]; |
|
ofp = fopen(path, "wb"); |
if (!ofp) |
return 1; |
ifd = open("/dev/fb0", 0); |
|
cx = scrdev.xvirtres; |
cy = scrdev.yvirtres; |
bpp = scrdev.bpp; |
bytespp = (bpp+7)/8; |
|
/* dword right padded*/ |
extra = (cx*bytespp) & 3; |
if (extra) |
extra = 4 - extra; |
ncolors = (bpp <= 8)? (1<<bpp): 0; |
/* color table is either palette or 3 longword r/g/b masks*/ |
sizecolortable = ncolors? ncolors*4: 3*4; |
if (bpp == 24) |
sizecolortable = 0; /* special case 24bpp has no table*/ |
|
/* fill out bmp header*/ |
memset(&bmp, 0, sizeof(bmp)); |
bmp.bfType[0] = 'B'; |
bmp.bfType[1] = 'M'; |
bmp.bfSize = dwswap(sizeof(bmp) + sizecolortable + (long)(cx+extra)*cy*bytespp); |
bmp.bfOffBits = dwswap(sizeof(bmp) + sizecolortable); |
bmp.BiSize = dwswap(40); |
bmp.BiWidth = dwswap(cx); |
bmp.BiHeight = dwswap(cy); |
bmp.BiPlanes = wswap(1); |
bmp.BiBitCount = wswap(bpp); |
bmp.BiCompression = dwswap((bpp==16 || bpp==32)? BI_BITFIELDS: BI_RGB); |
bmp.BiSizeImage = dwswap((long)(cx+extra)*cy*bytespp); |
bmp.BiClrUsed = dwswap((bpp <= 8)? ncolors: 0); |
/*bmp.BiClrImportant = 0;*/ |
|
/* write header*/ |
fwrite(&bmp, sizeof(bmp), 1, ofp); |
|
/* write colortable*/ |
if (sizecolortable) { |
if(bpp <= 8) { |
/* write palette*/ |
for(i=0; i<ncolors; i++) { |
putc(gr_palette[i].b, ofp); |
putc(gr_palette[i].g, ofp); |
putc(gr_palette[i].r, ofp); |
putc(0, ofp); |
} |
} else { |
/* write 3 r/g/b masks*/ |
switch (gr_pixtype) { |
case MWPF_TRUECOLOR0888: |
default: |
rmask = RMASK888; |
gmask = GMASK888; |
bmask = BMASK888; |
break; |
case MWPF_TRUECOLOR565: |
rmask = RMASK565; |
gmask = GMASK565; |
bmask = BMASK565; |
break; |
case MWPF_TRUECOLOR555: |
rmask = RMASK555; |
gmask = GMASK555; |
bmask = BMASK555; |
break; |
case MWPF_TRUECOLOR332: |
rmask = RMASK332; |
gmask = GMASK332; |
bmask = BMASK332; |
break; |
} |
putdw(rmask, ofp); |
putdw(gmask, ofp); |
putdw(bmask, ofp); |
} |
} |
|
/* write image data, upside down ;)*/ |
for(i=cy-1; i>=0; --i) { |
long base = sizeof(bmp) + sizecolortable + (long)i*cx*bytespp; |
fseek(ofp, base, SEEK_SET); |
read(ifd, buf, cx*bytespp); |
switch (bpp) { |
case 32: |
lptr = (unsigned long *)buf; |
for(j=0; j<cx; ++j) |
putdw(*lptr++, ofp); |
break; |
case 24: |
cptr = (unsigned char *)buf; |
for(j=0; j<cx; ++j) { |
putc(*cptr++, ofp); |
putc(*cptr++, ofp); |
putc(*cptr++, ofp); |
} |
break; |
case 16: |
sptr = (unsigned short *)buf; |
for(j=0; j<cx; ++j) |
putsw(*sptr++, ofp); |
break; |
default: |
cptr = (unsigned char *)buf; |
for(j=0; j<cx; ++j) |
putc(*cptr++, ofp); |
break; |
} |
for(j=0; j<extra; ++j) |
putc(0, ofp); /* DWORD pad each line*/ |
} |
|
fclose(ofp); |
close(ifd); |
#endif /* HAVE_FILEIO*/ |
return 0; |
} |
#endif /* !VXWORKS*/ |
/devpal8.c
0,0 → 1,302
/* |
* Copyright (c) 1999 Greg Haerr <greg@censoft.com> |
* |
* 8bpp (256 color) standard palette definition |
*/ |
#include "device.h" |
|
/* |
* Special palette for supporting 48 Windows colors and a 216 color |
* uniform color distribution. |
* Note: the first 20 colors are used internally as system colors. |
*/ |
MWPALENTRY mwstdpal8[256] = { |
/* 16 EGA colors, arranged for direct predefined palette indexing*/ |
RGBDEF( 0 , 0 , 0 ), /* black*/ |
RGBDEF( 0 , 0 , 128 ), /* blue*/ |
RGBDEF( 0 , 128, 0 ), /* green*/ |
RGBDEF( 0 , 128, 128 ), /* cyan*/ /* COLOR_BACKGROUND*/ |
RGBDEF( 128, 0 , 0 ), /* red*/ /* COLOR_ACTIVECAPTION A*/ |
RGBDEF( 128, 0 , 128 ), /* magenta*/ /* COLOR_ACTIVECAPTION B*/ |
RGBDEF( 128, 64 , 0 ), /* adjusted brown*/ |
RGBDEF( 192, 192, 192 ), /* ltgray*/ |
RGBDEF( 128, 128, 128 ), /* gray*/ |
RGBDEF( 0 , 0 , 255 ), /* ltblue*/ |
RGBDEF( 0 , 255, 0 ), /* ltgreen*/ |
RGBDEF( 0 , 255, 255 ), /* ltcyan*/ |
RGBDEF( 255, 0 , 0 ), /* ltred*/ |
RGBDEF( 255, 0 , 255 ), /* ltmagenta*/ |
RGBDEF( 255, 255, 0 ), /* yellow*/ |
RGBDEF( 255, 255, 255 ), /* white*/ |
|
/* 32 basic windows colors (first 8 are most important)*/ |
RGBDEF( 32 , 32 , 32 ), /* DKGRAY_BRUSH*/ |
RGBDEF( 128, 128, 0 ), /* non-adjusted brown*/ |
RGBDEF( 223, 223, 223 ), /* COLOR_3DLIGHT B*/ |
RGBDEF( 160, 160, 160 ), /* COLOR_3DLIGHT C*/ |
|
RGBDEF( 234, 230, 221 ), /* COLOR_BTNHIGHLIGHT A*/ |
RGBDEF( 213, 204, 187 ), /* COLOR_BTNFACE A*/ |
RGBDEF( 162, 141, 104 ), /* COLOR_BTNSHADOW A*/ |
RGBDEF( 0 , 64 , 128 ), /* COLOR_INACTIVECAPTION C*/ |
/*RGBDEF( 0 , 0 , 64 ),*/ |
/*RGBDEF( 0 , 64 , 0 ),*/ |
/*RGBDEF( 0 , 64 , 64 ),*/ |
/*RGBDEF( 0 , 128, 64 ),*/ |
RGBDEF( 0 , 128, 255 ), |
RGBDEF( 0 , 255, 128 ), |
RGBDEF( 64 , 0 , 0 ), |
RGBDEF( 64 , 0 , 64 ), |
RGBDEF( 64 , 0 , 128 ), |
RGBDEF( 64 , 128, 128 ), |
RGBDEF( 128, 0 , 64 ), |
RGBDEF( 128, 0 , 255 ), |
RGBDEF( 128, 64 , 64 ), |
RGBDEF( 128, 128, 64 ), |
RGBDEF( 128, 128, 192 ), |
RGBDEF( 128, 128, 255 ), |
RGBDEF( 128, 255, 0 ), |
RGBDEF( 128, 255, 255 ), |
RGBDEF( 164, 200, 240 ), |
RGBDEF( 192, 220, 192 ), |
RGBDEF( 255, 0 , 128 ), |
RGBDEF( 255, 128, 0 ), |
RGBDEF( 255, 128, 192 ), |
RGBDEF( 255, 128, 255 ), |
RGBDEF( 255, 128, 128 ), |
RGBDEF( 255, 255, 128 ), |
RGBDEF( 255, 251, 240 ), |
RGBDEF( 255, 255, 232 ), |
|
/* 216 colors spread uniformly across rgb spectrum*/ |
/* 8 colors removed that are duplicated above*/ |
/*RGBDEF( 0x00, 0x00, 0x00 ),*/ |
RGBDEF( 0x00, 0x00, 0x33 ), |
RGBDEF( 0x00, 0x00, 0x66 ), |
RGBDEF( 0x00, 0x00, 0x99 ), |
RGBDEF( 0x00, 0x00, 0xcc ), |
/*RGBDEF( 0x00, 0x00, 0xff ),*/ |
RGBDEF( 0x33, 0x00, 0x00 ), |
RGBDEF( 0x33, 0x00, 0x33 ), |
RGBDEF( 0x33, 0x00, 0x66 ), |
RGBDEF( 0x33, 0x00, 0x99 ), |
RGBDEF( 0x33, 0x00, 0xcc ), |
RGBDEF( 0x33, 0x00, 0xff ), |
RGBDEF( 0x66, 0x00, 0x00 ), |
RGBDEF( 0x66, 0x00, 0x33 ), |
RGBDEF( 0x66, 0x00, 0x66 ), |
RGBDEF( 0x66, 0x00, 0x99 ), |
RGBDEF( 0x66, 0x00, 0xcc ), |
RGBDEF( 0x66, 0x00, 0xff ), |
RGBDEF( 0x99, 0x00, 0x00 ), |
RGBDEF( 0x99, 0x00, 0x33 ), |
RGBDEF( 0x99, 0x00, 0x66 ), |
RGBDEF( 0x99, 0x00, 0x99 ), |
RGBDEF( 0x99, 0x00, 0xcc ), |
RGBDEF( 0x99, 0x00, 0xff ), |
RGBDEF( 0xcc, 0x00, 0x00 ), |
RGBDEF( 0xcc, 0x00, 0x33 ), |
RGBDEF( 0xcc, 0x00, 0x66 ), |
RGBDEF( 0xcc, 0x00, 0x99 ), |
RGBDEF( 0xcc, 0x00, 0xcc ), |
RGBDEF( 0xcc, 0x00, 0xff ), |
/*RGBDEF( 0xff, 0x00, 0x00 ),*/ |
RGBDEF( 0xff, 0x00, 0x33 ), |
RGBDEF( 0xff, 0x00, 0x66 ), |
RGBDEF( 0xff, 0x00, 0x99 ), |
RGBDEF( 0xff, 0x00, 0xcc ), |
/*RGBDEF( 0xff, 0x00, 0xff ),*/ |
RGBDEF( 0x00, 0x33, 0x00 ), |
RGBDEF( 0x00, 0x33, 0x33 ), |
RGBDEF( 0x00, 0x33, 0x66 ), |
RGBDEF( 0x00, 0x33, 0x99 ), |
RGBDEF( 0x00, 0x33, 0xcc ), |
RGBDEF( 0x00, 0x33, 0xff ), |
RGBDEF( 0x33, 0x33, 0x00 ), |
RGBDEF( 0x33, 0x33, 0x33 ), |
RGBDEF( 0x33, 0x33, 0x66 ), |
RGBDEF( 0x33, 0x33, 0x99 ), |
RGBDEF( 0x33, 0x33, 0xcc ), |
RGBDEF( 0x33, 0x33, 0xff ), |
RGBDEF( 0x66, 0x33, 0x00 ), |
RGBDEF( 0x66, 0x33, 0x33 ), |
RGBDEF( 0x66, 0x33, 0x66 ), |
RGBDEF( 0x66, 0x33, 0x99 ), |
RGBDEF( 0x66, 0x33, 0xcc ), |
RGBDEF( 0x66, 0x33, 0xff ), |
RGBDEF( 0x99, 0x33, 0x00 ), |
RGBDEF( 0x99, 0x33, 0x33 ), |
RGBDEF( 0x99, 0x33, 0x66 ), |
RGBDEF( 0x99, 0x33, 0x99 ), |
RGBDEF( 0x99, 0x33, 0xcc ), |
RGBDEF( 0x99, 0x33, 0xff ), |
RGBDEF( 0xcc, 0x33, 0x00 ), |
RGBDEF( 0xcc, 0x33, 0x33 ), |
RGBDEF( 0xcc, 0x33, 0x66 ), |
RGBDEF( 0xcc, 0x33, 0x99 ), |
RGBDEF( 0xcc, 0x33, 0xcc ), |
RGBDEF( 0xcc, 0x33, 0xff ), |
RGBDEF( 0xff, 0x33, 0x00 ), |
RGBDEF( 0xff, 0x33, 0x33 ), |
RGBDEF( 0xff, 0x33, 0x66 ), |
RGBDEF( 0xff, 0x33, 0x99 ), |
RGBDEF( 0xff, 0x33, 0xcc ), |
RGBDEF( 0xff, 0x33, 0xff ), |
RGBDEF( 0x00, 0x66, 0x00 ), |
RGBDEF( 0x00, 0x66, 0x33 ), |
RGBDEF( 0x00, 0x66, 0x66 ), |
RGBDEF( 0x00, 0x66, 0x99 ), |
RGBDEF( 0x00, 0x66, 0xcc ), |
RGBDEF( 0x00, 0x66, 0xff ), |
RGBDEF( 0x33, 0x66, 0x00 ), |
RGBDEF( 0x33, 0x66, 0x33 ), |
RGBDEF( 0x33, 0x66, 0x66 ), |
RGBDEF( 0x33, 0x66, 0x99 ), |
RGBDEF( 0x33, 0x66, 0xcc ), |
RGBDEF( 0x33, 0x66, 0xff ), |
RGBDEF( 0x66, 0x66, 0x00 ), |
RGBDEF( 0x66, 0x66, 0x33 ), |
RGBDEF( 0x66, 0x66, 0x66 ), |
RGBDEF( 0x66, 0x66, 0x99 ), |
RGBDEF( 0x66, 0x66, 0xcc ), |
RGBDEF( 0x66, 0x66, 0xff ), |
RGBDEF( 0x99, 0x66, 0x00 ), |
RGBDEF( 0x99, 0x66, 0x33 ), |
RGBDEF( 0x99, 0x66, 0x66 ), |
RGBDEF( 0x99, 0x66, 0x99 ), |
RGBDEF( 0x99, 0x66, 0xcc ), |
RGBDEF( 0x99, 0x66, 0xff ), |
RGBDEF( 0xcc, 0x66, 0x00 ), |
RGBDEF( 0xcc, 0x66, 0x33 ), |
RGBDEF( 0xcc, 0x66, 0x66 ), |
RGBDEF( 0xcc, 0x66, 0x99 ), |
RGBDEF( 0xcc, 0x66, 0xcc ), |
RGBDEF( 0xcc, 0x66, 0xff ), |
RGBDEF( 0xff, 0x66, 0x00 ), |
RGBDEF( 0xff, 0x66, 0x33 ), |
RGBDEF( 0xff, 0x66, 0x66 ), |
RGBDEF( 0xff, 0x66, 0x99 ), |
RGBDEF( 0xff, 0x66, 0xcc ), |
RGBDEF( 0xff, 0x66, 0xff ), |
RGBDEF( 0x00, 0x99, 0x00 ), |
RGBDEF( 0x00, 0x99, 0x33 ), |
RGBDEF( 0x00, 0x99, 0x66 ), |
RGBDEF( 0x00, 0x99, 0x99 ), |
RGBDEF( 0x00, 0x99, 0xcc ), |
RGBDEF( 0x00, 0x99, 0xff ), |
RGBDEF( 0x33, 0x99, 0x00 ), |
RGBDEF( 0x33, 0x99, 0x33 ), |
RGBDEF( 0x33, 0x99, 0x66 ), |
RGBDEF( 0x33, 0x99, 0x99 ), |
RGBDEF( 0x33, 0x99, 0xcc ), |
RGBDEF( 0x33, 0x99, 0xff ), |
RGBDEF( 0x66, 0x99, 0x00 ), |
RGBDEF( 0x66, 0x99, 0x33 ), |
RGBDEF( 0x66, 0x99, 0x66 ), |
RGBDEF( 0x66, 0x99, 0x99 ), |
RGBDEF( 0x66, 0x99, 0xcc ), |
RGBDEF( 0x66, 0x99, 0xff ), |
RGBDEF( 0x99, 0x99, 0x00 ), |
RGBDEF( 0x99, 0x99, 0x33 ), |
RGBDEF( 0x99, 0x99, 0x66 ), |
RGBDEF( 0x99, 0x99, 0x99 ), |
RGBDEF( 0x99, 0x99, 0xcc ), |
RGBDEF( 0x99, 0x99, 0xff ), |
RGBDEF( 0xcc, 0x99, 0x00 ), |
RGBDEF( 0xcc, 0x99, 0x33 ), |
RGBDEF( 0xcc, 0x99, 0x66 ), |
RGBDEF( 0xcc, 0x99, 0x99 ), |
RGBDEF( 0xcc, 0x99, 0xcc ), |
RGBDEF( 0xcc, 0x99, 0xff ), |
RGBDEF( 0xff, 0x99, 0x00 ), |
RGBDEF( 0xff, 0x99, 0x33 ), |
RGBDEF( 0xff, 0x99, 0x66 ), |
RGBDEF( 0xff, 0x99, 0x99 ), |
RGBDEF( 0xff, 0x99, 0xcc ), |
RGBDEF( 0xff, 0x99, 0xff ), |
RGBDEF( 0x00, 0xcc, 0x00 ), |
RGBDEF( 0x00, 0xcc, 0x33 ), |
RGBDEF( 0x00, 0xcc, 0x66 ), |
RGBDEF( 0x00, 0xcc, 0x99 ), |
RGBDEF( 0x00, 0xcc, 0xcc ), |
RGBDEF( 0x00, 0xcc, 0xff ), |
RGBDEF( 0x33, 0xcc, 0x00 ), |
RGBDEF( 0x33, 0xcc, 0x33 ), |
RGBDEF( 0x33, 0xcc, 0x66 ), |
RGBDEF( 0x33, 0xcc, 0x99 ), |
RGBDEF( 0x33, 0xcc, 0xcc ), |
RGBDEF( 0x33, 0xcc, 0xff ), |
RGBDEF( 0x66, 0xcc, 0x00 ), |
RGBDEF( 0x66, 0xcc, 0x33 ), |
RGBDEF( 0x66, 0xcc, 0x66 ), |
RGBDEF( 0x66, 0xcc, 0x99 ), |
RGBDEF( 0x66, 0xcc, 0xcc ), |
RGBDEF( 0x66, 0xcc, 0xff ), |
RGBDEF( 0x99, 0xcc, 0x00 ), |
RGBDEF( 0x99, 0xcc, 0x33 ), |
RGBDEF( 0x99, 0xcc, 0x66 ), |
RGBDEF( 0x99, 0xcc, 0x99 ), |
RGBDEF( 0x99, 0xcc, 0xcc ), |
RGBDEF( 0x99, 0xcc, 0xff ), |
RGBDEF( 0xcc, 0xcc, 0x00 ), |
RGBDEF( 0xcc, 0xcc, 0x33 ), |
RGBDEF( 0xcc, 0xcc, 0x66 ), |
RGBDEF( 0xcc, 0xcc, 0x99 ), |
RGBDEF( 0xcc, 0xcc, 0xcc ), |
RGBDEF( 0xcc, 0xcc, 0xff ), |
RGBDEF( 0xff, 0xcc, 0x00 ), |
RGBDEF( 0xff, 0xcc, 0x33 ), |
RGBDEF( 0xff, 0xcc, 0x66 ), |
RGBDEF( 0xff, 0xcc, 0x99 ), |
RGBDEF( 0xff, 0xcc, 0xcc ), |
RGBDEF( 0xff, 0xcc, 0xff ), |
/*RGBDEF( 0x00, 0xff, 0x00 ),*/ |
RGBDEF( 0x00, 0xff, 0x33 ), |
RGBDEF( 0x00, 0xff, 0x66 ), |
RGBDEF( 0x00, 0xff, 0x99 ), |
RGBDEF( 0x00, 0xff, 0xcc ), |
/*RGBDEF( 0x00, 0xff, 0xff ),*/ |
RGBDEF( 0x33, 0xff, 0x00 ), |
RGBDEF( 0x33, 0xff, 0x33 ), |
RGBDEF( 0x33, 0xff, 0x66 ), |
RGBDEF( 0x33, 0xff, 0x99 ), |
RGBDEF( 0x33, 0xff, 0xcc ), |
RGBDEF( 0x33, 0xff, 0xff ), |
RGBDEF( 0x66, 0xff, 0x00 ), |
RGBDEF( 0x66, 0xff, 0x33 ), |
RGBDEF( 0x66, 0xff, 0x66 ), |
RGBDEF( 0x66, 0xff, 0x99 ), |
RGBDEF( 0x66, 0xff, 0xcc ), |
RGBDEF( 0x66, 0xff, 0xff ), |
RGBDEF( 0x99, 0xff, 0x00 ), |
RGBDEF( 0x99, 0xff, 0x33 ), |
RGBDEF( 0x99, 0xff, 0x66 ), |
RGBDEF( 0x99, 0xff, 0x99 ), |
RGBDEF( 0x99, 0xff, 0xcc ), |
RGBDEF( 0x99, 0xff, 0xff ), |
RGBDEF( 0xcc, 0xff, 0x00 ), |
RGBDEF( 0xcc, 0xff, 0x33 ), |
RGBDEF( 0xcc, 0xff, 0x66 ), |
RGBDEF( 0xcc, 0xff, 0x99 ), |
RGBDEF( 0xcc, 0xff, 0xcc ), |
RGBDEF( 0xcc, 0xff, 0xff ), |
/*RGBDEF( 0xff, 0xff, 0x00 ),*/ |
RGBDEF( 0xff, 0xff, 0x33 ), |
RGBDEF( 0xff, 0xff, 0x66 ), |
RGBDEF( 0xff, 0xff, 0x99 ), |
RGBDEF( 0xff, 0xff, 0xcc ), |
/*RGBDEF( 0xff, 0xff, 0xff )*/ |
}; |
|
#if TEST |
main() |
{ |
int c; |
|
DPRINTF("%d\n", ((int)&stdpalette[1]) - (int)&stdpalette[0]); |
|
c = FindNearestColor(stdpalette, 224, 224, 224); |
DPRINTF("%d = %02x %02x %02x\n", c, stdpalette[c].r, stdpalette[c].g, |
stdpalette[c].b); |
} |
#endif |
/devarc.c
0,0 → 1,589
/* |
* Copyright (c) 2000-2001 Greg Haerr <greg@censoft.com> |
* |
* Device-independent arc, pie and ellipse routines. |
* GdArc is integer only and requires start/end points. |
* GdArcAngle requires floating point and uses angles. |
* GdArcAngle uses qsin() and qcos() instead of sin() / cos() |
* so no math lib needed. |
* |
* Portions Copyright (c) 1991 David I. Bell |
* Permission is granted to use, distribute, or modify this source, |
* provided that this copyright notice remains intact. |
* |
* Arc line clipping and integer qsin/qcos routines used by permission: |
* Copyright (C) 1997-1998 by Eero Tamminen |
* Bugfixed by Greg Haerr |
*/ |
|
#include <stdio.h> |
#include "device.h" |
|
#if HAVEFLOAT /* =1 compiles in GdArcAngle*/ |
#define HIGHPRECISION 0 /* =1 for high precision angles, uses mathlib*/ |
|
#if !HIGHPRECISION |
typedef float FLOAT; |
/* |
* qsin/qcos - calculate sin() and cos() approximations from a lookup table |
* |
* This uses a cosine lookup table of 0-90 degrees at one degree steps |
* with the difference between successive values used for interpolation. |
* The achieved accuracy should be about +/-0.0001. If you want more |
* accuracy, use doubles and smaller steps. If you want more speed, use |
* fixed point arithmetics. |
*/ |
static float cosine[91][2] = { |
{ 1.000000, -1.523048e-04 }, |
{ 0.999848, -4.568681e-04 }, |
{ 0.999391, -7.612923e-04 }, |
{ 0.998630, -1.065484e-03 }, |
{ 0.997564, -1.369352e-03 }, |
{ 0.996195, -1.672803e-03 }, |
{ 0.994522, -1.975744e-03 }, |
{ 0.992546, -2.278083e-03 }, |
{ 0.990268, -2.579728e-03 }, |
{ 0.987688, -2.880588e-03 }, |
{ 0.984808, -3.180570e-03 }, |
{ 0.981627, -3.479583e-03 }, |
{ 0.978148, -3.777536e-03 }, |
{ 0.974370, -4.074339e-03 }, |
{ 0.970296, -4.369900e-03 }, |
{ 0.965926, -4.664130e-03 }, |
{ 0.961262, -4.956940e-03 }, |
{ 0.956305, -5.248240e-03 }, |
{ 0.951057, -5.537941e-03 }, |
{ 0.945519, -5.825955e-03 }, |
{ 0.939693, -6.112194e-03 }, |
{ 0.933580, -6.396572e-03 }, |
{ 0.927184, -6.679001e-03 }, |
{ 0.920505, -6.959396e-03 }, |
{ 0.913545, -7.237671e-03 }, |
{ 0.906308, -7.513741e-03 }, |
{ 0.898794, -7.787522e-03 }, |
{ 0.891007, -8.058931e-03 }, |
{ 0.882948, -8.327886e-03 }, |
{ 0.874620, -8.594303e-03 }, |
{ 0.866025, -8.858103e-03 }, |
{ 0.857167, -9.119205e-03 }, |
{ 0.848048, -9.377528e-03 }, |
{ 0.838671, -9.632995e-03 }, |
{ 0.829038, -9.885528e-03 }, |
{ 0.819152, -1.013505e-02 }, |
{ 0.809017, -1.038148e-02 }, |
{ 0.798636, -1.062476e-02 }, |
{ 0.788011, -1.086479e-02 }, |
{ 0.777146, -1.110152e-02 }, |
{ 0.766044, -1.133486e-02 }, |
{ 0.754710, -1.156475e-02 }, |
{ 0.743145, -1.179112e-02 }, |
{ 0.731354, -1.201390e-02 }, |
{ 0.719340, -1.223302e-02 }, |
{ 0.707107, -1.244841e-02 }, |
{ 0.694658, -1.266001e-02 }, |
{ 0.681998, -1.286775e-02 }, |
{ 0.669131, -1.307158e-02 }, |
{ 0.656059, -1.327142e-02 }, |
{ 0.642788, -1.346722e-02 }, |
{ 0.629320, -1.365892e-02 }, |
{ 0.615661, -1.384645e-02 }, |
{ 0.601815, -1.402977e-02 }, |
{ 0.587785, -1.420882e-02 }, |
{ 0.573576, -1.438353e-02 }, |
{ 0.559193, -1.455387e-02 }, |
{ 0.544639, -1.471977e-02 }, |
{ 0.529919, -1.488119e-02 }, |
{ 0.515038, -1.503807e-02 }, |
{ 0.500000, -1.519038e-02 }, |
{ 0.484810, -1.533806e-02 }, |
{ 0.469472, -1.548106e-02 }, |
{ 0.453990, -1.561935e-02 }, |
{ 0.438371, -1.575289e-02 }, |
{ 0.422618, -1.588162e-02 }, |
{ 0.406737, -1.600551e-02 }, |
{ 0.390731, -1.612454e-02 }, |
{ 0.374607, -1.623864e-02 }, |
{ 0.358368, -1.634781e-02 }, |
{ 0.342020, -1.645199e-02 }, |
{ 0.325568, -1.655116e-02 }, |
{ 0.309017, -1.664529e-02 }, |
{ 0.292372, -1.673435e-02 }, |
{ 0.275637, -1.681831e-02 }, |
{ 0.258819, -1.689715e-02 }, |
{ 0.241922, -1.697084e-02 }, |
{ 0.224951, -1.703936e-02 }, |
{ 0.207912, -1.710270e-02 }, |
{ 0.190809, -1.716082e-02 }, |
{ 0.173648, -1.721371e-02 }, |
{ 0.156434, -1.726136e-02 }, |
{ 0.139173, -1.730376e-02 }, |
{ 0.121869, -1.734088e-02 }, |
{ 0.104528, -1.737272e-02 }, |
{ 0.087156, -1.739927e-02 }, |
{ 0.069756, -1.742052e-02 }, |
{ 0.052336, -1.743646e-02 }, |
{ 0.034899, -1.744709e-02 }, |
{ 0.017452, -1.745241e-02 }, |
{ 0.000000, -1.745241e-02 } |
}; |
|
static float |
qcos(FLOAT angle) |
{ |
short a, b, c; |
|
a = angle; |
if (a < 0) { |
angle = a - angle; |
a = -a; |
} else { |
angle = angle - a; |
} |
b = a / 90; |
c = a - b * 90; |
|
/* interpolate according to angle */ |
switch(b&3) { |
case 3: |
c = 90 - c; |
return cosine[c][0] - cosine[c-1][1] * angle; |
case 2: |
return -(cosine[c][0] + cosine[c][1] * angle); |
case 1: |
c = 90 - c; |
return cosine[c-1][1] * angle - cosine[c][0]; |
default: |
return cosine[c][0] + cosine[c][1] * angle; |
} |
} |
|
static float |
qsin(FLOAT angle) |
{ |
short a, b, c; |
|
/* change to cosine by subtracting 90 */ |
a = (int)angle - 90; |
if (a < 0) { |
angle = (a + 90) - angle; |
a = -a; |
} else { |
angle = angle - (a + 90); |
} |
b = a / 90; |
c = a - b * 90; |
|
/* interpolate according to angle */ |
switch(b&3) { |
case 3: |
c = 90 - c; |
return cosine[c][0] - cosine[c-1][1] * angle; |
case 2: |
return -(cosine[c][0] + cosine[c][1] * angle); |
case 1: |
c = 90 - c; |
return cosine[c-1][1] * angle - cosine[c][0]; |
default: |
return cosine[c][0] + cosine[c][1] * angle; |
} |
} |
#else /* HIGHPRECISION*/ |
|
#include <math.h> |
#define qcos QCOS |
#define qsin QSIN |
typedef double FLOAT; |
|
FLOAT QCOS(FLOAT a) |
{ |
return cos(a * M_PI / 180.); |
} |
|
FLOAT QSIN(FLOAT a) |
{ |
return sin(a * M_PI / 180.); |
} |
#endif /* HIGHPRECISION*/ |
#endif /* HAVEFLOAT*/ |
|
/* |
* Draw an arc or pie, angles are specified in 64th's of a degree. |
* This function requires floating point, use GdArc for integer only. |
*/ |
void |
GdArcAngle(PSD psd, MWCOORD x0, MWCOORD y0, MWCOORD rx, MWCOORD ry, |
MWCOORD angle1, MWCOORD angle2, int type) |
{ |
#if HAVEFLOAT |
MWCOORD ax, ay, bx, by; |
FLOAT a, b; |
|
/* calculate pie edge offsets from center to the ellipse rim */ |
ax = qcos(angle1/64.) * rx; |
bx = qcos(angle2/64.) * rx; |
|
a = -qsin(angle1/64.); |
b = -qsin(angle2/64.); |
ay = a * ry; |
by = b * ry; |
|
/* call integer routine*/ |
GdArc(psd, x0, y0, rx, ry, ax, ay, bx, by, type); |
#endif /* HAVEFLOAT*/ |
} |
|
/* argument holder for pie, arc and ellipse functions*/ |
typedef struct { |
PSD psd; |
MWCOORD x0, y0; |
MWCOORD rx, ry; |
MWCOORD ax, ay; |
MWCOORD bx, by; |
int adir, bdir; |
int type; |
} SLICE; |
|
extern void drawpoint(PSD psd, MWCOORD x, MWCOORD y); |
extern void drawrow(PSD psd, MWCOORD x1, MWCOORD x2, MWCOORD y); |
|
/* |
* Clip a line segment for arc or pie drawing. |
* Returns 0 if line is clipped or on acceptable side, 1 if it's vertically |
* on other side, otherwise 3. |
*/ |
static int |
clip_line(SLICE *slice, MWCOORD xe, MWCOORD ye, int dir, MWCOORD y, MWCOORD *x0, |
MWCOORD *x1) |
{ |
#if 0 |
/* |
* kluge: handle 180 degree case |
*/ |
if (y >= 0 && ye == 0) { |
/*printf("cl %d,%d %d,%d %d,%d %d,%d %d,%d\n", xe, ye, y, dir, |
slice->ax, slice->ay, slice->bx, slice->by, slice->adir, slice->bdir);*/ |
/* bottom 180*/ |
if (slice->adir < 0) { |
if (slice->ay || slice->by) |
return 1; |
if (slice->ax == -slice->bx) |
return 0; |
} |
return 3; |
} |
#endif |
/* hline on the same vertical side with the given edge? */ |
if ((y >= 0 && ye >= 0) || (y < 0 && ye < 0)) { |
MWCOORD x; |
|
if (ye == 0) x = xe; else |
x = (MWCOORD)(long)xe * y / ye; |
|
if (x >= *x0 && x <= *x1) { |
if (dir > 0) |
*x0 = x; |
else |
*x1 = x; |
return 0; |
} else { |
if (dir > 0) { |
if (x <= *x0) |
return 0; |
} else { |
if (x >= *x1) |
return 0; |
} |
} |
return 3; |
} |
return 1; |
} |
|
/* relative offsets, direction from left to right. */ |
static void |
draw_line(SLICE *slice, MWCOORD x0, MWCOORD y, MWCOORD x1) |
{ |
int dbl = (slice->adir > 0 && slice->bdir < 0); |
int discard, ret; |
MWCOORD x2 = x0, x3 = x1; |
|
if (y == 0) { |
if (slice->type != MWPIE) |
return; |
/* edges on different sides */ |
if ((slice->ay <= 0 && slice->by >= 0) || |
(slice->ay >= 0 && slice->by <= 0)) { |
if (slice->adir < 0) { |
if (x1 > 0) |
x1 = 0; |
} |
if (slice->bdir > 0) { |
if (x0 < 0) |
x0 = 0; |
} |
} else { |
if (!dbl) { |
/* FIXME leaving in draws dot in center*/ |
drawpoint(slice->psd, slice->x0, slice->y0); |
return; |
} |
} |
drawrow(slice->psd, slice->x0 + x0, slice->x0 + x1, slice->y0); |
return; |
} |
|
/* clip left edge / line */ |
ret = clip_line(slice, slice->ax, slice->ay, slice->adir, y, &x0, &x1); |
|
if (dbl) { |
if (!ret) { |
/* edges separate line to two parts */ |
drawrow(slice->psd, slice->x0 + x0, slice->x0 + x1, |
slice->y0 + y); |
x0 = x2; |
x1 = x3; |
} |
} else { |
if (ret > 1) { |
return; |
} |
} |
|
discard = ret; |
ret = clip_line(slice, slice->bx, slice->by, slice->bdir, y, &x0, &x1); |
|
discard += ret; |
if (discard > 2 && !(dbl && ret == 0 && discard == 3)) { |
return; |
} |
if (discard == 2) { |
/* line on other side than slice */ |
if (slice->adir < 0 || slice->bdir > 0) { |
return; |
} |
} |
drawrow(slice->psd, slice->x0 + x0, slice->x0 + x1, slice->y0 + y); |
} |
|
/* draw one line segment or set of points, called from drawarc routine*/ |
static void |
drawarcsegment(SLICE *slice, MWCOORD xp, MWCOORD yp) |
{ |
switch (slice->type) { |
case MWELLIPSEFILL: |
/* draw ellipse fill segment*/ |
drawrow(slice->psd, slice->x0-xp, slice->x0+xp, slice->y0-yp); |
drawrow(slice->psd, slice->x0-xp, slice->x0+xp, slice->y0+yp); |
return; |
|
case MWELLIPSE: |
/* set four points symmetrically situated around a point*/ |
drawpoint(slice->psd, slice->x0 + xp, slice->y0 + yp); |
drawpoint(slice->psd, slice->x0 - xp, slice->y0 + yp); |
drawpoint(slice->psd, slice->x0 + xp, slice->y0 - yp); |
drawpoint(slice->psd, slice->x0 - xp, slice->y0 - yp); |
return; |
|
case MWPIE: |
/* draw top and bottom halfs of pie*/ |
draw_line(slice, -xp, -yp, +xp); |
draw_line(slice, -xp, +yp, +xp); |
return; |
|
default: /* MWARC, MWARCOUTLINE*/ |
/* set four points symmetrically around a point and clip*/ |
draw_line(slice, +xp, +yp, +xp); |
draw_line(slice, -xp, +yp, -xp); |
draw_line(slice, +xp, -yp, +xp); |
draw_line(slice, -xp, -yp, -xp); |
return; |
} |
} |
|
/* General routine to plot points on an arc. Used by arc, pie and ellipse*/ |
static void |
drawarc(SLICE *slice) |
{ |
MWCOORD xp, yp; /* current point (based on center) */ |
MWCOORD rx, ry; |
long Asquared; /* square of x semi axis */ |
long TwoAsquared; |
long Bsquared; /* square of y semi axis */ |
long TwoBsquared; |
long d; |
long dx, dy; |
|
rx = slice->rx; |
ry = slice->ry; |
|
xp = 0; |
yp = ry; |
Asquared = rx * rx; |
TwoAsquared = 2 * Asquared; |
Bsquared = ry * ry; |
TwoBsquared = 2 * Bsquared; |
d = Bsquared - Asquared * ry + (Asquared >> 2); |
dx = 0; |
dy = TwoAsquared * ry; |
|
while (dx < dy) { |
drawarcsegment(slice, xp, yp); |
if (d > 0) { |
yp--; |
dy -= TwoAsquared; |
d -= dy; |
} |
xp++; |
dx += TwoBsquared; |
d += (Bsquared + dx); |
} |
|
d += ((3L * (Asquared - Bsquared) / 2L - (dx + dy)) >> 1); |
|
while (yp >= 0) { |
drawarcsegment(slice, xp, yp); |
if (d < 0) { |
xp++; |
dx += TwoBsquared; |
d += dx; |
} |
yp--; |
dy -= TwoAsquared; |
d += (Asquared - dy); |
} |
|
} |
|
/* |
* Draw an arc or pie using start/end points. |
* Integer only routine. To specify start/end angles, |
* use GdArcAngle, which requires floating point. |
*/ |
void |
GdArc(PSD psd, MWCOORD x0, MWCOORD y0, MWCOORD rx, MWCOORD ry, |
MWCOORD ax, MWCOORD ay, MWCOORD bx, MWCOORD by, int type) |
{ |
MWCOORD adir, bdir; |
SLICE slice; |
|
if (rx <= 0 || ry <= 0) |
return; |
|
/* |
* Calculate right/left side clipping, based on quadrant. |
* dir is positive when right side is filled and negative when |
* left side is to be filled. |
* |
* >= 0 is bottom half |
*/ |
if (ay >= 0) |
adir = 1; |
else |
adir = -1; |
|
if (by >= 0) |
bdir = -1; |
else |
bdir = 1; |
|
/* |
* The clip_line routine has problems around the 0 and |
* 180 degree axes. |
* This <fix> is required to make the clip_line algorithm |
* work. Getting these routines to work for all angles is |
* a bitch. And they're still buggy. Doing this causes |
* half circles to be outlined with a slightly bent line |
* on the x axis. FIXME |
*/ |
if (ay == 0) ++ay; |
if (by == 0) ++by; |
|
/* swap rightmost edge first */ |
if (bx > ax) { |
MWCOORD swap; |
|
swap = ax; |
ax = bx; |
bx = swap; |
|
swap = ay; |
ay = by; |
by = swap; |
|
swap = adir; |
adir = bdir; |
bdir = swap; |
} |
|
/* check for entire area clipped, draw with per-point clipping*/ |
if (GdClipArea(psd, x0-rx, y0-ry, x0+rx, y0+ry) == CLIP_INVISIBLE) |
return; |
|
slice.psd = psd; |
slice.x0 = x0; |
slice.y0 = y0; |
slice.rx = rx; |
slice.ry = ry; |
slice.ax = ax; |
slice.ay = ay; |
slice.bx = bx; |
slice.by = by; |
slice.adir = adir; |
slice.bdir = bdir; |
slice.type = type; |
|
drawarc(&slice); |
|
if (type & MWOUTLINE) { |
/* draw two lines from rx,ry to arc endpoints*/ |
GdLine(psd, x0, y0, x0+ax, y0+ay, TRUE); |
GdLine(psd, x0, y0, x0+bx, y0+by, TRUE); |
} |
|
GdFixCursor(psd); |
} |
|
/* |
* Draw an ellipse using the current clipping region and foreground color. |
* This draws in the outline of the ellipse, or fills it. |
* Integer only routine. |
*/ |
void |
GdEllipse(PSD psd, MWCOORD x, MWCOORD y, MWCOORD rx, MWCOORD ry, MWBOOL fill) |
{ |
SLICE slice; |
|
if (rx < 0 || ry < 0) |
return; |
|
/* Check if the ellipse bounding box is either totally visible |
* or totally invisible. Draw with per-point clipping. |
*/ |
switch (GdClipArea(psd, x - rx, y - ry, x + rx, y + ry)) { |
case CLIP_VISIBLE: |
/* |
* For size considerations, there's no low-level ellipse |
* draw, so we've got to draw all ellipses |
* with per-point clipping for the time being |
psd->DrawEllipse(psd, x, y, rx, ry, fill, gr_foreground); |
GdFixCursor(psd); |
return; |
*/ |
break; |
|
case CLIP_INVISIBLE: |
return; |
} |
|
slice.psd = psd; |
slice.x0 = x; |
slice.y0 = y; |
slice.rx = rx; |
slice.ry = ry; |
slice.type = fill? MWELLIPSEFILL: MWELLIPSE; |
/* other elements unused*/ |
|
drawarc(&slice); |
|
GdFixCursor(psd); |
} |
/devfont.c
0,0 → 1,2689
/* |
* Copyright (c) 2000 Greg Haerr <greg@censoft.com> |
* T1lib Adobe type1 routines contributed by Vidar Hokstad |
* Freetype TrueType routines contributed by Martin Jolicoeur |
* Han Zi Ku routines contributed by Tanghao and Jauming |
* |
* Device-independent font and text drawing routines |
* |
* These routines do the necessary range checking, clipping, and cursor |
* overwriting checks, and then call the lower level device dependent |
* routines to actually do the drawing. The lower level routines are |
* only called when it is known that all the pixels to be drawn are |
* within the device area and are visible. |
*/ |
/*#define NDEBUG*/ |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <assert.h> |
#include <string.h> |
|
#include "device.h" |
#if (UNIX | DOS_DJGPP) |
#define strcmpi strcasecmp |
#endif |
|
#if HAVE_T1LIB_SUPPORT |
#include <t1lib.h> |
#define T1LIB_USE_AA_HIGH |
|
typedef struct { |
PMWFONTPROCS fontprocs; /* common hdr*/ |
MWCOORD fontsize; |
int fontrotation; |
int fontattr; |
|
int fontid; /* t1lib stuff*/ |
} MWT1LIBFONT, *PMWT1LIBFONT; |
|
static int t1lib_init(PSD psd); |
static PMWT1LIBFONT t1lib_createfont(const char *name, MWCOORD height,int attr); |
static void t1lib_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags); |
static MWBOOL t1lib_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo); |
static void t1lib_gettextsize(PMWFONT pfont, const void *text, int cc, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase); |
static void t1lib_destroyfont(PMWFONT pfont); |
|
/* handling routines for MWT1LIBFONT*/ |
static MWFONTPROCS t1lib_procs = { |
MWTF_ASCII, /* routines expect ascii*/ |
t1lib_getfontinfo, |
t1lib_gettextsize, |
NULL, /* gettextbits*/ |
t1lib_destroyfont, |
t1lib_drawtext, |
NULL, /* setfontsize*/ |
NULL, /* setfontrotation*/ |
NULL, /* setfontattr*/ |
}; |
#endif |
|
#ifdef T1LIB_USE_AA_HIGH |
typedef unsigned long OUTPIXELVAL; |
#else |
typedef MWPIXELVAL OUTPIXELVAL; |
#endif |
|
#if HAVE_FREETYPE_SUPPORT |
#include <freetype/freetype.h> |
#include <freetype/ftxkern.h> |
#include <freetype/ftnameid.h> |
#include <freetype/ftxcmap.h> |
#include <freetype/ftxwidth.h> |
#include <math.h> |
|
#ifndef FREETYPE_FONT_DIR |
#define FREETYPE_FONT_DIR "/usr/local/microwin/fonts" |
#endif |
|
#if TT_FREETYPE_MAJOR != 1 | TT_FREETYPE_MINOR < 3 |
#error "You must link with freetype lib version 1.3.x +, and not freetype 2. \ |
Download it at http://www.freetype.org or http://microwindows.org" |
#endif |
|
typedef struct { |
PMWFONTPROCS fontprocs; /* common hdr*/ |
MWCOORD fontsize; |
int fontrotation; |
int fontattr; |
|
TT_Face face; /* freetype stuff*/ |
TT_Instance instance; |
TT_CharMap char_map; |
TT_Kerning directory; |
TT_Matrix matrix; |
TT_Glyph glyph; |
MWBOOL can_kern; |
short last_glyph_code; |
short last_pen_pos; |
} MWFREETYPEFONT, *PMWFREETYPEFONT; |
|
static int freetype_init(PSD psd); |
static PMWFREETYPEFONT freetype_createfont(const char *name, MWCOORD height, |
int attr); |
static MWBOOL freetype_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo); |
static void freetype_gettextsize(PMWFONT pfont, const void *text, |
int cc, MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase); |
static void freetype_destroyfont(PMWFONT pfont); |
static void freetype_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags); |
static void freetype_setfontsize(PMWFONT pfont, MWCOORD fontsize); |
static void freetype_setfontrotation(PMWFONT pfont, int tenthdegrees); |
|
/* handling routines for MWFREETYPEFONT*/ |
static MWFONTPROCS freetype_procs = { |
MWTF_UC16, /* routines expect unicode 16*/ |
freetype_getfontinfo, |
freetype_gettextsize, |
NULL, /* gettextbits*/ |
freetype_destroyfont, |
freetype_drawtext, |
freetype_setfontsize, |
freetype_setfontrotation, |
NULL, /* setfontattr*/ |
}; |
|
static TT_Engine engine; /* THE ONLY freetype engine */ |
#endif /* HAVE_FREETYPE_SUPPORT*/ |
|
#if HAVE_HZK_SUPPORT |
/* |
* 12x12 and 16x16 ascii and chinese fonts |
* Big5 and GB2312 encodings supported |
*/ |
#define MAX_PATH 256 |
typedef struct { |
int width; |
int height; |
int size; |
unsigned long use_count; |
char * pFont; |
char file[MAX_PATH + 1]; |
} HZKFONT; |
|
static int use_big5=1; |
static HZKFONT CFont[2]; /* font cache*/ |
static HZKFONT AFont[2]; /* font cache*/ |
|
/* jmt: moved inside MWHZKFONT*/ |
static int afont_width = 8; |
static int cfont_width = 16; |
static int font_height = 16; |
static char *afont_address; |
static char *cfont_address; |
|
typedef struct { |
PMWFONTPROCS fontprocs; /* common hdr*/ |
MWCOORD fontsize; |
int fontrotation; |
int fontattr; |
|
HZKFONT CFont; /* hzkfont stuff */ |
HZKFONT AFont; |
int afont_width; |
int cfont_width; |
int font_height; |
char *afont_address; |
char *cfont_address; |
} MWHZKFONT, *PMWHZKFONT; |
|
static int hzk_init(PSD psd); |
static PMWHZKFONT hzk_createfont(const char *name, MWCOORD height,int fontattr); |
static MWBOOL hzk_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo); |
static void hzk_gettextsize(PMWFONT pfont, const void *text, |
int cc, MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase); |
#if 0 |
static void hzk_gettextbits(PMWFONT pfont, int ch, IMAGEBITS *retmap, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase); |
static void hzk_setfontrotation(PMWFONT pfont, int tenthdegrees); |
#endif |
static void hzk_destroyfont(PMWFONT pfont); |
static void hzk_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags); |
static void hzk_setfontsize(PMWFONT pfont, MWCOORD fontsize); |
|
/* handling routines for MWHZKFONT*/ |
static MWFONTPROCS hzk_procs = { |
MWTF_ASCII, /* routines expect ASCII*/ |
hzk_getfontinfo, |
hzk_gettextsize, |
NULL, /* hzk_gettextbits*/ |
hzk_destroyfont, |
hzk_drawtext, |
hzk_setfontsize, |
NULL, /* setfontrotation*/ |
NULL, /* setfontattr*/ |
}; |
|
static int |
UC16_to_GB(const unsigned char *uc16, int cc, unsigned char *ascii); |
#endif /* HAVE_HZK_SUPPORT*/ |
|
static PMWFONT gr_pfont; /* current font*/ |
|
/* temp extern decls*/ |
extern MWPIXELVAL gr_foreground; |
extern MWPIXELVAL gr_background; |
extern MWBOOL gr_usebg; |
|
static int utf8_to_utf16(const unsigned char *utf8, int cc, |
unsigned short *unicode16); |
|
#if FONTMAPPER |
/* entry point for font selection*/ |
int select_font(const PMWLOGFONT plogfont, char *physname); |
#endif |
|
/* |
* Set the font for future calls. |
*/ |
PMWFONT |
GdSetFont(PMWFONT pfont) |
{ |
PMWFONT oldfont = gr_pfont; |
|
gr_pfont = pfont; |
return oldfont; |
} |
|
/* |
* Select a font, based on various parameters. |
* If plogfont is specified, name and height parms are ignored |
* and instead used from MWLOGFONT. |
* |
* If height is 0, return builtin font from passed name. |
* Otherwise find builtin font best match based on height. |
*/ |
PMWFONT |
GdCreateFont(PSD psd, const char *name, MWCOORD height, |
const PMWLOGFONT plogfont) |
{ |
int i; |
int fontht; |
int fontno; |
int fontclass; |
int fontattr = 0; |
PMWFONT pfont; |
PMWCOREFONT pf = psd->builtin_fonts; |
MWFONTINFO fontinfo; |
MWSCREENINFO scrinfo; |
char fontname[128]; |
|
GdGetScreenInfo(psd, &scrinfo); |
|
/* if plogfont not specified, use name and height*/ |
if (!plogfont) { |
if (!name) |
name = MWFONT_SYSTEM_VAR; |
strcpy(fontname, name); |
fontclass = MWLF_CLASS_ANY; |
} else { |
#if FONTMAPPER |
/* otherwise, use MWLOGFONT name and height*/ |
fontclass = select_font(plogfont, fontname); |
#else |
if (!name) |
name = MWFONT_SYSTEM_VAR; |
strcpy(fontname, name); |
fontclass = MWLF_CLASS_ANY; |
#endif |
height = plogfont->lfHeight; |
if (plogfont->lfUnderline) |
fontattr = MWTF_UNDERLINE; |
} |
height = abs(height); |
|
if (!fontclass) |
goto first; |
|
/* use builtin screen fonts, FONT_xxx, if height is 0 */ |
if (height == 0 || fontclass == MWLF_CLASS_ANY || |
fontclass == MWLF_CLASS_BUILTIN) { |
for(i = 0; i < scrinfo.fonts; ++i) { |
if(!strcmpi(pf[i].name, fontname)) { |
pf[i].fontsize = pf[i].cfont->height; |
pf[i].fontattr = fontattr; |
return (PMWFONT)&pf[i]; |
} |
} |
|
/* return first builtin font*/ |
if (height == 0 || fontclass == MWLF_CLASS_BUILTIN) |
goto first; |
} |
|
#if HAVE_HZK_SUPPORT |
/* Make sure the library is initialized */ |
if (hzk_init(psd)) { |
pfont = (PMWFONT)hzk_createfont(name, height, fontattr); |
if(pfont) |
return pfont; |
fprintf(stderr, "hzk_createfont: %s not found\n", name); |
} |
#endif |
|
#if HAVE_FREETYPE_SUPPORT |
if (fontclass == MWLF_CLASS_ANY || fontclass == MWLF_CLASS_FREETYPE) { |
if (freetype_init(psd)) { |
/* auto antialias for height > 14 for kaffe*/ |
if (plogfont && plogfont->lfHeight > 14 && |
plogfont->lfQuality) |
fontattr |= MWTF_ANTIALIAS; |
|
pfont = (PMWFONT)freetype_createfont(fontname, height, |
fontattr); |
if(pfont) { |
/* temp kaffe kluge*/ |
pfont->fontattr |= FS_FREETYPE; |
return pfont; |
} |
DPRINTF("freetype_createfont: %s,%d not found\n", |
fontname, height); |
} |
} |
#endif |
|
#if HAVE_T1LIB_SUPPORT |
if (fontclass == MWLF_CLASS_ANY || fontclass == MWLF_CLASS_T1LIB) { |
if (t1lib_init(psd)) { |
pfont = (PMWFONT)t1lib_createfont(fontname, height, |
fontattr); |
if(pfont) |
return pfont; |
DPRINTF("t1lib_createfont: %s,%d not found\n", |
fontname, height); |
} |
} |
#endif |
|
/* find builtin font closest in height*/ |
if(height != 0) { |
fontno = 0; |
height = abs(height); |
fontht = MAX_MWCOORD; |
for(i = 0; i < scrinfo.fonts; ++i) { |
pfont = (PMWFONT)&pf[i]; |
GdGetFontInfo(pfont, &fontinfo); |
if(fontht > abs(height-fontinfo.height)) { |
fontno = i; |
fontht = abs(height-fontinfo.height); |
} |
} |
pf[fontno].fontsize = pf[fontno].cfont->height; |
pf[fontno].fontattr = fontattr; |
return (PMWFONT)&pf[fontno]; |
} |
|
first: |
/* Return first builtin font*/ |
pf->fontsize = pf->cfont->height; |
pf->fontattr = fontattr; |
return (PMWFONT)&pf[0]; |
} |
|
/* Set the font size for the passed font*/ |
MWCOORD |
GdSetFontSize(PMWFONT pfont, MWCOORD fontsize) |
{ |
MWCOORD oldfontsize = pfont->fontsize; |
|
pfont->fontsize = fontsize; |
|
if (pfont->fontprocs->SetFontSize) |
pfont->fontprocs->SetFontSize(pfont, fontsize); |
|
return oldfontsize; |
} |
|
/* Set the font rotation angle in tenths of degrees for the passed font*/ |
int |
GdSetFontRotation(PMWFONT pfont, int tenthdegrees) |
{ |
MWCOORD oldrotation = pfont->fontrotation; |
|
pfont->fontrotation = tenthdegrees; |
|
if (pfont->fontprocs->SetFontRotation) |
pfont->fontprocs->SetFontRotation(pfont, tenthdegrees); |
|
return oldrotation; |
} |
|
/* |
* Set/reset font attributes (MWTF_KERNING, MWTF_ANTIALIAS) |
* for the passed font. |
*/ |
int |
GdSetFontAttr(PMWFONT pfont, int setflags, int clrflags) |
{ |
MWCOORD oldattr = pfont->fontattr; |
|
pfont->fontattr &= ~clrflags; |
pfont->fontattr |= setflags; |
|
if (pfont->fontprocs->SetFontAttr) |
pfont->fontprocs->SetFontAttr(pfont, setflags, clrflags); |
|
return oldattr; |
} |
|
/* Unload and deallocate font*/ |
void |
GdDestroyFont(PMWFONT pfont) |
{ |
if (pfont->fontprocs->DestroyFont) |
pfont->fontprocs->DestroyFont(pfont); |
} |
|
/* Return information about a specified font*/ |
MWBOOL |
GdGetFontInfo(PMWFONT pfont, PMWFONTINFO pfontinfo) |
{ |
if(!pfont || !pfont->fontprocs->GetFontInfo) |
return FALSE; |
|
return pfont->fontprocs->GetFontInfo(pfont, pfontinfo); |
} |
|
/* |
* Convert from one encoding to another |
* Input cc and returned cc is character count, not bytes |
* Return < 0 on error or can't translate |
*/ |
int |
GdConvertEncoding(const void *istr, int iflags, int cc, void *ostr, int oflags) |
{ |
const unsigned char *istr8; |
const unsigned short *istr16; |
const unsigned long *istr32; |
unsigned char *ostr8; |
unsigned short *ostr16; |
unsigned long *ostr32; |
unsigned int ch; |
int icc; |
unsigned short buf16[512]; |
|
iflags &= MWTF_PACKMASK; |
oflags &= MWTF_PACKMASK; |
|
/* allow -1 for len with ascii*/ |
if(cc == -1 && (iflags == MWTF_ASCII)) |
cc = strlen((char *)istr); |
|
/* first check for utf8 input encoding*/ |
if(iflags == MWTF_UTF8) { |
/* we've only got uc16 now so convert to uc16...*/ |
cc = utf8_to_utf16((unsigned char *)istr, cc, |
oflags==MWTF_UC16?(unsigned short*) ostr: buf16); |
|
if(oflags == MWTF_UC16 || cc < 0) |
return cc; |
|
/* will decode again to requested format (probably ascii)*/ |
iflags = MWTF_UC16; |
istr = buf16; |
} |
|
#if HAVE_HZK_SUPPORT |
if(iflags == MWTF_UC16 && oflags == MWTF_ASCII) { |
/* only support uc16 convert to ascii now...*/ |
cc = UC16_to_GB( istr, cc, ostr); |
return cc; |
} |
#endif |
|
icc = cc; |
istr8 = istr; |
istr16 = istr; |
istr32 = istr; |
ostr8 = ostr; |
ostr16 = ostr; |
ostr32 = ostr; |
|
/* Convert between formats. Note that there's no error |
* checking here yet. |
*/ |
while(--icc >= 0) { |
switch(iflags) { |
default: |
ch = *istr8++; |
break; |
case MWTF_UC16: |
ch = *istr16++; |
break; |
case MWTF_UC32: |
ch = *istr32++; |
} |
switch(oflags) { |
default: |
*ostr8++ = (unsigned char)ch; |
break; |
case MWTF_UC16: |
*ostr16++ = (unsigned short)ch; |
break; |
case MWTF_UC32: |
*ostr32++ = ch; |
} |
} |
return cc; |
} |
|
/* Get the width and height of passed text string in the passed font*/ |
void |
GdGetTextSize(PMWFONT pfont, const void *str, int cc, MWCOORD *pwidth, |
MWCOORD *pheight, MWCOORD *pbase, int flags) |
{ |
const void * text; |
unsigned long buf[256]; |
int defencoding = pfont->fontprocs->encoding; |
|
/* convert encoding if required*/ |
if((flags & MWTF_PACKMASK) != defencoding) { |
cc = GdConvertEncoding(str, flags, cc, buf, defencoding); |
flags &= ~MWTF_PACKMASK; |
flags |= defencoding; |
text = buf; |
} else text = str; |
|
if(cc == -1 && (flags & MWTF_PACKMASK) == MWTF_ASCII) |
cc = strlen((char *)str); |
|
if(cc <= 0 || !pfont->fontprocs->GetTextSize) { |
*pwidth = *pheight = *pbase = 0; |
return; |
} |
|
/* calc height and width of string*/ |
pfont->fontprocs->GetTextSize(pfont, text, cc, pwidth, pheight, pbase); |
} |
|
/* Draw a text string at a specifed coordinates in the foreground color |
* (and possibly the background color), applying clipping if necessary. |
* The background color is only drawn if the gr_usebg flag is set. |
* Use the current font. |
*/ |
void |
GdText(PSD psd, MWCOORD x, MWCOORD y, const void *str, int cc, int flags) |
{ |
const void * text; |
unsigned long buf[256]; |
int defencoding = gr_pfont->fontprocs->encoding; |
|
/* convert encoding if required*/ |
if((flags & MWTF_PACKMASK) != defencoding) { |
cc = GdConvertEncoding(str, flags, cc, buf, defencoding); |
flags &= ~MWTF_PACKMASK; |
flags |= defencoding; |
text = buf; |
} else text = str; |
|
if(cc == -1 && (flags & MWTF_PACKMASK) == MWTF_ASCII) |
cc = strlen((char *)str); |
|
if(cc <= 0 || !gr_pfont->fontprocs->DrawText) |
return; |
|
/* draw text string*/ |
gr_pfont->fontprocs->DrawText(gr_pfont, psd, x, y, text, cc, flags); |
} |
|
/* |
* Draw ascii text using COREFONT type font. |
*/ |
void |
corefont_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags) |
{ |
const unsigned char *str = text; |
MWCOORD width; /* width of text area */ |
MWCOORD height; /* height of text area */ |
MWCOORD base; /* baseline of text*/ |
MWCOORD startx, starty; |
/* bitmap for characters */ |
MWIMAGEBITS bitmap[MAX_CHAR_HEIGHT*MAX_CHAR_WIDTH/MWIMAGE_BITSPERIMAGE]; |
|
pfont->fontprocs->GetTextSize(pfont, str, cc, &width, &height, &base); |
|
if(flags & MWTF_BASELINE) |
y -= base; |
else if(flags & MWTF_BOTTOM) |
y -= (height - 1); |
startx = x; |
starty = y + base; |
|
switch (GdClipArea(psd, x, y, x + width - 1, y + height - 1)) { |
case CLIP_VISIBLE: |
/* |
* For size considerations, there's no low-level text |
* draw, so we've got to draw all text |
* with per-point clipping for the time being |
if (gr_usebg) |
psd->FillRect(psd, x, y, x + width - 1, y + height - 1, |
gr_background); |
psd->DrawText(psd, x, y, str, cc, gr_foreground, pfont); |
GdFixCursor(psd); |
return; |
*/ |
break; |
|
case CLIP_INVISIBLE: |
return; |
} |
|
/* Get the bitmap for each character individually, and then display |
* them using clipping for each one. |
*/ |
while (--cc >= 0 && x < psd->xvirtres) { |
unsigned int ch = *str++; |
#if HAVE_BIG5_SUPPORT |
/* chinese big5 decoding*/ |
if (ch >= 0xA1 && ch <= 0xF9 && cc >= 1 && |
((*str >= 0x40 && *str <= 0x7E) || |
(*str >= 0xA1 && *str <= 0xFE)) ) { |
ch = (ch << 8) | *str++; |
--cc; |
} |
#endif |
#if HAVE_GB2312_SUPPORT |
/* chinese gb2312 decoding*/ |
if (ch >= 0xA1 && ch < 0xF8 && cc >= 1 && |
*str >= 0xA1 && *str < 0xFF) { |
ch = (ch << 8) | *str++; |
--cc; |
} |
#endif |
#if HAVE_KSC5601_SUPPORT |
/* Korean KSC5601 decoding */ |
if (ch >= 0xA1 && ch <= 0xFE && cc >= 1 && |
(*str >= 0xA1 && *str <= 0xFE)) { |
ch = (ch << 8) | *str++; |
--cc; |
} |
#endif |
pfont->fontprocs->GetTextBits(pfont, ch, bitmap, &width, |
&height, &base); |
|
/* note: change to bitmap*/ |
GdBitmap(psd, x, y, width, height, bitmap); |
x += width; |
} |
|
if (pfont->fontattr & MWTF_UNDERLINE) |
GdLine(psd, startx, starty, x, starty, FALSE); |
|
GdFixCursor(psd); |
} |
|
#if HAVE_T1LIB_SUPPORT | HAVE_FREETYPE_SUPPORT |
/* |
* Produce blend table from src and dst based on passed alpha table |
* Used because we don't quite yet have GdArea with alphablending, |
* so we pre-blend fg/bg colors for fade effect. |
*/ |
static void |
alphablend(PSD psd, OUTPIXELVAL *out, MWPIXELVAL src, MWPIXELVAL dst, |
unsigned char *alpha, int count) |
{ |
unsigned int a, d; |
unsigned char r, g, b; |
MWCOLORVAL palsrc, paldst; |
extern MWPALENTRY gr_palette[256]; |
|
while (--count >= 0) { |
a = *alpha++; |
|
#define BITS(pixel,shift,mask) (((pixel)>>shift)&(mask)) |
if(a == 0) |
*out++ = dst; |
else if(a == 255) |
*out++ = src; |
else |
switch(psd->pixtype) { |
case MWPF_TRUECOLOR0888: |
case MWPF_TRUECOLOR888: |
d = BITS(dst, 16, 0xff); |
r = (unsigned char)(((BITS(src, 16, 0xff) - d)*a)>>8) + d; |
d = BITS(dst, 8, 0xff); |
g = (unsigned char)(((BITS(src, 8, 0xff) - d)*a)>>8) + d; |
d = BITS(dst, 0, 0xff); |
b = (unsigned char)(((BITS(src, 0, 0xff) - d)*a)>>8) + d; |
*out++ = (r << 16) | (g << 8) | b; |
break; |
|
case MWPF_TRUECOLOR565: |
d = BITS(dst, 11, 0x1f); |
r = (unsigned char)(((BITS(src, 11, 0x1f) - d)*a)>>8) + d; |
d = BITS(dst, 5, 0x3f); |
g = (unsigned char)(((BITS(src, 5, 0x3f) - d)*a)>>8) + d; |
d = BITS(dst, 0, 0x1f); |
b = (unsigned char)(((BITS(src, 0, 0x1f) - d)*a)>>8) + d; |
*out++ = (r << 11) | (g << 5) | b; |
break; |
|
case MWPF_TRUECOLOR555: |
d = BITS(dst, 10, 0x1f); |
r = (unsigned char)(((BITS(src, 10, 0x1f) - d)*a)>>8) + d; |
d = BITS(dst, 5, 0x1f); |
g = (unsigned char)(((BITS(src, 5, 0x1f) - d)*a)>>8) + d; |
d = BITS(dst, 0, 0x1f); |
b = (unsigned char)(((BITS(src, 0, 0x1f) - d)*a)>>8) + d; |
*out++ = (r << 10) | (g << 5) | b; |
break; |
|
case MWPF_TRUECOLOR332: |
d = BITS(dst, 5, 0x07); |
r = (unsigned char)(((BITS(src, 5, 0x07) - d)*a)>>8) + d; |
d = BITS(dst, 2, 0x07); |
g = (unsigned char)(((BITS(src, 2, 0x07) - d)*a)>>8) + d; |
d = BITS(dst, 0, 0x03); |
b = (unsigned char)(((BITS(src, 0, 0x03) - d)*a)>>8) + d; |
*out++ = (r << 5) | (g << 2) | b; |
break; |
|
case MWPF_PALETTE: |
/* reverse lookup palette entry for blend ;-)*/ |
palsrc = GETPALENTRY(gr_palette, src); |
paldst = GETPALENTRY(gr_palette, dst); |
d = REDVALUE(paldst); |
r = (unsigned char)(((REDVALUE(palsrc) - d)*a)>>8) + d; |
d = GREENVALUE(paldst); |
g = (unsigned char)(((GREENVALUE(palsrc) - d)*a)>>8) + d; |
d = BLUEVALUE(paldst); |
b = (unsigned char)(((BLUEVALUE(palsrc) - d)*a)>>8) + d; |
*out++ = GdFindNearestColor(gr_palette, (int)psd->ncolors, |
MWRGB(r, g, b)); |
break; |
} |
} |
} |
#endif /*HAVE_T1LIB_SUPPORT | HAVE_FREETYPE_SUPPORT*/ |
|
#if HAVE_T1LIB_SUPPORT |
/* contributed by Vidar Hokstad*/ |
|
static int |
t1lib_init(PSD psd) |
{ |
char **encoding; |
static int inited = 0; |
|
if (inited) |
return 1; |
|
T1_SetBitmapPad(8); |
if (!T1_InitLib(0)) |
return 0; |
|
/* set default Latin1 encoding*/ |
encoding = T1_LoadEncoding("IsoLatin1.enc"); |
T1_SetDefaultEncoding(encoding); |
|
#ifdef T1LIB_USE_AA_HIGH |
T1_AASetLevel(T1_AA_HIGH); |
#else |
T1_AASetLevel(T1_AA_LOW); |
#endif |
#if 0 |
/* kluge: this is required if 16bpp drawarea driver is used*/ |
if(psd->bpp == 16) |
T1_AASetBitsPerPixel(16); |
else |
#endif |
T1_AASetBitsPerPixel(sizeof(MWPIXELVAL)*8); |
|
inited = 1; |
return 1; |
} |
|
static PMWT1LIBFONT |
t1lib_createfont(const char *name, MWCOORD height, int attr) |
{ |
PMWT1LIBFONT pf; |
int id; |
char * p; |
char buf[256]; |
|
/* match name against t1 font id's from t1 font database*/ |
for(id=0; id<T1_Get_no_fonts(); ++id) { |
strncpy(buf, T1_GetFontFileName(id), sizeof(buf)); |
|
/* remove extension*/ |
for(p=buf; *p; ++p) { |
if(*p == '.') { |
*p = 0; |
break; |
} |
} |
|
if(!strcmpi(name, buf)) { |
/* allocate font structure*/ |
pf = (PMWT1LIBFONT)calloc(sizeof(MWT1LIBFONT), 1); |
if (!pf) |
return NULL; |
pf->fontprocs = &t1lib_procs; |
GdSetFontSize((PMWFONT)pf, height); |
GdSetFontRotation((PMWFONT)pf, 0); |
GdSetFontAttr((PMWFONT)pf, attr, 0); |
pf->fontid = id; |
return pf; |
} |
} |
return NULL; |
} |
|
/* |
* Draw ascii text string using T1LIB type font |
*/ |
static void |
t1lib_drawtext(PMWFONT pfont, PSD psd, MWCOORD x, MWCOORD y, |
const void *text, int cc, int flags) |
{ |
PMWT1LIBFONT pf = (PMWT1LIBFONT)pfont; |
const unsigned char *str = text; |
MWCOORD width; /* width of text area */ |
MWCOORD height; /* height of text area */ |
MWCOORD underliney; |
GLYPH * g; /* T1lib glyph structure. Memory handling by T1lib */ |
#ifdef T1LIB_USE_AA_HIGH |
OUTPIXELVAL gvals[17]; |
|
/* Blending array for antialiasing. The steeper the values increase |
* near the end, the sharper the characters look, but also more jagged |
*/ |
static unsigned char blend[17] = { |
0x00, 0x00, 0x04, 0x0c, 0x10, 0x14, 0x18, 0x20, |
0x30, 0x38, 0x40, 0x50, 0x70, 0x80, 0xa0, 0xc0, 0xff |
}; |
#else |
OUTPIXELVAL gvals[5]; |
static unsigned char blend[5] = { 0x00, 0x44, 0x88, 0xcc, 0xff }; |
#endif |
|
/* Check if we should throw out some fonts */ |
|
if (pf->fontattr&MWTF_ANTIALIAS) { |
#ifdef T1LIB_USE_AA_HIGH |
alphablend(psd, gvals, gr_foreground, gr_background, blend, 17); |
T1_AAHSetGrayValues(gvals); |
#else |
alphablend(psd, gvals, gr_foreground, gr_background, blend, 5); |
T1_AASetGrayValues(gvals[0],gvals[1],gvals[2],gvals[3],gvals[4]); |
#endif |
g = T1_AASetString(pf->fontid,(char *)str,cc,0, |
(pf->fontattr&MWTF_KERNING)? T1_KERNING: 0, |
pf->fontsize * 1.0, 0); |
|
if (g && g->bits) { |
/*MWPIXELVAL save = gr_background;*/ |
width = g->metrics.rightSideBearing - g->metrics.leftSideBearing; |
height = g->metrics.ascent - g->metrics.descent; |
|
if(flags & MWTF_BASELINE) |
y -= g->metrics.ascent; |
else if(flags & MWTF_BOTTOM) |
y -= (height - 1); |
underliney = y + g->metrics.ascent; |
|
/* FIXME: Looks damn ugly if usebg is false. |
* Will be handled when using alphablending in GdArea... |
*/ |
/* clipping handled in GdArea*/ |
/*FIXME kluge for transparency*/ |
/*gr_background = gr_foreground + 1;*/ |
/*gr_usebg = 0;*/ |
GdArea(psd,x,y, width, height, g->bits, MWPF_PIXELVAL); |
/*gr_background = save;*/ |
|
if (pf->fontattr & MWTF_UNDERLINE) |
GdLine(psd, x, underliney, x+width, underliney, FALSE); |
|
} |
} else { |
/* Do non-aa drawing */ |
g = T1_SetString(pf->fontid,(char *)str,cc,0, |
(pf->fontattr&MWTF_KERNING)? T1_KERNING: 0, |
pf->fontsize * 1.0, 0); |
|
if (g && g->bits) { |
unsigned char * b; |
int xoff; |
int maxy; |
int xmod; |
|
/* I'm sure this sorry excuse for a bitmap rendering routine can |
* be optimized quite a bit ;) |
*/ |
width = g->metrics.rightSideBearing - g->metrics.leftSideBearing; |
height = g->metrics.ascent - g->metrics.descent; |
|
if(flags & MWTF_BASELINE) |
y -= g->metrics.ascent; |
else if(flags & MWTF_BOTTOM) |
y -= (height - 1); |
underliney = y + g->metrics.ascent; |
|
b = g->bits; |
maxy = y + height; |
|
/* if ((x + width) > psd->xvirtres) { |
xmod = (x + width - psd->xvirtres + 7) >> 3; |
width = width + x + width - psd->xvirtres; |
} else xmod = 0; |
*/ |
xmod = 0; |
while (y < maxy) { |
unsigned char data; |
xoff = 0; |
while (xoff < width ) { |
if (!(xoff % 8)) { |
data = *b; |
b++; |
} |
|
if (GdClipPoint(psd, x+xoff,y)) { |
if (gr_usebg) { |
psd->DrawPixel(psd,x+xoff,y, |
data & (1 << (xoff % 8)) ? |
gr_foreground : gr_background); |
} else if (data & (1 << (xoff % 8))) { |
psd->DrawPixel(psd,x+xoff,y, gr_foreground); |
} |
} |
xoff++; |
} |
b += xmod; |
y++; |
} |
if (pf->fontattr & MWTF_UNDERLINE) |
GdLine(psd, x, underliney, x+xoff, underliney, FALSE); |
} |
} |
|
if (g && g->bits) { |
/* Save some memory */ |
free(g->bits); |
g->bits = 0; /* Make sure T1lib doesnt try to free it again */ |
} |
|
GdFixCursor(psd); |
} |
|
static MWBOOL |
t1lib_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo) |
{ |
int i; |
MWCOORD width, height, baseline; |
|
/* FIXME, guess all sizes*/ |
GdGetTextSize(pfont, "A", 1, &width, &height, &baseline, MWTF_ASCII); |
pfontinfo->height = height; |
pfontinfo->maxwidth = width; |
pfontinfo->baseline = 0; |
pfontinfo->firstchar = 32; |
pfontinfo->lastchar = 255; |
pfontinfo->fixed = TRUE; |
for(i=0; i<256; ++i) |
pfontinfo->widths[i] = width; |
return TRUE; |
} |
|
/* Get the width and height of passed text string in the current font*/ |
static void |
t1lib_gettextsize(PMWFONT pfont, const void *text, int cc, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase) |
{ |
PMWT1LIBFONT pf = (PMWT1LIBFONT)pfont; |
const unsigned char * str = text; |
GLYPH * g; |
|
g = T1_SetString(pf->fontid, (char *)str, cc, 0, |
(pf->fontattr&MWTF_KERNING)? T1_KERNING: 0, pf->fontsize * 1.0, 0); |
*pwidth = g->metrics.rightSideBearing - g->metrics.leftSideBearing; |
*pheight = g->metrics.ascent - g->metrics.descent; |
if(g && g->bits) { |
free(g->bits); |
g->bits = 0; |
} |
#if 0 |
BBox b; |
|
/* FIXME. Something is *VERY* wrong here */ |
b = T1_GetStringBBox(pf->fontid, str, cc, 0, (pf->fontattr&MWTF_KERNING)?T1_KERNING:0); |
|
DPRINTF("b.urx = %d, b.llx = %d\n",b.urx, b.llx); |
DPRINTF("b.ury = %d, b.lly = %d\n",b.ury, b.lly); |
*pwidth = (b.urx - b.llx); |
*pheight = (b.lly - b.ury); |
#endif |
} |
|
static void |
t1lib_destroyfont(PMWFONT pfont) |
{ |
PMWT1LIBFONT pf = (PMWT1LIBFONT)pfont; |
|
T1_DeleteAllSizes(pf->fontid); |
free(pf); |
} |
|
#endif /* HAVE_T1LIB_SUPPORT*/ |
|
#if HAVE_FREETYPE_SUPPORT |
static OUTPIXELVAL gray_palette[5]; |
|
static int |
freetype_init(PSD psd) |
{ |
static int inited = 0; |
|
if (inited) |
return 1; |
|
/* Init freetype library */ |
if (TT_Init_FreeType (&engine) != TT_Err_Ok) { |
return 0; |
} |
|
/* Init kerning extension */ |
if (TT_Init_Kerning_Extension (engine) != TT_Err_Ok) |
return 0; |
|
inited = 1; |
return 1; |
} |
|
static PMWFREETYPEFONT |
freetype_createfont(const char *name, MWCOORD height, int attr) |
{ |
PMWFREETYPEFONT pf; |
unsigned short i, n; |
unsigned short platform, encoding; |
TT_Face_Properties properties; |
char * p; |
char fontname[128]; |
|
/* check for pathname prefix*/ |
if (strchr(name, '/') != NULL) |
strcpy(fontname, name); |
else { |
strcpy(fontname, FREETYPE_FONT_DIR); |
strcat(fontname, "/"); |
strcat(fontname, name); |
} |
|
/* check for extension*/ |
if ((p = strrchr(fontname, '.')) == NULL || |
strcmp(p, ".ttf") != 0) { |
strcat(fontname, ".ttf"); |
} |
|
/* allocate font structure*/ |
pf = (PMWFREETYPEFONT)calloc(sizeof(MWFREETYPEFONT), 1); |
if (!pf) |
return NULL; |
pf->fontprocs = &freetype_procs; |
|
/* Load face */ |
if (TT_Open_Face (engine, fontname, &pf->face) != TT_Err_Ok) |
goto out; |
|
/* Load first kerning table */ |
pf->can_kern = TRUE; |
if (TT_Load_Kerning_Table (pf->face, 0) != TT_Err_Ok) |
pf->can_kern = FALSE; |
else { |
if (TT_Get_Kerning_Directory (pf->face, &pf->directory) |
!= TT_Err_Ok) |
pf->can_kern = FALSE; |
else { |
/* Support only version 0 kerning table ... */ |
if ((pf->directory.version != 0) || |
(pf->directory.nTables <= 0) || |
(pf->directory.tables->loaded != 1) || |
(pf->directory.tables->version != 0) || |
(pf->directory.tables->t.kern0.nPairs <= 0)) |
pf->can_kern = FALSE; |
} |
} |
|
/* get face properties and allocate preload arrays */ |
TT_Get_Face_Properties (pf->face, &properties); |
|
#if 0 |
/* |
* Use header information for ascent and descent |
* to compute scaled ascent/descent for current font height. |
*/ |
h = properties.os2->sTypoAscender - properties.os2->sTypoDescender |
+ properties.os2->sTypoLineGap; |
ascent = properties.os2->sTypoAscender |
+ properties.os2->sTypoLineGap/2; |
pf->ascent = (ascent * height + h/2) / h; |
pf->descent = height - pf->ascent; |
#endif |
/* Create a glyph container */ |
if (TT_New_Glyph (pf->face, &pf->glyph) != TT_Err_Ok) |
goto out; |
|
/* create instance */ |
if (TT_New_Instance (pf->face, &pf->instance) != TT_Err_Ok) |
goto out; |
|
/* Set the instance resolution */ |
if (TT_Set_Instance_Resolutions (pf->instance, 96, 96) != TT_Err_Ok) |
goto out; |
|
/* Look for a Unicode charmap: Windows flavor of Apple flavor only */ |
n = properties.num_CharMaps; |
|
for (i = 0; i < n; i++) { |
TT_Get_CharMap_ID (pf->face, i, &platform, &encoding); |
if (((platform == TT_PLATFORM_MICROSOFT) && |
(encoding == TT_MS_ID_UNICODE_CS)) || |
((platform == TT_PLATFORM_APPLE_UNICODE) && |
(encoding == TT_APPLE_ID_DEFAULT))) |
{ |
TT_Get_CharMap (pf->face, i, &pf->char_map); |
i = n + 1; |
} |
} |
if (i == n) { |
DPRINTF("freetype_createfont: no unicode map table\n"); |
goto out; |
} |
|
GdSetFontSize((PMWFONT)pf, height); |
GdSetFontRotation((PMWFONT)pf, 0); |
GdSetFontAttr((PMWFONT)pf, attr, 0); |
|
return pf; |
|
out: |
free(pf); |
return NULL; |
} |
|
static int |
compute_kernval(PMWFREETYPEFONT pf, short current_glyph_code) |
{ |
int i = 0; |
int kernval; |
int nPairs = pf->directory.tables->t.kern0.nPairs; |
TT_Kern_0_Pair *pair = pf->directory.tables->t.kern0.pairs; |
|
if (pf->last_glyph_code != -1) { |
while ((pair->left != pf->last_glyph_code) |
&& (pair->right != current_glyph_code)) |
{ |
pair++; |
i++; |
if (i == nPairs) |
break; |
} |
|
if (i == nPairs) |
kernval = 0; |
else |
/* We round the value (hence the +32) */ |
kernval = (pair->value + 32) & -64; |
} else |
kernval = 0; |
|
return kernval; |
} |
|
static TT_UShort |
Get_Glyph_Width(PMWFREETYPEFONT pf, TT_UShort glyph_index) |
{ |
TT_Glyph_Metrics metrics; |
|
if (TT_Load_Glyph ( pf->instance, pf->glyph, |
TT_Char_Index (pf->char_map,glyph_index), |
TTLOAD_SCALE_GLYPH|TTLOAD_HINT_GLYPH) != TT_Err_Ok) |
{ |
/* Try to load default glyph: index 0 */ |
if (TT_Load_Glyph ( pf->instance, pf->glyph, 0, |
TTLOAD_SCALE_GLYPH|TTLOAD_HINT_GLYPH) != TT_Err_Ok) |
return 0; |
} |
|
TT_Get_Glyph_Metrics (pf->glyph, &metrics); |
return((metrics.advance & 0xFFFFFFC0) >> 6); |
} |
|
/* Render a single glyph*/ |
static void |
drawchar(PMWFREETYPEFONT pf, PSD psd, TT_Glyph glyph, int x_offset, |
int y_offset) |
{ |
TT_F26Dot6 xmin, ymin, xmax, ymax, x, y, z; |
unsigned char *src, *srcptr; |
MWPIXELVAL *dst, *dstptr; |
MWPIXELVAL *bitmap; |
int size, width, height; |
TT_Outline outline; |
TT_BBox bbox; |
TT_Raster_Map Raster; |
TT_Error error; |
/*MWPIXELVAL save;*/ |
|
/* we begin by grid-fitting the bounding box */ |
TT_Get_Glyph_Outline (pf->glyph, &outline); |
TT_Get_Outline_BBox (&outline, &bbox); |
|
xmin = (bbox.xMin & -64) >> 6; |
ymin = (bbox.yMin & -64) >> 6; |
xmax = ((bbox.xMax + 63) & -64) >> 6; |
ymax = ((bbox.yMax + 63) & -64) >> 6; |
width = xmax - xmin; |
height = ymax - ymin; |
size = width * height; |
|
/* now re-allocate the raster bitmap */ |
Raster.rows = height; |
Raster.width = width; |
|
if (pf->fontattr&MWTF_ANTIALIAS) |
Raster.cols = (Raster.width + 3) & -4; /* pad to 32-bits */ |
else |
Raster.cols = (Raster.width + 7) & -8; /* pad to 64-bits ??? */ |
|
Raster.flow = TT_Flow_Up; |
Raster.size = Raster.rows * Raster.cols; |
Raster.bitmap = malloc (Raster.size); |
|
memset (Raster.bitmap, 0, Raster.size); |
|
/* now render the glyph in the small pixmap */ |
|
/* IMPORTANT NOTE: the offset parameters passed to the function */ |
/* TT_Get_Glyph_Bitmap() must be integer pixel values, i.e., */ |
/* multiples of 64. HINTING WILL BE RUINED IF THIS ISN'T THE CASE! */ |
/* This is why we _did_ grid-fit the bounding box, especially xmin */ |
/* and ymin. */ |
|
if (!(pf->fontattr&MWTF_ANTIALIAS)) |
error = TT_Get_Glyph_Bitmap (pf->glyph, &Raster, |
-xmin * 64, -ymin * 64); |
else |
error = TT_Get_Glyph_Pixmap (pf->glyph, &Raster, |
-xmin * 64, -ymin * 64); |
|
if (error) { |
free (Raster.bitmap); |
return; |
} |
|
bitmap = malloc (size * sizeof (MWPIXELVAL)); |
memset (bitmap, 0, size * sizeof (MWPIXELVAL)); |
|
src = (char *) Raster.bitmap; |
dst = bitmap + (size - width); |
|
for (y = ymin; y < ymax; y++) { |
srcptr = src; |
dstptr = dst; |
|
for (x = xmin; x < xmax; x++) { |
if (pf->fontattr&MWTF_ANTIALIAS) |
*dstptr++ = gray_palette[(int) *srcptr]; |
else { |
for(z=0; |
z <= ((xmax-x-1) < 7 ? (xmax-x-1) : 7); |
z++) { |
*dstptr++ = ((*srcptr << z) & 0x80)? |
gr_foreground: gr_background; |
} |
x += 7; |
} |
|
srcptr++; |
} |
|
src += Raster.cols; |
dst -= width; |
} |
|
/* FIXME - must clear background upstairs if not gr_usebg*/ |
/* FIXME: GdArea problem if fg == bg*/ |
/*save = gr_background;*/ |
/*gr_background = gr_foreground + 1;*/ |
|
/* Now draw the bitmap ... */ |
GdArea(psd, x_offset + xmin, y_offset - (ymin + height), width, height, |
bitmap, MWPF_PIXELVAL); |
|
/*gr_background = save;*/ |
|
free (bitmap); |
free (Raster.bitmap); |
} |
|
/* |
* Draw unicode 16 text string using FREETYPE type font |
*/ |
static void |
freetype_drawtext(PMWFONT pfont, PSD psd, MWCOORD ax, MWCOORD ay, |
const void *text, int cc, int flags) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
const unsigned short * str = text; |
TT_F26Dot6 x = ax, y = ay; |
TT_Pos vec_x, vec_y; |
int i; |
TT_F26Dot6 startx, starty; |
TT_Outline outline; |
TT_UShort curchar; |
TT_Glyph_Metrics metrics; |
TT_Face_Properties properties; |
TT_Instance_Metrics imetrics; |
TT_F26Dot6 ascent, descent; |
static unsigned char blend[5] = { 0x00, 0x44, 0x88, 0xcc, 0xff }; |
static unsigned char virtual_palette[5] = { 0, 1, 2, 3, 4 }; |
|
pf->last_glyph_code = -1; /* reset kerning*/ |
pf->last_pen_pos = -32767; |
|
/* |
* Compute instance ascent & descent values |
* in fractional units (1/64th pixel) |
*/ |
TT_Get_Face_Properties (pf->face, &properties); |
TT_Get_Instance_Metrics(pf->instance, &imetrics); |
|
ascent = ((properties.horizontal->Ascender * imetrics.y_scale)/0x10000); |
descent = ((properties.horizontal->Descender*imetrics.y_scale)/0x10000); |
|
/* |
* Offset the starting point if necessary, |
* FreeType always aligns at baseline |
*/ |
if (flags&MWTF_BOTTOM) { |
vec_x = 0; |
vec_y = descent; |
TT_Transform_Vector(&vec_x, &vec_y,&pf->matrix); |
x -= vec_x / 64; |
y += vec_y / 64; |
} else if (flags&MWTF_TOP) { |
vec_x = 0; |
vec_y = ascent; |
TT_Transform_Vector(&vec_x, &vec_y,&pf->matrix); |
x -= vec_x / 64; |
y += vec_y / 64; |
} |
|
/* Set the "graylevels" */ |
if (pf->fontattr&MWTF_ANTIALIAS) { |
TT_Set_Raster_Gray_Palette (engine, virtual_palette); |
|
alphablend(psd, gray_palette, gr_foreground, gr_background, |
blend, 5); |
} |
|
startx = x; |
starty = y; |
for (i = 0; i < cc; i++) { |
curchar = TT_Char_Index (pf->char_map, str[i]); |
|
if (TT_Load_Glyph (pf->instance, pf->glyph, curchar, |
TTLOAD_DEFAULT) != TT_Err_Ok) |
continue; |
|
if (pf->fontrotation) { |
TT_Get_Glyph_Outline (pf->glyph, &outline); |
TT_Transform_Outline (&outline, &pf->matrix); |
} |
|
TT_Get_Glyph_Metrics (pf->glyph, &metrics); |
|
if ((pf->fontattr&MWTF_KERNING) && pf->can_kern) { |
if (pf->fontrotation) { |
vec_x = compute_kernval(pf, curchar); |
vec_y = 0; |
TT_Transform_Vector(&vec_x, &vec_y,&pf->matrix); |
|
x += vec_x / 64; |
y -= vec_y / 64; |
} else |
x += compute_kernval(pf, curchar) / 64; |
} |
|
drawchar(pf, psd, pf->glyph, x, y); |
|
if (pf->fontrotation) { |
vec_x = metrics.advance; |
vec_y = 0; |
TT_Transform_Vector (&vec_x, &vec_y, &pf->matrix); |
|
x += vec_x / 64; |
y -= vec_y / 64; |
} else { |
x += metrics.advance / 64; |
|
/* Kerning point syndrome avoidance */ |
if (pf->last_pen_pos > x) |
x = pf->last_pen_pos; |
pf->last_pen_pos = x; |
} |
|
pf->last_glyph_code = curchar; |
} |
|
if (pf->fontattr & MWTF_UNDERLINE) |
GdLine(psd, startx, starty, x, y, FALSE); |
} |
|
/* |
* Return information about a specified font. |
*/ |
static MWBOOL |
freetype_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo) |
{ |
int i; |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
TT_Face_Properties properties; |
TT_Instance_Metrics imetrics; |
TT_UShort last_glyph_index; |
|
TT_Get_Face_Properties (pf->face, &properties); |
TT_Get_Instance_Metrics(pf->instance, &imetrics); |
|
/* Fill up the fields */ |
pfontinfo->height = (((properties.horizontal->Ascender * \ |
imetrics.y_scale)/ 0x10000) >> 6) - |
(((properties.horizontal->Descender * \ |
imetrics.y_scale)/ 0x10000) >> 6); |
pfontinfo->maxwidth = ((properties.horizontal->xMax_Extent * \ |
imetrics.x_scale)/ 0x10000) >> 6; |
pfontinfo->baseline = ((properties.horizontal->Ascender * \ |
imetrics.y_scale)/ 0x10000) >> 6; |
pfontinfo->firstchar = TT_CharMap_First(pf->char_map, NULL); |
pfontinfo->lastchar = TT_CharMap_Last(pf->char_map, NULL); |
pfontinfo->fixed = properties.postscript->isFixedPitch; |
|
last_glyph_index = properties.num_Glyphs > 255 ? 255: properties.num_Glyphs-1; |
|
/* Doesn't work ... don't know why ....*/ |
#if 0 |
if (TT_Get_Face_Widths( pf->face, 0, |
last_glyph_index, widths, NULL ) != TT_Err_Ok) { |
return TRUE; |
} |
|
for(i=0; i<=last_glyph_index; i++) |
DPRINTF("widths[%d]: %d\n", i, widths[i]); |
#endif |
|
/* Get glyphs widths */ |
for(i=0; i<=last_glyph_index; i++) |
pfontinfo->widths[i] = Get_Glyph_Width(pf, i); |
|
#if 0 |
DPRINTF("x_ppem: %d\ny_ppem: %d\nx_scale: %d\ny_scale: %d\n\ |
x_resolution: %d\ny_resolution: %d\n", |
imetrics.x_ppem, imetrics.y_ppem, imetrics.x_scale, \ |
imetrics.y_scale, imetrics.x_resolution, imetrics.y_resolution); |
|
DPRINTF("Ascender: %d\nDescender: %d\nxMax_Extent: %d\n\ |
Mac Style Say Italic?: %d\nMac Style Say Bold?: %d\n\ |
sTypoAscender: %d\nsTypoDescender: %d\nusWinAscent: %d\n\ |
usWinDescent: %d\nusFirstCharIndex: %d\nusLastCharIndex: %d\n\ |
OS2 Say Italic?: %d\nOS2 Say Bold?: %d\nOS2 Say monospaced?: %d\n\ |
Postscript Say monospaced?: %d\n",\ |
properties.horizontal->Ascender, |
properties.horizontal->Descender, |
properties.horizontal->xMax_Extent, |
(properties.header->Mac_Style & 0x2)?1:0, |
(properties.header->Mac_Style & 0x1)?1:0, |
properties.os2->sTypoAscender, |
properties.os2->sTypoDescender, |
properties.os2->usWinAscent, |
properties.os2->usWinDescent, |
properties.os2->usFirstCharIndex, |
properties.os2->usLastCharIndex, |
(properties.os2->fsSelection & 0x1)?1:0, |
(properties.os2->fsSelection & 0x20)?1:0, |
properties.postscript->isFixedPitch, |
(properties.os2->panose[3] == 9)?1:0); |
#endif |
|
return TRUE; |
} |
|
static void |
freetype_gettextsize(PMWFONT pfont, const void *text, int cc, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
const unsigned short * str = text; |
TT_F26Dot6 x = 0; |
int i; |
TT_UShort curchar; |
TT_Glyph_Metrics metrics; |
TT_Face_Properties properties; |
TT_Instance_Metrics imetrics; |
|
TT_Get_Face_Properties (pf->face, &properties); |
TT_Get_Instance_Metrics(pf->instance, &imetrics); |
|
pf->last_glyph_code = -1; /* reset kerning*/ |
pf->last_pen_pos = -32767; |
|
for (i = 0; i < cc; i++) { |
curchar = TT_Char_Index (pf->char_map, str[i]); |
|
if (TT_Load_Glyph (pf->instance, pf->glyph, curchar, |
TTLOAD_SCALE_GLYPH|TTLOAD_HINT_GLYPH) != TT_Err_Ok) |
continue; |
|
TT_Get_Glyph_Metrics (pf->glyph, &metrics); |
|
if ((pf->fontattr&MWTF_KERNING) && pf->can_kern) { |
x += compute_kernval(pf, curchar) / 64; |
} |
|
x += metrics.advance / 64; |
|
/* Kerning point syndrome avoidance */ |
if (pf->last_pen_pos > x) |
x = pf->last_pen_pos; |
pf->last_pen_pos = x; |
|
pf->last_glyph_code = curchar; |
} |
|
*pwidth = x; |
*pheight = (((properties.horizontal->Ascender * |
imetrics.y_scale)/ 0x10000) >> 6) - |
(((properties.horizontal->Descender * |
imetrics.y_scale)/ 0x10000) >> 6); |
/* FIXME: is it what's required ?? */ |
*pbase = (((-properties.horizontal->Descender) * |
imetrics.y_scale)/ 0x10000) >> 6; |
} |
|
static void |
freetype_destroyfont(PMWFONT pfont) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
|
TT_Close_Face(pf->face); |
free(pf); |
} |
|
static void |
freetype_setfontsize(PMWFONT pfont, MWCOORD fontsize) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
|
pf->fontsize = fontsize; |
|
/* We want real pixel sizes ... not points ...*/ |
TT_Set_Instance_PixelSizes( pf->instance, pf->fontsize, |
pf->fontsize, pf->fontsize * 64 ); |
#if 0 |
/* set charsize (convert to points for freetype)*/ |
TT_Set_Instance_CharSize (pf->instance, |
((pf->fontsize * 72 + 96/2) / 96) * 64); |
#endif |
} |
|
static void |
freetype_setfontrotation(PMWFONT pfont, int tenthdegrees) |
{ |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
float angle; |
|
pf->fontrotation = tenthdegrees; |
|
/* Build the rotation matrix with the given angle */ |
TT_Set_Instance_Transform_Flags (pf->instance, TRUE, FALSE); |
|
angle = pf->fontrotation * M_PI / 1800; |
pf->matrix.yy = (TT_Fixed) (cos (angle) * (1 << 16)); |
pf->matrix.yx = (TT_Fixed) (sin (angle) * (1 << 16)); |
pf->matrix.xx = pf->matrix.yy; |
pf->matrix.xy = -pf->matrix.yx; |
} |
|
#endif /* HAVE_FREETYPE_SUPPORT*/ |
|
/* UTF-8 to UTF-16 conversion. Surrogates are handeled properly, e.g. |
* a single 4-byte UTF-8 character is encoded into a surrogate pair. |
* On the other hand, if the UTF-8 string contains surrogate values, this |
* is considered an error and returned as such. |
* |
* The destination array must be able to hold as many Unicode-16 characters |
* as there are ASCII characters in the UTF-8 string. This in case all UTF-8 |
* characters are ASCII characters. No more will be needed. |
* |
* Copyright (c) 2000 Morten Rolland, Screen Media |
*/ |
static int |
utf8_to_utf16(const unsigned char *utf8, int cc, unsigned short *unicode16) |
{ |
int count = 0; |
unsigned char c0, c1; |
unsigned long scalar; |
|
while(--cc >= 0) { |
c0 = *utf8++; |
/*DPRINTF("Trying: %02x\n",c0);*/ |
|
if ( c0 < 0x80 ) { |
/* Plain ASCII character, simple translation :-) */ |
*unicode16++ = c0; |
count++; |
continue; |
} |
|
if ( (c0 & 0xc0) == 0x80 ) |
/* Illegal; starts with 10xxxxxx */ |
return -1; |
|
/* c0 must be 11xxxxxx if we get here => at least 2 bytes */ |
scalar = c0; |
if(--cc < 0) |
return -1; |
c1 = *utf8++; |
/*DPRINTF("c1=%02x\n",c1);*/ |
if ( (c1 & 0xc0) != 0x80 ) |
/* Bad byte */ |
return -1; |
scalar <<= 6; |
scalar |= (c1 & 0x3f); |
|
if ( !(c0 & 0x20) ) { |
/* Two bytes UTF-8 */ |
if ( scalar < 0x80 ) |
return -1; /* Overlong encoding */ |
*unicode16++ = scalar & 0x7ff; |
count++; |
continue; |
} |
|
/* c0 must be 111xxxxx if we get here => at least 3 bytes */ |
if(--cc < 0) |
return -1; |
c1 = *utf8++; |
/*DPRINTF("c1=%02x\n",c1);*/ |
if ( (c1 & 0xc0) != 0x80 ) |
/* Bad byte */ |
return -1; |
scalar <<= 6; |
scalar |= (c1 & 0x3f); |
|
if ( !(c0 & 0x10) ) { |
/*DPRINTF("####\n");*/ |
/* Three bytes UTF-8 */ |
if ( scalar < 0x800 ) |
return -1; /* Overlong encoding */ |
if ( scalar >= 0xd800 && scalar < 0xe000 ) |
return -1; /* UTF-16 high/low halfs */ |
*unicode16++ = scalar & 0xffff; |
count++; |
continue; |
} |
|
/* c0 must be 1111xxxx if we get here => at least 4 bytes */ |
c1 = *utf8++; |
if(--cc < 0) |
return -1; |
/*DPRINTF("c1=%02x\n",c1);*/ |
if ( (c1 & 0xc0) != 0x80 ) |
/* Bad byte */ |
return -1; |
scalar <<= 6; |
scalar |= (c1 & 0x3f); |
|
if ( !(c0 & 0x08) ) { |
/* Four bytes UTF-8, needs encoding as surrogates */ |
if ( scalar < 0x10000 ) |
return -1; /* Overlong encoding */ |
scalar -= 0x10000; |
*unicode16++ = ((scalar >> 10) & 0x3ff) + 0xd800; |
*unicode16++ = (scalar & 0x3ff) + 0xdc00; |
count += 2; |
continue; |
} |
|
return -1; /* No support for more than four byte UTF-8 */ |
} |
return count; |
} |
|
#if HAVE_HZK_SUPPORT |
|
/* UniCode-16 (MWTF_UC16) to GB(MWTF_ASCII) Chinese Characters conversion. |
* a single 2-byte UC16 character is encoded into a surrogate pair. |
* return -1 ,if error; |
* The destination array must be able to hold as many |
* as there are Unicode-16 characters. |
* |
* Copyright (c) 2000 Tang Hao (TownHall)(tang_hao@263.net). |
*/ |
static int |
UC16_to_GB(const unsigned char *uc16, int cc, unsigned char *ascii) |
{ |
FILE* fp; |
char buffer[256]; |
unsigned char *uc16p; |
int i=0,j=0, k; |
unsigned char *filebuffer; |
unsigned short *uc16pp,*table; |
unsigned short uc16px; |
int length=31504; |
|
if (use_big5) |
length=54840; |
|
uc16p=(unsigned char *) uc16; |
uc16pp=(unsigned short *) uc16; |
|
strcpy(buffer,HZK_FONT_DIR); |
if (use_big5) |
strcat(buffer,"/BG2UBG.KU"); |
else |
strcat(buffer,"/UGB2GB.KU"); |
if(!(fp = fopen(buffer, "rb"))) |
{ |
fprintf (stderr, "Error.\nThe %s file can not be found!\n",buffer); |
return -1; |
} |
|
filebuffer= (unsigned char *)malloc ( length); |
|
if(fread(filebuffer, sizeof(char),length, fp) < length) { |
fprintf (stderr, "Error in reading ugb2gb.ku file!\n"); |
fclose(fp); |
return -1; |
} |
fclose(fp); |
|
if (use_big5) |
{ |
table=(unsigned short *)filebuffer; |
while(1) |
{ |
if(j>=cc) |
{ |
ascii[i]=0; |
break; |
} |
uc16px=*uc16pp; |
if((uc16px)<=0x00ff) |
{ |
ascii[i]=(char)(*uc16pp); |
i++; |
} |
else |
{ |
ascii[i]=0xa1; ascii[i+1]=0x40; |
for (k=0; k<13710; k++) |
{ |
if (*(table+(k*2+1))==(uc16px)) |
{ |
ascii[i]=(char)((*(table+(k*2)) & 0xff00) >> 8); |
ascii[i+1]=(char)(*(table+(k*2)) & 0x00ff); |
break; |
} |
} |
i+=2; |
} |
uc16pp++; j++; |
} |
} |
else |
{ |
while(1) |
{ |
if(j>=cc) |
{ |
ascii[i]=0; |
break; |
} |
if((*((uc16p)+j)==0)&&(*((uc16p)+j+1)==0)) |
{ |
ascii[i]=0; |
break; |
} |
else |
{ |
if(*((uc16p)+j+1)==0) |
{ |
ascii[i]=*((uc16p)+j); |
i++; |
j+=2; |
} |
else |
{ |
|
{ |
int p1=0,p2=length-4,p; |
unsigned int c1,c2,c,d; |
c1=((unsigned int )filebuffer[p1])*0x100+(filebuffer[p1+1]); |
c2=((unsigned int )filebuffer[p2])*0x100+(filebuffer[p2+1]); |
d=((unsigned int )*((uc16p)+j))*0x100+*((uc16p)+j+1); |
if(c1==d) |
{ |
ascii[i]=filebuffer[p1+2]; |
ascii[i+1]=filebuffer[p1+3]; |
goto findit; |
} |
if(c2==d) |
{ |
ascii[i]=filebuffer[p2+2]; |
ascii[i+1]=filebuffer[p2+3]; |
goto findit; |
} |
while(1) |
{ |
p=(((p2-p1)/2+p1)>>2)<<2; |
c=((unsigned int )filebuffer[p])*0x100+(filebuffer[p+1]); |
if(d==c) /* find it*/ |
{ |
ascii[i]=filebuffer[p+2]; |
ascii[i+1]=filebuffer[p+3]; |
break; |
} |
else if(p2<=p1+4) /* can't find.*/ |
{ |
ascii[i]='.'; /* ((uc16p)+j);*/ |
ascii[i+1]='.'; /* ((uc16p)+j+1);*/ |
break; |
} |
else if(d<c) |
{ |
p2=p; |
c2=c; |
} |
else |
{ |
p1=p; |
c1=c; |
} |
} |
} |
findit: |
i+=2; |
j+=2; |
} |
} |
} |
} |
free(filebuffer); |
|
return i; |
} |
|
/************************** functions definition ******************************/ |
|
static int hzk_id( PMWHZKFONT pf ) |
{ |
switch(pf->font_height) |
{ |
case 12: |
return 0; |
case 16: default: |
return 1; |
} |
} |
|
/* This function get Chinese font info from etc file.*/ |
static MWBOOL GetCFontInfo( PMWHZKFONT pf ) |
{ |
int charset; |
|
if (use_big5) |
charset=(13094+408); |
else |
charset=8178; |
|
CFont[hzk_id(pf)].width = pf->cfont_width; |
pf->CFont.width = pf->cfont_width; |
|
CFont[hzk_id(pf)].height = pf->font_height; |
pf->CFont.height = pf->font_height; |
|
CFont[hzk_id(pf)].size = ((pf->CFont.width + 7) / 8) * |
pf->CFont.height * charset; |
pf->CFont.size = ((pf->CFont.width + 7) / 8) * pf->CFont.height * charset; |
|
if(pf->CFont.size < charset * 8) |
return FALSE; |
|
strcpy(CFont[hzk_id(pf)].file,HZK_FONT_DIR); |
strcpy(pf->CFont.file,HZK_FONT_DIR); |
|
if(pf->font_height==16) |
{ |
strcat(CFont[hzk_id(pf)].file,"/hzk16"); |
strcat(pf->CFont.file,"/hzk16"); |
} |
else |
{ |
strcat(CFont[hzk_id(pf)].file,"/hzk12"); |
strcat(pf->CFont.file,"/hzk12"); |
} |
|
if (use_big5) |
{ |
CFont[hzk_id(pf)].file[strlen(pf->CFont.file)-3]+=use_big5; |
pf->CFont.file[strlen(pf->CFont.file)-3]+=use_big5; |
} |
|
return TRUE; |
} |
|
/* This function get ASCII font info from etc file.*/ |
static MWBOOL GetAFontInfo( PMWHZKFONT pf ) |
{ |
AFont[hzk_id(pf)].width = pf->afont_width; |
pf->AFont.width = pf->afont_width; |
|
AFont[hzk_id(pf)].height = pf->font_height; |
pf->AFont.height = pf->font_height; |
|
AFont[hzk_id(pf)].size = ((pf->AFont.width + 7) / 8) * |
pf->AFont.height * 255; |
pf->AFont.size = ((pf->AFont.width + 7) / 8) * pf->AFont.height * 255; |
|
if(pf->AFont.size < 255 * 8) |
return FALSE; |
|
strcpy(AFont[hzk_id(pf)].file,HZK_FONT_DIR); |
strcpy(pf->AFont.file,HZK_FONT_DIR); |
|
if(pf->font_height==16) |
{ |
strcat(AFont[hzk_id(pf)].file,"/asc16"); |
strcat(pf->AFont.file,"/asc16"); |
} |
else |
{ |
strcat(AFont[hzk_id(pf)].file,"/asc12"); |
strcat(pf->AFont.file,"/asc12"); |
} |
return TRUE; |
} |
|
/* This function load system font into memory.*/ |
static MWBOOL LoadFont( PMWHZKFONT pf ) |
{ |
FILE* fp; |
|
if(!GetCFontInfo(pf)) |
{ |
fprintf (stderr, "Get Chinese HZK font info failure!\n"); |
return FALSE; |
} |
if(CFont[hzk_id(pf)].pFont == NULL) /* check font cache*/ |
{ |
|
|
/* Allocate system memory for Chinese font.*/ |
if( !(CFont[hzk_id(pf)].pFont = (char *)malloc(pf->CFont.size)) ) |
{ |
fprintf (stderr, "Allocate memory for Chinese HZK font failure.\n"); |
return FALSE; |
} |
|
/* Open font file and read information to the system memory.*/ |
fprintf (stderr, "Loading Chinese HZK font from file(%s)..." ,pf->CFont.file); |
if(!(fp = fopen(CFont[hzk_id(pf)].file, "rb"))) |
{ |
fprintf (stderr, "Error.\nThe Chinese HZK font file can not be found!\n"); |
return FALSE; |
} |
if(fread(CFont[hzk_id(pf)].pFont, sizeof(char), pf->CFont.size, fp) < pf->CFont.size) |
{ |
fprintf (stderr, "Error in reading Chinese HZK font file!\n"); |
fclose(fp); |
return FALSE; |
} |
|
fclose(fp); |
|
CFont[hzk_id(pf)].use_count=0; |
|
fprintf (stderr, "done.\n" ); |
|
} |
cfont_address = CFont[hzk_id(pf)].pFont; |
pf->cfont_address = CFont[hzk_id(pf)].pFont; |
pf->CFont.pFont = CFont[hzk_id(pf)].pFont; |
|
CFont[hzk_id(pf)].use_count++; |
|
if(!GetAFontInfo(pf)) |
{ |
fprintf (stderr, "Get ASCII HZK font info failure!\n"); |
return FALSE; |
} |
if(AFont[hzk_id(pf)].pFont == NULL) /* check font cache*/ |
{ |
|
|
/* Allocate system memory for ASCII font.*/ |
if( !(AFont[hzk_id(pf)].pFont = (char *)malloc(pf->AFont.size)) ) |
{ |
fprintf (stderr, "Allocate memory for ASCII HZK font failure.\n"); |
free(CFont[hzk_id(pf)].pFont); |
CFont[hzk_id(pf)].pFont = NULL; |
return FALSE; |
} |
|
/* Load ASCII font information to the near memory.*/ |
fprintf (stderr, "Loading ASCII HZK font..." ); |
if(!(fp = fopen(AFont[hzk_id(pf)].file, "rb"))) |
{ |
fprintf (stderr, "Error.\nThe ASCII HZK font file can not be found!\n"); |
return FALSE; |
} |
if(fread(AFont[hzk_id(pf)].pFont, sizeof(char), pf->AFont.size, fp) < pf->AFont.size) |
{ |
fprintf (stderr, "Error in reading ASCII HZK font file!\n"); |
fclose(fp); |
return FALSE; |
} |
|
fclose(fp); |
|
AFont[hzk_id(pf)].use_count=0; |
|
fprintf (stderr, "done.\n" ); |
|
} |
afont_address = AFont[hzk_id(pf)].pFont; |
pf->afont_address = AFont[hzk_id(pf)].pFont; |
pf->AFont.pFont = AFont[hzk_id(pf)].pFont; |
|
AFont[hzk_id(pf)].use_count++; |
|
return TRUE; |
} |
|
/* This function unload system font from memory.*/ |
static void UnloadFont( PMWHZKFONT pf ) |
{ |
CFont[hzk_id(pf)].use_count--; |
AFont[hzk_id(pf)].use_count--; |
|
if (!CFont[hzk_id(pf)].use_count) |
{ |
free(pf->CFont.pFont); |
free(pf->AFont.pFont); |
|
CFont[hzk_id(pf)].pFont = NULL; |
AFont[hzk_id(pf)].pFont = NULL; |
} |
} |
|
static int |
hzk_init(PSD psd) |
{ |
/* FIXME: *.KU file should be opened and |
* read in here...*/ |
return 1; |
} |
|
static PMWHZKFONT |
hzk_createfont(const char *name, MWCOORD height, int attr) |
{ |
PMWHZKFONT pf; |
|
if(strcmp(name,"HZKFONT")!=0 && strcmp(name,"HZXFONT")!=0) |
return FALSE; |
|
/*printf("hzk_createfont(%s,%d)\n",name,height);*/ |
|
use_big5=name[2]-'K'; |
|
/* allocate font structure*/ |
pf = (PMWHZKFONT)calloc(sizeof(MWHZKFONT), 1); |
if (!pf) |
return NULL; |
pf->fontprocs = &hzk_procs; |
|
pf->fontsize=height; |
#if 0 |
GdSetFontSize((PMWFONT)pf, height); |
#endif |
GdSetFontRotation((PMWFONT)pf, 0); |
GdSetFontAttr((PMWFONT)pf, attr, 0); |
|
if(height==12) |
{ |
afont_width = 6; |
cfont_width = 12; |
font_height = 12; |
|
pf->afont_width = 6; |
pf->cfont_width = 12; |
pf->font_height = 12; |
} |
else |
{ |
afont_width = 8; |
cfont_width = 16; |
font_height = 16; |
|
pf->afont_width = 8; |
pf->cfont_width = 16; |
pf->font_height = 16; |
} |
|
/* Load the font library to the system memory.*/ |
if(!LoadFont(pf)) |
return FALSE; |
|
return pf; |
} |
|
int IsBig5(int i) |
{ |
if ((i>=0xa140 && i<=0xa3bf) || /* a140-a3bf(!a3e0) */ |
(i>=0xa440 && i<=0xc67e) || /* a440-c67e */ |
(i>=0xc6a1 && i<=0xc8d3) || /* c6a1-c8d3(!c8fe) */ |
(i>=0xc940 && i<=0xf9fe)) /* c940-f9fe */ |
return 1; |
else |
return 0; |
} |
|
/* |
* following several function is used in hzk_drawtext |
*/ |
|
static int getnextchar(char* s, unsigned char* cc) |
{ |
if( s[0] == '\0') return 0; |
|
cc[0] = (unsigned char)(*s); |
cc[1] = (unsigned char)(*(s + 1)); |
|
if (use_big5) |
{ |
if( IsBig5( (int) ( (cc[0] << 8) + cc[1]) ) ) |
return 1; |
} |
else |
{ |
if( ((unsigned char)cc[0] > 0xa0) && |
((unsigned char)cc[1] > 0xa0) ) |
return 1; |
} |
|
cc[1] = '\0'; |
|
return 1; |
} |
|
static void |
expandcchar(PMWHZKFONT pf, int bg, int fg, unsigned char* c, MWPIXELVAL* bitmap) |
{ |
int i=0; |
int c1, c2, seq; |
int x,y; |
unsigned char *font; |
int b = 0; /* keep gcc happy with b = 0 - MW */ |
|
int pixelsize; |
pixelsize=sizeof(MWPIXELVAL); |
|
c1 = c[0]; |
c2 = c[1]; |
if (use_big5) |
{ |
seq=0; |
/* ladd=loby-(if(loby<127)?64:98)*/ |
c2-=(c2<127?64:98); |
|
/* hadd=(hiby-164)*157*/ |
if (c1>=0xa4) /* standard font*/ |
{ |
seq=(((c1-164)*157)+c2); |
if (seq>=5809) seq-=408; |
} |
|
/* hadd=(hiby-161)*157*/ |
if (c1<=0xa3) /* special font*/ |
seq=(((c1-161)*157)+c2)+13094; |
} |
else |
seq=((c1 - 161)*94 + c2 - 161); |
|
font = pf->cfont_address + ((seq) * |
(pf->font_height * ((pf->cfont_width + 7) / 8))); |
|
for (y = 0; y < pf->font_height; y++) |
for (x = 0; x < pf->cfont_width; x++) |
{ |
if (x % 8 == 0) |
b = *font++; |
|
if (b & (128 >> (x % 8))) /* pixel */ |
bitmap[i++]=fg; |
else |
bitmap[i++]=bg; |
} |
} |
|
static void expandchar(PMWHZKFONT pf, int bg, int fg, int c, MWPIXELVAL* bitmap) |
{ |
int i=0; |
int x,y; |
unsigned char *font; |
int pixelsize; |
int b = 0; /* keep gcc happy with b = 0 - MW */ |
|
pixelsize=sizeof(MWPIXELVAL); |
|
font = pf->afont_address + c * (pf->font_height * |
((pf->afont_width + 7) / 8)); |
|
for (y = 0; y < pf->font_height; y++) |
for (x = 0; x < pf->afont_width; x++) |
{ |
if (x % 8 == 0) |
b = *font++; |
if (b & (128 >> (x % 8))) /* pixel */ |
bitmap[i++]=fg; |
else |
bitmap[i++]=bg; |
} |
} |
|
/* |
* Draw ASCII text string using HZK type font |
*/ |
static void |
hzk_drawtext(PMWFONT pfont, PSD psd, MWCOORD ax, MWCOORD ay, |
const void *text, int cc, int flags) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
|
unsigned char c[2]; |
MWPIXELVAL *bitmap; |
unsigned char s1[3]; |
char *s,*sbegin; |
|
s=(char *)text; |
|
if(cc==1) |
{ |
s1[0]=*((unsigned char*)text); |
s1[1]=0x0; |
s1[2]=0x0; |
s=s1; |
} |
|
sbegin=s; |
bitmap = (MWPIXELVAL *)ALLOCA(pf->cfont_width * pf->font_height * |
sizeof(MWPIXELVAL)); |
|
while( getnextchar(s, c) ) |
{ |
if( c[1] != '\0') |
{ |
expandcchar(pf, gr_background,gr_foreground, |
c, bitmap); |
/* Now draw the bitmap ... */ |
|
if (flags&MWTF_TOP) |
GdArea(psd,ax, ay, pf->cfont_width, |
pf->font_height, bitmap, MWPF_PIXELVAL); |
else |
GdArea(psd,ax, ay-pf->font_height+2, |
pf->cfont_width, pf->font_height, |
bitmap, MWPF_PIXELVAL); |
|
s += 2; |
ax += pf->cfont_width; |
} |
else |
{ |
expandchar(pf, gr_background,gr_foreground, |
c[0], bitmap); |
/* Now draw the bitmap ... */ |
|
if (flags&MWTF_TOP) |
GdArea(psd,ax, ay, pf->afont_width, |
pf->font_height, bitmap, MWPF_PIXELVAL); |
else |
GdArea(psd,ax, ay-pf->font_height+2, |
pf->afont_width, pf->font_height, |
bitmap, MWPF_PIXELVAL); |
|
s += 1; |
ax += pf->afont_width; |
} |
|
if(s>=sbegin+cc)break; |
} |
|
FREEA(bitmap); |
} |
|
/* |
* Return information about a specified font. |
*/ |
static MWBOOL |
hzk_getfontinfo(PMWFONT pfont, PMWFONTINFO pfontinfo) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
|
int i; |
|
pfontinfo->height = pf->font_height; |
pfontinfo->maxwidth = pf->cfont_width; |
pfontinfo->baseline = pf->font_height-2; |
pfontinfo->firstchar = 0; |
pfontinfo->lastchar = 0; |
pfontinfo->fixed = TRUE; |
|
for(i=0; i<=256; i++) |
pfontinfo->widths[i] = pf->afont_width; |
|
return TRUE; |
} |
|
static void |
hzk_gettextsize(PMWFONT pfont, const void *text, int cc, |
MWCOORD *pwidth, MWCOORD *pheight, MWCOORD *pbase) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
|
unsigned char c[2]; |
char *s,*sbegin; |
unsigned char s1[3]; |
|
int ax=0; |
s=(char *)text; |
if(cc==0) |
{ |
*pwidth = 0; |
*pheight = pf->font_height; |
*pbase = pf->font_height-2; |
|
} |
if(cc==1) |
{ |
s1[0]=*((unsigned char*)text); |
s1[1]=0x0; |
s1[2]=0x0; |
s=s1; |
} |
sbegin=s; |
while( getnextchar(s, c) ) |
{ |
if( c[1] != '\0') |
{ |
s += 2; |
ax += pf->cfont_width; |
} |
else |
{ |
s += 1; |
ax += pf->afont_width; |
} |
if(s>=sbegin+cc) { |
/*fprintf(stderr,"s=%x,sbegin=%x,cc=%x\n",s,sbegin,cc);*/ |
break; |
} |
|
} |
/*fprintf(stderr,"ax=%d,\n",ax);*/ |
|
*pwidth = ax; |
*pheight = pf->font_height; |
*pbase = pf->font_height-2; |
} |
|
static void |
hzk_destroyfont(PMWFONT pfont) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
UnloadFont(pf); |
free(pf); |
} |
|
static void |
hzk_setfontsize(PMWFONT pfont, MWCOORD fontsize) |
{ |
PMWHZKFONT pf=(PMWHZKFONT)pfont; |
/* jmt: hzk_setfontsize not supported*/ |
/* & pf->fontsize can't be changed*/ |
/* because of hzk_id() :p*/ |
pf->fontsize=pf->font_height; |
} |
#endif /* HAVE_HZK_SUPPORT*/ |
|
/* FIXME: this routine should work for all font renderers...*/ |
int |
GdGetTextSizeEx(PMWFONT pfont, const void *str, int cc,int nMaxExtent, |
int* lpnFit, int* alpDx,MWCOORD *pwidth,MWCOORD *pheight, |
MWCOORD *pbase, int flags) |
{ |
#ifdef HAVE_FREETYPE_SUPPORT |
unsigned short buf[256]; |
unsigned short* text; |
PMWFREETYPEFONT pf = (PMWFREETYPEFONT)pfont; |
int defencoding = pf->fontprocs->encoding; |
int x = 0; |
int i; |
TT_UShort curchar; |
TT_Glyph_Metrics metrics; |
TT_Face_Properties properties; |
TT_Instance_Metrics imetrics; |
|
if ((cc<0)||(!str)) |
{ |
*pwidth = *pheight = *pbase = 0; |
return 0; |
} |
/* convert encoding if required*/ |
if((flags & MWTF_PACKMASK) != defencoding) |
{ |
cc = GdConvertEncoding(str, flags, cc, buf, defencoding); |
flags &= ~MWTF_PACKMASK; |
flags |= defencoding; |
text=buf; |
} else text =(unsigned short*)str; |
if(cc <= 0) |
{ |
*pwidth = *pheight = *pbase = 0; |
return 0; |
} |
|
TT_Get_Face_Properties (pf->face, &properties); |
TT_Get_Instance_Metrics(pf->instance, &imetrics); |
|
pf->last_glyph_code = -1; /* reset kerning*/ |
pf->last_pen_pos = -32767; |
if (lpnFit) |
*lpnFit=-1; |
for (i = 0; i < cc; i++) |
{ |
curchar = TT_Char_Index (pf->char_map,text[i]); |
|
if (TT_Load_Glyph (pf->instance, pf->glyph, curchar, |
TTLOAD_SCALE_GLYPH|TTLOAD_HINT_GLYPH) != TT_Err_Ok) |
{ |
fprintf(stderr, "Unable to load glyph with index=%d\n",curchar); |
return 0; |
} |
TT_Get_Glyph_Metrics (pf->glyph, &metrics); |
if ((pf->fontattr&MWTF_KERNING) && pf->can_kern) |
{ |
x += compute_kernval(pf, curchar) / 64; |
} |
x += metrics.advance / 64; |
if((lpnFit)&&(alpDx)) |
{ |
if (x<=nMaxExtent) |
alpDx[i]=x; |
else |
if (*lpnFit==-1) |
(*lpnFit)=i; |
} |
/* Kerning point syndrome avoidance */ |
if (pf->last_pen_pos > x) |
x = pf->last_pen_pos; |
pf->last_pen_pos = x; |
pf->last_glyph_code = curchar; |
} |
if ((lpnFit)&&(*lpnFit==-1)) |
*lpnFit=cc; |
*pwidth = x; |
*pheight = (((properties.horizontal->Ascender * |
imetrics.y_scale)/ 0x10000) >> 6) - |
(((properties.horizontal->Descender * |
imetrics.y_scale)/ 0x10000) >> 6); |
/* FIXME: is it what's required ??*/ |
if (pbase) |
*pbase = (((-properties.horizontal->Descender) * |
imetrics.y_scale)/ 0x10000) >> 6; |
return 1; |
#else /* HAVE_FREETYPE_SUPPORT*/ |
*pwidth = *pheight = *pbase = 0; |
return 0; |
#endif |
} |
|
#ifdef HAVE_FREETYPE_SUPPORT |
#include <dirent.h> |
/* |
* This function is taken almost verbatim from ftdump.c from |
* the freetype library (version 1.3.1) |
*/ |
static char * |
tt_lookup_name(TT_Face face) |
{ |
TT_Face_Properties prop; |
unsigned short i, n; |
unsigned short platform, encoding, language, id; |
char *string; |
char *name_buffer; |
unsigned short string_len; |
int j, found; |
int index = 4; /* I dont know why as yet.. */ |
int name_len; |
|
|
TT_Get_Face_Properties(face, &prop); |
n = prop.num_Names; |
|
for ( i = 0; i < n; i++ ) { |
TT_Get_Name_ID( face, i, &platform, &encoding, &language, &id ); |
TT_Get_Name_String( face, i, &string, &string_len ); |
|
if (id == index ) { |
/* The following code was inspired from Mark Leisher's */ |
/* ttf2bdf package */ |
found = 0; |
|
/* Try to find a Microsoft English name */ |
if ( platform == 3 ) |
for ( j = 1; j >= 0; j-- ) |
if ( encoding == j ) /* Microsoft ? */ |
if ( (language & 0x3FF) == 0x009 ) { |
/* English language */ |
found = 1; |
break; |
} |
|
if ( !found && platform == 0 && language == 0 ) |
found = 1; |
|
/* Found a Unicode Name. */ |
if ( found ) { |
if ( string_len > 512 ) |
string_len = 512; |
|
name_len = 0; |
name_buffer = (char*)malloc((string_len / 2) + 1); |
|
for ( i = 1; i < string_len; i += 2 ) |
name_buffer[name_len++] = string[i]; |
|
name_buffer[name_len] = '\0'; |
|
return name_buffer; |
} |
} |
} |
|
/* Not found */ |
return NULL; |
} |
|
static char * |
get_tt_name(char *p) |
{ |
TT_Face face; |
char *ret; |
|
/*printf("Trying to open: %s!\n",p);*/ |
|
if (TT_Open_Face(engine, p, &face) != TT_Err_Ok) { |
fprintf(stderr, "Error opening font: %s\n", p); |
return NULL; |
} |
|
ret = tt_lookup_name(face); |
|
TT_Close_Face(face); |
|
return ret; |
} |
|
void |
GdFreeFontList(MWFONTLIST ***fonts, int n) |
{ |
int i; |
MWFONTLIST *g, **list = *fonts; |
|
for (i = 0; i < n; i++) { |
g = list[i]; |
if(g) { |
if(g->mwname) |
free(g->mwname); |
if(g->ttname) |
free(g->ttname); |
free(g); |
} |
} |
free(list); |
*fonts = 0; |
} |
|
void |
GdGetFontList(MWFONTLIST ***fonts, int *numfonts) |
{ |
DIR *dir; |
struct dirent *dent; |
char *p, *ftmp; |
int pl, idx = 0; |
MWFONTLIST **list; |
|
|
if (TT_Err_Ok != TT_Init_FreeType(&engine)) { |
fprintf(stderr, "Unable to initialize freetype\n"); |
*numfonts = -1; |
return ; |
} |
|
dir = opendir(FREETYPE_FONT_DIR); |
|
if (dir <= 0) { |
fprintf(stderr, "Error opening font directory\n"); |
*numfonts = -1; |
return ; |
} |
|
/* get the number of strings we need to allocate */ |
while ((dent = readdir(dir)) != NULL) { |
p = strrchr(dent->d_name, '.'); |
if (strcasecmp(p, ".ttf") == 0) |
idx++; |
} |
|
*numfonts = idx; |
rewinddir(dir); |
|
/* allocate strings */ |
list = (MWFONTLIST**)malloc(idx * sizeof(MWFONTLIST*)); |
for (pl = 0; pl < idx; pl++) |
list[pl] = (MWFONTLIST*)malloc(sizeof(MWFONTLIST)); |
|
*fonts = list; |
|
idx = 0; |
|
while ((dent = readdir(dir)) != NULL) { |
/* check extension */ |
p = strrchr(dent->d_name, '.'); |
|
if (strcasecmp(p, ".ttf") == 0) { |
|
/* get full path */ |
p = 0; |
pl = strlen(FREETYPE_FONT_DIR) + strlen(dent->d_name) * |
sizeof(char) + 2; |
p = (char*)malloc(pl); |
p = (char*)memset(p, '\0', pl); |
p = (char*)strcat(p, FREETYPE_FONT_DIR); |
p = (char*)strcat(p, "/"); |
p = (char*)strcat(p, dent->d_name); |
|
|
if((ftmp = get_tt_name(p)) != NULL) { |
list[idx]->ttname = ftmp; |
list[idx]->mwname = malloc(strlen(dent->d_name) + 1); |
list[idx]->mwname = strcpy(list[idx]->mwname, dent->d_name); |
|
idx++; |
} |
|
free(p); |
} |
} |
|
closedir(dir); |
} |
#else /* !HAVE_FREETYPE_SUPPORT*/ |
|
void |
GdFreeFontList(MWFONTLIST ***fonts, int n) |
{ |
} |
|
void |
GdGetFontList(MWFONTLIST ***fonts, int *numfonts) |
{ |
*numfonts = -1; |
} |
#endif /* !HAVE_FREETYPE_SUPPORT*/ |
/devrgn2.c
0,0 → 1,910
/* |
* Portions Copyright (c) 2000 Greg Haerr <greg@censoft.com> |
* Somewhat less shamelessly ripped from the Wine distribution |
* and the X Window System. |
* |
* Device-independent Microwindows polygon regions, implemented using |
* multiple rectangles. |
* |
* Shamelessly ripped out from the X11 distribution |
* Thanks for the nice licence. |
* |
* Copyright 1993, 1994, 1995 Alexandre Julliard |
* Modifications and additions: Copyright 1998 Huw Davies |
*/ |
|
/************************************************************************ |
|
Copyright (c) 1987, 1988 X Consortium |
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
of this software and associated documentation files (the "Software"), to deal |
in the Software without restriction, including without limitation the rights |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the Software is |
furnished to do so, subject to the following conditions: |
|
The above copyright notice and this permission notice shall be included in |
all copies or substantial portions of the Software. |
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
Except as contained in this notice, the name of the X Consortium shall not be |
used in advertising or otherwise to promote the sale, use or other dealings |
in this Software without prior written authorization from the X Consortium. |
|
|
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. |
|
All Rights Reserved |
|
Permission to use, copy, modify, and distribute this software and its |
documentation for any purpose and without fee is hereby granted, |
provided that the above copyright notice appear in all copies and that |
both that copyright notice and this permission notice appear in |
supporting documentation, and that the name of Digital not be |
used in advertising or publicity pertaining to distribution of the |
software without specific, written prior permission. |
|
DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
SOFTWARE. |
|
************************************************************************/ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include "device.h" |
|
#if POLYREGIONS |
|
/* |
* number of points to buffer before sending them off |
* to scanlines() : Must be an even number |
*/ |
#define NUMPTSTOBUFFER 200 |
|
/* |
* used to allocate buffers for points and link |
* the buffers together |
*/ |
|
typedef struct _POINTBLOCK { |
MWPOINT pts[NUMPTSTOBUFFER]; |
struct _POINTBLOCK *next; |
} POINTBLOCK; |
|
/* |
* This file contains a few macros to help track |
* the edge of a filled object. The object is assumed |
* to be filled in scanline order, and thus the |
* algorithm used is an extension of Bresenham's line |
* drawing algorithm which assumes that y is always the |
* major axis. |
* Since these pieces of code are the same for any filled shape, |
* it is more convenient to gather the library in one |
* place, but since these pieces of code are also in |
* the inner loops of output primitives, procedure call |
* overhead is out of the question. |
* See the author for a derivation if needed. |
*/ |
|
/* |
* In scan converting polygons, we want to choose those pixels |
* which are inside the polygon. Thus, we add .5 to the starting |
* x coordinate for both left and right edges. Now we choose the |
* first pixel which is inside the pgon for the left edge and the |
* first pixel which is outside the pgon for the right edge. |
* Draw the left pixel, but not the right. |
* |
* How to add .5 to the starting x coordinate: |
* If the edge is moving to the right, then subtract dy from the |
* error term from the general form of the algorithm. |
* If the edge is moving to the left, then add dy to the error term. |
* |
* The reason for the difference between edges moving to the left |
* and edges moving to the right is simple: If an edge is moving |
* to the right, then we want the algorithm to flip immediately. |
* If it is moving to the left, then we don't want it to flip until |
* we traverse an entire pixel. |
*/ |
|
#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ |
int dx; /* local storage */ \ |
\ |
/* \ |
* if the edge is horizontal, then it is ignored \ |
* and assumed not to be processed. Otherwise, do this stuff. \ |
*/ \ |
if ((dy) != 0) { \ |
xStart = (x1); \ |
dx = (x2) - xStart; \ |
if (dx < 0) { \ |
m = dx / (dy); \ |
m1 = m - 1; \ |
incr1 = (-2) * (dx) + 2 * (dy) * (m1); \ |
incr2 = (-2) * (dx) + 2 * (dy) * (m); \ |
d = 2 * (m) * (dy) - 2 * (dx) - 2 * (dy); \ |
} else { \ |
m = dx / (dy); \ |
m1 = m + 1; \ |
incr1 = (2 * dx) - 2 * (dy) * m1; \ |
incr2 = (2 * dx) - 2 * (dy) * m; \ |
d = (-2) * m * (dy) + 2 * dx; \ |
} \ |
} \ |
} |
|
#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ |
if (m1 > 0) { \ |
if (d > 0) { \ |
minval += m1; \ |
d += incr1; \ |
} \ |
else { \ |
minval += m; \ |
d += incr2; \ |
} \ |
} else {\ |
if (d >= 0) { \ |
minval += m1; \ |
d += incr1; \ |
} \ |
else { \ |
minval += m; \ |
d += incr2; \ |
} \ |
} \ |
} |
|
|
/* |
* This structure contains all of the information needed |
* to run the bresenham algorithm. |
* The variables may be hardcoded into the declarations |
* instead of using this structure to make use of |
* register declarations. |
*/ |
typedef struct { |
MWCOORD minor_axis; /* minor axis */ |
int d; /* decision variable */ |
int m, m1; /* slope and slope+1 */ |
int incr1, incr2; /* error increments */ |
} BRESINFO; |
|
#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ |
BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \ |
bres.m, bres.m1, bres.incr1, bres.incr2) |
|
#define BRESINCRPGONSTRUCT(bres) \ |
BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2) |
|
|
/* |
* These are the data structures needed to scan |
* convert regions. Two different scan conversion |
* methods are available -- the even-odd method, and |
* the winding number method. |
* The even-odd rule states that a point is inside |
* the polygon if a ray drawn from that point in any |
* direction will pass through an odd number of |
* path segments. |
* By the winding number rule, a point is decided |
* to be inside the polygon if a ray drawn from that |
* point in any direction passes through a different |
* number of clockwise and counter-clockwise path |
* segments. |
* |
* These data structures are adapted somewhat from |
* the algorithm in (Foley/Van Dam) for scan converting |
* polygons. |
* The basic algorithm is to start at the top (smallest y) |
* of the polygon, stepping down to the bottom of |
* the polygon by incrementing the y coordinate. We |
* keep a list of edges which the current scanline crosses, |
* sorted by x. This list is called the Active Edge Table (AET) |
* As we change the y-coordinate, we update each entry in |
* in the active edge table to reflect the edges new xcoord. |
* This list must be sorted at each scanline in case |
* two edges intersect. |
* We also keep a data structure known as the Edge Table (ET), |
* which keeps track of all the edges which the current |
* scanline has not yet reached. The ET is basically a |
* list of ScanLineList structures containing a list of |
* edges which are entered at a given scanline. There is one |
* ScanLineList per scanline at which an edge is entered. |
* When we enter a new edge, we move it from the ET to the AET. |
* |
* From the AET, we can implement the even-odd rule as in |
* (Foley/Van Dam). |
* The winding number rule is a little trickier. We also |
* keep the EdgeTableEntries in the AET linked by the |
* nextWETE (winding EdgeTableEntry) link. This allows |
* the edges to be linked just as before for updating |
* purposes, but only uses the edges linked by the nextWETE |
* link as edges representing spans of the polygon to |
* drawn (as with the even-odd rule). |
*/ |
|
/* |
* for the winding number rule |
*/ |
#define CLOCKWISE 1 |
#define COUNTERCLOCKWISE -1 |
|
typedef struct _EdgeTableEntry { |
MWCOORD ymax; /* ycoord at which we exit this edge. */ |
BRESINFO bres; /* Bresenham info to run the edge */ |
struct _EdgeTableEntry *next; /* next in the list */ |
struct _EdgeTableEntry *back; /* for insertion sort */ |
struct _EdgeTableEntry *nextWETE; /* for winding num rule */ |
int ClockWise; /* flag for winding number rule */ |
} EdgeTableEntry; |
|
|
typedef struct _ScanLineList{ |
int scanline; /* the scanline represented */ |
EdgeTableEntry *edgelist; /* header node */ |
struct _ScanLineList *next; /* next in the list */ |
} ScanLineList; |
|
|
typedef struct { |
MWCOORD ymax; /* ymax for the polygon */ |
MWCOORD ymin; /* ymin for the polygon */ |
ScanLineList scanlines; /* header node */ |
} EdgeTable; |
|
|
/* |
* Here is a struct to help with storage allocation |
* so we can allocate a big chunk at a time, and then take |
* pieces from this heap when we need to. |
*/ |
#define SLLSPERBLOCK 25 |
|
typedef struct _ScanLineListBlock { |
ScanLineList SLLs[SLLSPERBLOCK]; |
struct _ScanLineListBlock *next; |
} ScanLineListBlock; |
|
/* |
* |
* a few macros for the inner loops of the fill code where |
* performance considerations don't allow a procedure call. |
* |
* Evaluate the given edge at the given scanline. |
* If the edge has expired, then we leave it and fix up |
* the active edge table; otherwise, we increment the |
* x value to be ready for the next scanline. |
* The winding number rule is in effect, so we must notify |
* the caller when the edge has been removed so he |
* can reorder the Winding Active Edge Table. |
*/ |
#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ |
if (pAET->ymax == y) { /* leaving this edge */ \ |
pPrevAET->next = pAET->next; \ |
pAET = pPrevAET->next; \ |
fixWAET = 1; \ |
if (pAET) \ |
pAET->back = pPrevAET; \ |
} \ |
else { \ |
BRESINCRPGONSTRUCT(pAET->bres); \ |
pPrevAET = pAET; \ |
pAET = pAET->next; \ |
} \ |
} |
|
|
/* |
* Evaluate the given edge at the given scanline. |
* If the edge has expired, then we leave it and fix up |
* the active edge table; otherwise, we increment the |
* x value to be ready for the next scanline. |
* The even-odd rule is in effect. |
*/ |
#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ |
if (pAET->ymax == y) { /* leaving this edge */ \ |
pPrevAET->next = pAET->next; \ |
pAET = pPrevAET->next; \ |
if (pAET) \ |
pAET->back = pPrevAET; \ |
} \ |
else { \ |
BRESINCRPGONSTRUCT(pAET->bres); \ |
pPrevAET = pAET; \ |
pAET = pAET->next; \ |
} \ |
} |
|
|
|
#define LARGE_COORDINATE 0x7fffffff /* FIXME */ |
#define SMALL_COORDINATE 0x80000000 |
|
/* |
* REGION_InsertEdgeInET |
* |
* Insert the given edge into the edge table. |
* First we must find the correct bucket in the |
* Edge table, then find the right slot in the |
* bucket. Finally, we can insert it. |
* |
*/ |
static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE, |
int scanline, ScanLineListBlock **SLLBlock, int *iSLLBlock) |
|
{ |
EdgeTableEntry *start, *prev; |
ScanLineList *pSLL, *pPrevSLL; |
ScanLineListBlock *tmpSLLBlock; |
|
/* |
* find the right bucket to put the edge into |
*/ |
pPrevSLL = &ET->scanlines; |
pSLL = pPrevSLL->next; |
while (pSLL && (pSLL->scanline < scanline)) |
{ |
pPrevSLL = pSLL; |
pSLL = pSLL->next; |
} |
|
/* |
* reassign pSLL (pointer to ScanLineList) if necessary |
*/ |
if ((!pSLL) || (pSLL->scanline > scanline)) |
{ |
if (*iSLLBlock > SLLSPERBLOCK-1) |
{ |
tmpSLLBlock = malloc( sizeof(ScanLineListBlock)); |
if(!tmpSLLBlock) |
{ |
return; |
} |
(*SLLBlock)->next = tmpSLLBlock; |
tmpSLLBlock->next = (ScanLineListBlock *)NULL; |
*SLLBlock = tmpSLLBlock; |
*iSLLBlock = 0; |
} |
pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); |
|
pSLL->next = pPrevSLL->next; |
pSLL->edgelist = (EdgeTableEntry *)NULL; |
pPrevSLL->next = pSLL; |
} |
pSLL->scanline = scanline; |
|
/* |
* now insert the edge in the right bucket |
*/ |
prev = (EdgeTableEntry *)NULL; |
start = pSLL->edgelist; |
while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) |
{ |
prev = start; |
start = start->next; |
} |
ETE->next = start; |
|
if (prev) |
prev->next = ETE; |
else |
pSLL->edgelist = ETE; |
} |
|
|
/* |
* REGION_CreateEdgeTable |
* |
* This routine creates the edge table for |
* scan converting polygons. |
* The Edge Table (ET) looks like: |
* |
* EdgeTable |
* -------- |
* | ymax | ScanLineLists |
* |scanline|-->------------>-------------->... |
* -------- |scanline| |scanline| |
* |edgelist| |edgelist| |
* --------- --------- |
* | | |
* | | |
* V V |
* list of ETEs list of ETEs |
* |
* where ETE is an EdgeTableEntry data structure, |
* and there is one ScanLineList per scanline at |
* which an edge is initially entered. |
* |
*/ |
static void REGION_CreateETandAET(int *Count, int nbpolygons, |
MWPOINT *pts, EdgeTable *ET, EdgeTableEntry *AET, |
EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock) |
{ |
MWPOINT *top, *bottom; |
MWPOINT *PrevPt, *CurrPt, *EndPt; |
int poly, count; |
int iSLLBlock = 0; |
int dy; |
|
|
/* |
* initialize the Active Edge Table |
*/ |
AET->next = (EdgeTableEntry *)NULL; |
AET->back = (EdgeTableEntry *)NULL; |
AET->nextWETE = (EdgeTableEntry *)NULL; |
AET->bres.minor_axis = SMALL_COORDINATE; |
|
/* |
* initialize the Edge Table. |
*/ |
ET->scanlines.next = (ScanLineList *)NULL; |
ET->ymax = SMALL_COORDINATE; |
ET->ymin = LARGE_COORDINATE; |
pSLLBlock->next = (ScanLineListBlock *)NULL; |
|
EndPt = pts - 1; |
for(poly = 0; poly < nbpolygons; poly++) |
{ |
count = Count[poly]; |
EndPt += count; |
if(count < 2) |
continue; |
|
PrevPt = EndPt; |
|
/* |
* for each vertex in the array of points. |
* In this loop we are dealing with two vertices at |
* a time -- these make up one edge of the polygon. |
*/ |
while (count--) |
{ |
CurrPt = pts++; |
/* |
* find out which point is above and which is below. |
*/ |
if (PrevPt->y > CurrPt->y) |
{ |
bottom = PrevPt, top = CurrPt; |
pETEs->ClockWise = 0; |
} |
else |
{ |
bottom = CurrPt, top = PrevPt; |
pETEs->ClockWise = 1; |
} |
|
/* |
* don't add horizontal edges to the Edge table. |
*/ |
if (bottom->y != top->y) |
{ |
pETEs->ymax = bottom->y-1; |
/* -1 so we don't get last scanline */ |
|
/* |
* initialize integer edge algorithm |
*/ |
dy = bottom->y - top->y; |
|
BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres); |
|
REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, |
&iSLLBlock); |
|
if (PrevPt->y > ET->ymax) |
ET->ymax = PrevPt->y; |
if (PrevPt->y < ET->ymin) |
ET->ymin = PrevPt->y; |
pETEs++; |
} |
|
PrevPt = CurrPt; |
} |
} |
} |
|
/* |
* REGION_loadAET |
* |
* This routine moves EdgeTableEntries from the |
* EdgeTable into the Active Edge Table, |
* leaving them sorted by smaller x coordinate. |
* |
*/ |
static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs) |
{ |
EdgeTableEntry *pPrevAET; |
EdgeTableEntry *tmp; |
|
pPrevAET = AET; |
AET = AET->next; |
while (ETEs) |
{ |
while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis)) |
{ |
pPrevAET = AET; |
AET = AET->next; |
} |
tmp = ETEs->next; |
ETEs->next = AET; |
if (AET) |
AET->back = ETEs; |
ETEs->back = pPrevAET; |
pPrevAET->next = ETEs; |
pPrevAET = ETEs; |
|
ETEs = tmp; |
} |
} |
|
/* |
* REGION_computeWAET |
* |
* This routine links the AET by the |
* nextWETE (winding EdgeTableEntry) link for |
* use by the winding number rule. The final |
* Active Edge Table (AET) might look something |
* like: |
* |
* AET |
* ---------- --------- --------- |
* |ymax | |ymax | |ymax | |
* | ... | |... | |... | |
* |next |->|next |->|next |->... |
* |nextWETE| |nextWETE| |nextWETE| |
* --------- --------- ^-------- |
* | | | |
* V-------------------> V---> ... |
* |
*/ |
static void REGION_computeWAET(EdgeTableEntry *AET) |
{ |
EdgeTableEntry *pWETE; |
int inside = 1; |
int isInside = 0; |
|
AET->nextWETE = (EdgeTableEntry *)NULL; |
pWETE = AET; |
AET = AET->next; |
while (AET) |
{ |
if (AET->ClockWise) |
isInside++; |
else |
isInside--; |
|
if ((!inside && !isInside) || |
( inside && isInside)) |
{ |
pWETE->nextWETE = AET; |
pWETE = AET; |
inside = !inside; |
} |
AET = AET->next; |
} |
pWETE->nextWETE = (EdgeTableEntry *)NULL; |
} |
|
/* |
* REGION_InsertionSort |
* |
* Just a simple insertion sort using |
* pointers and back pointers to sort the Active |
* Edge Table. |
* |
*/ |
static MWBOOL REGION_InsertionSort(EdgeTableEntry *AET) |
{ |
EdgeTableEntry *pETEchase; |
EdgeTableEntry *pETEinsert; |
EdgeTableEntry *pETEchaseBackTMP; |
MWBOOL changed = FALSE; |
|
AET = AET->next; |
while (AET) |
{ |
pETEinsert = AET; |
pETEchase = AET; |
while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis) |
pETEchase = pETEchase->back; |
|
AET = AET->next; |
if (pETEchase != pETEinsert) |
{ |
pETEchaseBackTMP = pETEchase->back; |
pETEinsert->back->next = AET; |
if (AET) |
AET->back = pETEinsert->back; |
pETEinsert->next = pETEchase; |
pETEchase->back->next = pETEinsert; |
pETEchase->back = pETEinsert; |
pETEinsert->back = pETEchaseBackTMP; |
changed = TRUE; |
} |
} |
return changed; |
} |
|
/* |
* REGION_FreeStorage |
* |
* Clean up our act. |
*/ |
static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock) |
{ |
ScanLineListBlock *tmpSLLBlock; |
|
while (pSLLBlock) |
{ |
tmpSLLBlock = pSLLBlock->next; |
free( pSLLBlock ); |
pSLLBlock = tmpSLLBlock; |
} |
} |
|
|
/* |
* REGION_PtsToRegion |
* |
* Create an array of rectangles from a list of points. |
*/ |
static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock, |
POINTBLOCK *FirstPtBlock, MWCLIPREGION *reg) |
{ |
MWRECT *rects; |
MWPOINT *pts; |
POINTBLOCK *CurPtBlock; |
int i; |
MWRECT *extents; |
int numRects; |
|
extents = ®->extents; |
|
numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1; |
|
if (!(reg->rects = realloc( reg->rects, sizeof(MWRECT) * numRects ))) |
return(0); |
|
reg->size = numRects; |
CurPtBlock = FirstPtBlock; |
rects = reg->rects - 1; |
numRects = 0; |
extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE; |
|
for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) { |
/* the loop uses 2 points per iteration */ |
i = NUMPTSTOBUFFER >> 1; |
if (!numFullPtBlocks) |
i = iCurPtBlock >> 1; |
for (pts = CurPtBlock->pts; i--; pts += 2) { |
if (pts->x == pts[1].x) |
continue; |
if (numRects && pts->x == rects->left && pts->y == rects->bottom && |
pts[1].x == rects->right && |
(numRects == 1 || rects[-1].top != rects->top) && |
(i && pts[2].y > pts[1].y)) { |
rects->bottom = pts[1].y + 1; |
continue; |
} |
numRects++; |
rects++; |
rects->left = pts->x; rects->top = pts->y; |
rects->right = pts[1].x; rects->bottom = pts[1].y + 1; |
if (rects->left < extents->left) |
extents->left = rects->left; |
if (rects->right > extents->right) |
extents->right = rects->right; |
} |
CurPtBlock = CurPtBlock->next; |
} |
|
if (numRects) { |
extents->top = reg->rects->top; |
extents->bottom = rects->bottom; |
} else { |
extents->left = 0; |
extents->top = 0; |
extents->right = 0; |
extents->bottom = 0; |
} |
reg->numRects = numRects; |
|
return(TRUE); |
} |
|
/* |
* GdAllocPolygonRegion |
*/ |
MWCLIPREGION * |
GdAllocPolygonRegion(MWPOINT *points, int count, int mode) |
{ |
return GdAllocPolyPolygonRegion(points, &count, 1, mode ); |
} |
|
/* |
* GdAllocPolyPolygonRegion |
*/ |
MWCLIPREGION * |
GdAllocPolyPolygonRegion(MWPOINT *points, int *count, int nbpolygons, int mode) |
{ |
MWCLIPREGION *rgn; |
EdgeTableEntry *pAET; /* Active Edge Table */ |
int y; /* current scanline */ |
int iPts = 0; /* number of pts in buffer */ |
EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/ |
ScanLineList *pSLL; /* current scanLineList */ |
MWPOINT *pts; /* output buffer */ |
EdgeTableEntry *pPrevAET; /* ptr to previous AET */ |
EdgeTable ET; /* header node for ET */ |
EdgeTableEntry AET; /* header node for AET */ |
EdgeTableEntry *pETEs; /* EdgeTableEntries pool */ |
ScanLineListBlock SLLBlock; /* header for scanlinelist */ |
int fixWAET = FALSE; |
POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */ |
POINTBLOCK *tmpPtBlock; |
int numFullPtBlocks = 0; |
int poly, total; |
|
if(!(rgn = GdAllocRegion())) |
return NULL; |
|
/* special case a rectangle */ |
|
if (((nbpolygons == 1) && ((*count == 4) || |
((*count == 5) && (points[4].x == points[0].x) |
&& (points[4].y == points[0].y)))) && |
(((points[0].y == points[1].y) && |
(points[1].x == points[2].x) && |
(points[2].y == points[3].y) && |
(points[3].x == points[0].x)) || |
((points[0].x == points[1].x) && |
(points[1].y == points[2].y) && |
(points[2].x == points[3].x) && |
(points[3].y == points[0].y)))) |
{ |
GdSetRectRegion( rgn, |
MWMIN(points[0].x, points[2].x), MWMIN(points[0].y, points[2].y), |
MWMAX(points[0].x, points[2].x), MWMAX(points[0].y, points[2].y) ); |
return rgn; |
} |
|
for(poly = total = 0; poly < nbpolygons; poly++) |
total += count[poly]; |
if (! (pETEs = malloc( sizeof(EdgeTableEntry) * total ))) |
{ |
GdDestroyRegion( rgn ); |
return 0; |
} |
pts = FirstPtBlock.pts; |
REGION_CreateETandAET(count, nbpolygons, points, &ET, &AET, |
pETEs, &SLLBlock); |
pSLL = ET.scanlines.next; |
curPtBlock = &FirstPtBlock; |
|
if (mode != MWPOLY_WINDING) { |
/* |
* for each scanline |
*/ |
for (y = ET.ymin; y < ET.ymax; y++) { |
/* |
* Add a new edge to the active edge table when we |
* get to the next edge. |
*/ |
if (pSLL != NULL && y == pSLL->scanline) { |
REGION_loadAET(&AET, pSLL->edgelist); |
pSLL = pSLL->next; |
} |
pPrevAET = &AET; |
pAET = AET.next; |
|
/* |
* for each active edge |
*/ |
while (pAET) { |
pts->x = pAET->bres.minor_axis, pts->y = y; |
pts++, iPts++; |
|
/* |
* send out the buffer |
*/ |
if (iPts == NUMPTSTOBUFFER) { |
tmpPtBlock = malloc( sizeof(POINTBLOCK)); |
if(!tmpPtBlock) { |
return 0; |
} |
curPtBlock->next = tmpPtBlock; |
curPtBlock = tmpPtBlock; |
pts = curPtBlock->pts; |
numFullPtBlocks++; |
iPts = 0; |
} |
EVALUATEEDGEEVENODD(pAET, pPrevAET, y); |
|
} |
REGION_InsertionSort(&AET); |
} |
} |
else { |
/* |
* for each scanline |
*/ |
for (y = ET.ymin; y < ET.ymax; y++) { |
/* |
* Add a new edge to the active edge table when we |
* get to the next edge. |
*/ |
if (pSLL != NULL && y == pSLL->scanline) { |
REGION_loadAET(&AET, pSLL->edgelist); |
REGION_computeWAET(&AET); |
pSLL = pSLL->next; |
} |
pPrevAET = &AET; |
pAET = AET.next; |
pWETE = pAET; |
|
/* |
* for each active edge |
*/ |
while (pAET) { |
/* |
* add to the buffer only those edges that |
* are in the Winding active edge table. |
*/ |
if (pWETE == pAET) { |
pts->x = pAET->bres.minor_axis, pts->y = y; |
pts++, iPts++; |
|
/* |
* send out the buffer |
*/ |
if (iPts == NUMPTSTOBUFFER) { |
tmpPtBlock = malloc( sizeof(POINTBLOCK) ); |
if(!tmpPtBlock) { |
return 0; |
} |
curPtBlock->next = tmpPtBlock; |
curPtBlock = tmpPtBlock; |
pts = curPtBlock->pts; |
numFullPtBlocks++; iPts = 0; |
} |
pWETE = pWETE->nextWETE; |
} |
EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET); |
} |
|
/* |
* recompute the winding active edge table if |
* we just resorted or have exited an edge. |
*/ |
if (REGION_InsertionSort(&AET) || fixWAET) { |
REGION_computeWAET(&AET); |
fixWAET = FALSE; |
} |
} |
} |
REGION_FreeStorage(SLLBlock.next); |
REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, rgn); |
for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) { |
tmpPtBlock = curPtBlock->next; |
free( curPtBlock ); |
curPtBlock = tmpPtBlock; |
} |
free( pETEs ); |
return rgn; |
} |
|
#endif /* POLYREGIONS*/ |
/devclip1.c
0,0 → 1,247
/* |
* Copyright (c) 1999 Greg Haerr <greg@censoft.com> |
* Copyright (c) 1991 David I. Bell |
* Permission is granted to use, distribute, or modify this source, |
* provided that this copyright notice remains intact. |
* |
* Device-independent routines to determine clipping regions. |
*/ |
#include "device.h" |
|
/* Clip cache rectangle information. |
* After calling GdClipPoint, this rectangle is guaranteed to contain the |
* specified point (among others), and all points in the rectangle are |
* plottable or not according to the value of clipresult. |
*/ |
MWCOORD clipminx; /* minimum x value of cache rectangle */ |
MWCOORD clipminy; /* minimum y value of cache rectangle */ |
MWCOORD clipmaxx; /* maximum x value of cache rectangle */ |
MWCOORD clipmaxy; /* maximum y value of cache rectangle */ |
|
static MWBOOL clipresult; /* whether clip rectangle is plottable */ |
int clipcount; /* number of clip rectangles */ |
MWCLIPRECT cliprects[MAX_CLIPRECTS]; /* clip rectangles */ |
|
/* |
* Set an array of clip rectangles for future drawing actions. |
* Each pixel will be drawn only if lies in one or more of the specified |
* clip rectangles. As a special case, specifying no rectangles implies |
* clipping is for the complete screen. All clip rectangles are modified |
* if necessary to lie within the device area. Call only after device |
* has been initialized. |
*/ |
void |
GdSetClipRects(PSD psd,int count, MWCLIPRECT *table) |
{ |
register MWCLIPRECT *rp; /* current rectangle */ |
|
/* If there are no clip rectangles, then default to the full device area. */ |
if (count <= 0) { |
clipminx = 0; |
clipminy = 0; |
clipmaxx = psd->xvirtres - 1; |
clipmaxy = psd->yvirtres - 1; |
clipcount = 0; |
clipresult = TRUE; |
return; |
} |
|
/* Copy the clip table to our own static array, modifying each |
* rectangle as necesary to fit within the device area. If the clip |
* rectangle lies entirely outside of the device area, then skip it. |
*/ |
rp = cliprects; |
clipcount = 0; |
if (count > MAX_CLIPRECTS) count = MAX_CLIPRECTS; |
while (count-- > 0) { |
*rp = *table++; |
if (rp->x < 0) { |
rp->width += rp->x; |
rp->x = 0; |
} |
if (rp->y < 0) { |
rp->height += rp->y; |
rp->y = 0; |
} |
if ((rp->x >= psd->xvirtres) || (rp->width <= 0) || |
(rp->y >= psd->yvirtres) || (rp->height <= 0)) |
continue; |
if (rp->x + rp->width > psd->xvirtres) |
rp->width = psd->xvirtres - rp->x; |
if (rp->y + rp->height > psd->yvirtres) |
rp->height = psd->yvirtres - rp->y; |
rp++; |
clipcount++; |
} |
|
/* If there were no surviving clip rectangles, then set the clip |
* cache to prevent all drawing. |
*/ |
if (clipcount == 0) { |
clipminx = MIN_MWCOORD; |
clipminy = MIN_MWCOORD; |
clipmaxx = MAX_MWCOORD; |
clipmaxy = MAX_MWCOORD; |
clipresult = FALSE; |
return; |
} |
|
/* There was at least one valid clip rectangle. Default the clip |
* cache to be the first clip rectangle. |
*/ |
clipminx = cliprects[0].x; |
clipminy = cliprects[0].y; |
clipmaxx = clipminx + cliprects[0].width - 1; |
clipmaxy = clipminy + cliprects[0].height - 1; |
clipresult = TRUE; |
} |
|
|
/* Check a point against the list of clip rectangles. |
* Returns TRUE if the point is within one or more rectangles and thus |
* can be plotted, or FALSE if the point is not within any rectangle and |
* thus cannot be plotted. Also remembers the coordinates of a clip cache |
* rectangle containing the specified point such that every point in the |
* rectangle would give the same result. By examining this clip cache |
* rectangle after a call to this routine, the caller can efficiently |
* check many nearby points without needing any further calls. If the |
* point lies within the cursor, then the cursor is removed. |
*/ |
MWBOOL |
GdClipPoint(PSD psd,MWCOORD x,MWCOORD y) |
{ |
int count; |
MWCLIPRECT *rp; |
MWCOORD temp; |
|
/* First see whether the point lies within the current clip cache |
* rectangle. If so, then we already know the result. |
*/ |
if ((x >= clipminx) && (x <= clipmaxx) && |
(y >= clipminy) && (y <= clipmaxy)) { |
if (clipresult) GdCheckCursor(psd, x, y, x, y); |
return clipresult; |
} |
|
/* If the point is outside of the screen area, then it is not |
* plottable, and the clip cache rectangle is the whole half-plane |
* outside of the screen area. |
*/ |
if (x < 0) { |
clipminx = MIN_MWCOORD; |
clipmaxx = -1; |
clipminy = MIN_MWCOORD; |
clipmaxy = MAX_MWCOORD; |
clipresult = FALSE; |
return FALSE; |
} |
if (y < 0) { |
clipminx = MIN_MWCOORD; |
clipmaxx = MAX_MWCOORD; |
clipminy = MIN_MWCOORD; |
clipmaxy = -1; |
clipresult = FALSE; |
return FALSE; |
} |
if (x >= psd->xvirtres) { |
clipminx = psd->xvirtres; |
clipmaxx = MAX_MWCOORD; |
clipminy = MIN_MWCOORD; |
clipmaxy = MAX_MWCOORD; |
clipresult = FALSE; |
return FALSE; |
} |
if (y >= psd->yvirtres) { |
clipminx = MIN_MWCOORD; |
clipmaxx = MAX_MWCOORD; |
clipminy = psd->yvirtres; |
clipmaxy = MAX_MWCOORD; |
clipresult = FALSE; |
return FALSE; |
} |
|
/* The point is within the screen area. If there are no clip |
* rectangles, then the point is plottable and the rectangle is the |
* whole screen. |
*/ |
count = clipcount; |
if (count <= 0) { |
clipminx = 0; |
clipmaxx = psd->xvirtres - 1; |
clipminy = 0; |
clipmaxy = psd->yvirtres - 1; |
clipresult = TRUE; |
GdCheckCursor(psd, x, y, x, y); |
return TRUE; |
} |
|
/* We need to scan the list of clip rectangles to calculate a new |
* clip cache rectangle containing this point, and the result. First |
* see if the point lies within any of the clip rectangles. If so, |
* then it is plottable and use that clip rectangle as the cache |
* rectangle. This is not necessarily the best result, but works ok |
* and is fast. |
*/ |
for (rp = cliprects; count-- > 0; rp++) { |
if ((x >= rp->x) && (y >= rp->y) && (x < rp->x + rp->width) |
&& (y < rp->y + rp->height)) { |
clipminx = rp->x; |
clipminy = rp->y; |
clipmaxx = rp->x + rp->width - 1; |
clipmaxy = rp->y + rp->height - 1; |
clipresult = TRUE; |
GdCheckCursor(psd, x, y, x, y); |
return TRUE; |
} |
} |
|
/* The point is not plottable. Scan the clip rectangles again to |
* determine a rectangle containing more non-plottable points. |
* Simply pick the largest rectangle whose area doesn't contain any |
* of the same coordinates as appropriate sides of the clip |
* rectangles. This is not necessarily the best result, but works ok |
* and is fast. |
*/ |
clipminx = MIN_MWCOORD; |
clipminy = MIN_MWCOORD; |
clipmaxx = MAX_MWCOORD; |
clipmaxy = MAX_MWCOORD; |
count = clipcount; |
for (rp = cliprects; count-- > 0; rp++) { |
if ((x < rp->x) && (rp->x <= clipmaxx)) clipmaxx = rp->x - 1; |
temp = rp->x + rp->width - 1; |
if ((x > temp) && (temp >= clipminx)) clipminx = temp + 1; |
if ((y < rp->y) && (rp->y <= clipmaxy)) clipmaxy = rp->y - 1; |
temp = rp->y + rp->height - 1; |
if ((y > temp) && (temp >= clipminy)) clipminy = temp + 1; |
} |
clipresult = FALSE; |
return FALSE; |
} |
|
|
/* Check the area determined by the specified pair of points against the |
* list of clip rectangles. The area will either be totally visible, |
* totally visible, or possibly partially visible. This routine updates |
* the clip cache rectangle, and returns one of the following values: |
* CLIP_VISIBLE The whole rectangle is visible |
* CLIP_INVISIBLE The whole rectangle is invisible |
* CLIP_PARTIAL The rectangle may be partially visible |
* In the case that the area is totally visible, the cursor is removed |
* if it overlaps the clip area. |
*/ |
int |
GdClipArea(PSD psd,MWCOORD x1, MWCOORD y1, MWCOORD x2, MWCOORD y2) |
{ |
if ((x1 < clipminx) || (x1 > clipmaxx) || |
(y1 < clipminy) || (y1 > clipmaxy)) |
GdClipPoint(psd, x1, y1); |
|
if ((x2 >= clipminx) && (x2 <= clipmaxx) && |
(y2 >= clipminy) && (y2 <= clipmaxy)) { |
if (!clipresult) return CLIP_INVISIBLE; |
GdCheckCursor(psd, x1, y1, x2, y2); |
return CLIP_VISIBLE; |
} |
return CLIP_PARTIAL; |
} |
/devclip2.c
0,0 → 1,248
/* |
* Copyright (c) 2000 Greg Haerr <greg@censoft.com> |
* Copyright (c) 1991 David I. Bell |
* Permission is granted to use, distribute, or modify this source, |
* provided that this copyright notice remains intact. |
* |
* DYNAMICREGIONS Device-independent routines to set clipping regions. |
*/ |
#include <stdio.h> |
#include "device.h" |
|
/* Clip cache rectangle information. |
* After calling GdClipPoint, this rectangle is guaranteed to contain the |
* specified point (among others), and all points in the rectangle are |
* plottable or not according to the value of clipresult. |
*/ |
MWCOORD clipminx; /* minimum x value of cache rectangle */ |
MWCOORD clipminy; /* minimum y value of cache rectangle */ |
MWCOORD clipmaxx; /* maximum x value of cache rectangle */ |
MWCOORD clipmaxy; /* maximum y value of cache rectangle */ |
|
static MWBOOL clipresult; /* whether clip rectangle is plottable */ |
MWCLIPREGION *clipregion = NULL; |
|
/* |
* Set a clip region for future drawing actions. |
* Each pixel will be drawn only if lies in one or more of the contained |
* clip rectangles. All clip rectangles are modified |
* if necessary to lie within the device area. Call only after device |
* has been initialized. |
*/ |
void |
GdSetClipRegion(PSD psd, MWCLIPREGION *reg) |
{ |
if(clipregion) |
GdDestroyRegion(clipregion); |
|
if(!reg) |
reg = GdAllocRegion(); |
|
clipregion = reg; |
|
|
#if 0 |
MWRECT rc; |
/* Copy the clip table to our own static array, modifying each |
* rectangle as necesary to fit within the device area. If the clip |
* rectangle lies entirely outside of the device area, then skip it. |
*/ |
while (count-- > 0) { |
MWCLIPRECT cr; |
MWCLIPRECT *rp = &cr; |
|
*rp = *table++; |
if (rp->x < 0) { |
rp->width += rp->x; |
rp->x = 0; |
} |
if (rp->y < 0) { |
rp->height += rp->y; |
rp->y = 0; |
} |
if ((rp->x >= psd->xvirtres) || (rp->width <= 0) || |
(rp->y >= psd->yvirtres) || (rp->height <= 0)) |
continue; |
if (rp->x + rp->width > psd->xvirtres) |
rp->width = psd->xvirtres - rp->x; |
if (rp->y + rp->height > psd->yvirtres) |
rp->height = psd->yvirtres - rp->y; |
rc.left = rp->x; |
rc.top = rp->y; |
rc.right = rp->x+rp->width; |
rc.bottom = rp->y+rp->height; |
GdUnionRectWithRegion(&rc, clipregion); |
} |
#endif |
|
/* If there were no surviving clip rectangles, then set the clip |
* cache to prevent all drawing. |
*/ |
if (clipregion->numRects == 0) { |
clipminx = MIN_MWCOORD; |
clipminy = MIN_MWCOORD; |
clipmaxx = MAX_MWCOORD; |
clipmaxy = MAX_MWCOORD; |
clipresult = FALSE; |
return; |
} |
|
/* There was at least one valid clip rectangle. Default the clip |
* cache to be the first clip rectangle. |
*/ |
clipminx = clipregion->rects[0].left; |
clipminy = clipregion->rects[0].top; |
clipmaxx = clipregion->rects[0].right - 1; |
clipmaxy = clipregion->rects[0].bottom - 1; |
clipresult = TRUE; |
} |
|
|
/* Check a point against the list of clip rectangles. |
* Returns TRUE if the point is within one or more rectangles and thus |
* can be plotted, or FALSE if the point is not within any rectangle and |
* thus cannot be plotted. Also remembers the coordinates of a clip cache |
* rectangle containing the specified point such that every point in the |
* rectangle would give the same result. By examining this clip cache |
* rectangle after a call to this routine, the caller can efficiently |
* check many nearby points without needing any further calls. If the |
* point lies within the cursor, then the cursor is removed. |
*/ |
MWBOOL |
GdClipPoint(PSD psd,MWCOORD x,MWCOORD y) |
{ |
int count; |
MWRECT *rp; |
MWCOORD temp; |
|
/* First see whether the point lies within the current clip cache |
* rectangle. If so, then we already know the result. |
*/ |
if ((x >= clipminx) && (x <= clipmaxx) && |
(y >= clipminy) && (y <= clipmaxy)) { |
if (clipresult) GdCheckCursor(psd, x, y, x, y); |
return clipresult; |
} |
|
/* If the point is outside of the screen area, then it is not |
* plottable, and the clip cache rectangle is the whole half-plane |
* outside of the screen area. |
*/ |
if (x < 0) { |
clipminx = MIN_MWCOORD; |
clipmaxx = -1; |
clipminy = MIN_MWCOORD; |
clipmaxy = MAX_MWCOORD; |
clipresult = FALSE; |
return FALSE; |
} |
if (y < 0) { |
clipminx = MIN_MWCOORD; |
clipmaxx = MAX_MWCOORD; |
clipminy = MIN_MWCOORD; |
clipmaxy = -1; |
clipresult = FALSE; |
return FALSE; |
} |
if (x >= psd->xvirtres) { |
clipminx = psd->xvirtres; |
clipmaxx = MAX_MWCOORD; |
clipminy = MIN_MWCOORD; |
clipmaxy = MAX_MWCOORD; |
clipresult = FALSE; |
return FALSE; |
} |
if (y >= psd->yvirtres) { |
clipminx = MIN_MWCOORD; |
clipmaxx = MAX_MWCOORD; |
clipminy = psd->yvirtres; |
clipmaxy = MAX_MWCOORD; |
clipresult = FALSE; |
return FALSE; |
} |
|
/* The point is within the screen area. If there are no clip |
* rectangles, then the point is plottable and the rectangle is the |
* whole screen. |
*/ |
count = clipregion->numRects; |
if (count <= 0) { |
clipminx = 0; |
clipmaxx = psd->xvirtres - 1; |
clipminy = 0; |
clipmaxy = psd->yvirtres - 1; |
clipresult = TRUE; |
GdCheckCursor(psd, x, y, x, y); |
return TRUE; |
} |
|
/* We need to scan the list of clip rectangles to calculate a new |
* clip cache rectangle containing this point, and the result. First |
* see if the point lies within any of the clip rectangles. If so, |
* then it is plottable and use that clip rectangle as the cache |
* rectangle. This is not necessarily the best result, but works ok |
* and is fast. |
*/ |
for (rp = clipregion->rects; count-- > 0; rp++) { |
if ((x >= rp->left) && (y >= rp->top) && (x < rp->right) |
&& (y < rp->bottom)) { |
clipminx = rp->left; |
clipminy = rp->top; |
clipmaxx = rp->right - 1; |
clipmaxy = rp->bottom - 1; |
clipresult = TRUE; |
GdCheckCursor(psd, x, y, x, y); |
return TRUE; |
} |
} |
|
/* The point is not plottable. Scan the clip rectangles again to |
* determine a rectangle containing more non-plottable points. |
* Simply pick the largest rectangle whose area doesn't contain any |
* of the same coordinates as appropriate sides of the clip |
* rectangles. This is not necessarily the best result, but works ok |
* and is fast. |
*/ |
clipminx = MIN_MWCOORD; |
clipminy = MIN_MWCOORD; |
clipmaxx = MAX_MWCOORD; |
clipmaxy = MAX_MWCOORD; |
count = clipregion->numRects; |
for (rp = clipregion->rects; count-- > 0; rp++) { |
if ((x < rp->left) && (rp->left <= clipmaxx)) clipmaxx = rp->left - 1; |
temp = rp->right - 1; |
if ((x > temp) && (temp >= clipminx)) clipminx = temp + 1; |
if ((y < rp->top) && (rp->top <= clipmaxy)) clipmaxy = rp->top - 1; |
temp = rp->bottom - 1; |
if ((y > temp) && (temp >= clipminy)) clipminy = temp + 1; |
} |
clipresult = FALSE; |
return FALSE; |
} |
|
|
/* Check the area determined by the specified pair of points against the |
* list of clip rectangles. The area will either be totally visible, |
* totally visible, or possibly partially visible. This routine updates |
* the clip cache rectangle, and returns one of the following values: |
* CLIP_VISIBLE The whole rectangle is visible |
* CLIP_INVISIBLE The whole rectangle is invisible |
* CLIP_PARTIAL The rectangle may be partially visible |
* In the case that the area is totally visible, the cursor is removed |
* if it overlaps the clip area. |
*/ |
int |
GdClipArea(PSD psd,MWCOORD x1, MWCOORD y1, MWCOORD x2, MWCOORD y2) |
{ |
if ((x1 < clipminx) || (x1 > clipmaxx) || |
(y1 < clipminy) || (y1 > clipmaxy)) |
GdClipPoint(psd, x1, y1); |
|
if ((x2 >= clipminx) && (x2 <= clipmaxx) && |
(y2 >= clipminy) && (y2 <= clipmaxy)) { |
if (!clipresult) return CLIP_INVISIBLE; |
GdCheckCursor(psd, x1, y1, x2, y2); |
return CLIP_VISIBLE; |
} |
return CLIP_PARTIAL; |
} |
/selfont.c
0,0 → 1,813
/* |
* Copyright (c) 2000 Greg Haerr <greg@censoft.com> |
* Copyright (c) 2000 Morten Rolland |
* |
* Device-independent font selection routines |
*/ |
/*#define NDEBUG*/ |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <assert.h> |
#include <ctype.h> /* toupper*/ |
#include <string.h> |
|
#include "device.h" |
#if (UNIX | DOS_DJGPP) |
#define strcmpi strcasecmp |
#endif |
|
#if FONTMAPPER |
|
/* entry points*/ |
int select_font(const PMWLOGFONT plogfont, char *physname); |
|
/* |
* The following structure, defines and variables are used to administrate |
* a set of fonts that are scanned when a font is requested. The set of |
* fonts can be configured with the GdClearFontList and GdAddFont functions. |
*/ |
|
struct available_font { |
MWLOGFONT lf; |
char *foundry; |
char *family; |
char *fontname; |
int fontclass, alias; |
struct available_font *next; |
}; |
|
static struct available_font *all_fonts = 0; |
|
/* |
* Stupid little function to perform a comparison of two strings, |
* returning one of STREQ_EXACT, STREQ_CASE or STREQ_NOMATCH. |
*/ |
#define STREQ_EXACT 0 |
#define STREQ_CASE 1 |
#define STREQ_NOMATCH 2 |
|
static int streq(const char *a, const char *b) |
{ |
int rc; |
|
for ( rc = STREQ_EXACT; *a != 0; a++, b++ ) { |
if ( *a == *b ) |
continue; |
if ( toupper((unsigned char)*a) != toupper((unsigned char)*b) ) |
return STREQ_NOMATCH; |
rc = STREQ_CASE; |
} |
if ( *b != 0 ) |
return STREQ_NOMATCH; |
return rc; |
} |
|
/* |
* Decode the "foundry" information into a MWLF_CLASS_ value denoting |
* which rendering system should be used to render the font. |
*/ |
static int decode_font_class(const char *class) |
{ |
if ( class == NULL ) |
return 0; |
if ( !strcmp("T1",class) ) |
return MWLF_CLASS_T1LIB; |
if ( !strcmp("FT",class) ) |
return MWLF_CLASS_FREETYPE; |
if ( !strcmp("MWF",class) ) |
return MWLF_CLASS_BUILTIN; |
return 0; |
} |
|
/* |
* Try to find a font that matches the foundry and font name |
* requested. |
*/ |
void test_font_naming(const char *foundry, const char *fontname, |
struct available_font *af, |
struct available_font **best, int *goodness) |
{ |
int penalty = 0; |
|
if ( foundry != 0 ) { |
if ( af->foundry != 0 ) { |
switch ( streq(foundry,af->foundry) ) { |
case STREQ_EXACT: |
break; |
case STREQ_CASE: |
penalty += 2; |
break; |
default: |
penalty += 8; |
break; |
} |
} else { |
penalty += 4; |
} |
} |
|
switch ( streq(fontname,af->fontname) ) { |
case STREQ_EXACT: |
break; |
case STREQ_CASE: |
penalty += 1; |
break; |
default: |
penalty += 16; |
break; |
} |
|
if ( *goodness < 0 || penalty < *goodness ) { |
*best = af; |
*goodness = penalty; |
} |
} |
|
|
/* |
* The 'test_font_goodness' function attempts to find how suitable a font is |
* compared to a desired one. The desired font is specified by: |
* |
* foundry (can be NULL), family and the MWLOGFONT |
* structure (can be NULL). |
* |
* If any fonts are configured, one will always be returned from here, |
* but the goodness may be very bad. The goodness (or similarity) |
* factor is computed by adding up: |
*/ |
|
#define GOODNESS_NO_FONTNAME 1000 /* No matching font-name found*/ |
#define GOODNESS_WEIGHT_DIFF 1 /* 0-900 for weight difference*/ |
|
#define GOODNESS_NO_FOUNDRY 100 /* No foundry */ |
|
#define GOODNESS_FOUNDRY_UNKNOWN 16 /* Foundry unknown */ |
#define GOODNESS_CASE_FAMILY 8 |
#define GOODNESS_CASE_FONTNAME 4 |
#define GOODNESS_CASE_FOUNDRY 2 |
#define GOODNESS_EXACT_FAMILY 1 /* Matched font family */ |
|
#define GOODNESS_EXACT_FOUNDRY 0 /* Clean match */ |
#define GOODNESS_EXACT_FONTNAME 0 /* Clean match */ |
|
#define GOODNESS_FAMILY_UNKNOWN 3000 |
#define GOODNESS_NOMATCH_FAMILY 10000 |
|
#define GOODNESS_EXACT_FONTFAMILY 0 |
#define GOODNESS_CASE_FONTFAMILY 32 |
#define GOODNESS_CLOSE_SLANT 128 |
#define GOODNESS_BAD_SLANT 256 |
#define GOODNESS_WRONG_SPACING 2048 |
|
#define GOODNESS_WRONG_SERIFSTYLE 1024 |
|
#define GOODNESS_WANTED_SMALLCAPS_NOT_AVAILABLE 4096 |
#define GOODNESS_ONLY_SMALLCAPS_AVAILABLE 8192 |
|
|
static int find_foundry_penalty(const struct available_font *have, |
const char *foundry) |
{ |
if ( foundry == 0 ) |
return 0; |
|
if ( have->foundry == 0 ) |
return GOODNESS_FOUNDRY_UNKNOWN; |
|
switch ( streq(foundry,have->foundry) ) { |
case STREQ_EXACT: |
return GOODNESS_EXACT_FOUNDRY; |
break; |
case STREQ_CASE: |
return GOODNESS_CASE_FOUNDRY; |
break; |
default: |
return GOODNESS_NO_FOUNDRY; |
break; |
} |
} |
|
static int find_family_penalty(const struct available_font *have, |
const char *family) |
{ |
if ( family == 0 ) |
return 0; |
|
if ( have->family == 0 ) |
return GOODNESS_FAMILY_UNKNOWN; |
|
switch ( streq(family,have->family) ) { |
case STREQ_EXACT: |
return GOODNESS_EXACT_FAMILY; |
break; |
case STREQ_CASE: |
return GOODNESS_CASE_FAMILY; |
break; |
default: |
return GOODNESS_NOMATCH_FAMILY; |
break; |
} |
} |
|
static int find_fontname_penalty(const struct available_font *have, |
const char *fontname) |
{ |
switch ( streq(have->fontname,fontname) ) { |
case STREQ_EXACT: |
return GOODNESS_EXACT_FONTNAME; |
break; |
case STREQ_CASE: |
return GOODNESS_CASE_FONTNAME; |
break; |
default: |
break; |
} |
|
/* Test Fontname against font family name */ |
if ( have->family != 0 ) { |
switch ( streq(have->family,fontname) ) { |
case STREQ_EXACT: |
return GOODNESS_EXACT_FONTFAMILY; |
break; |
case STREQ_CASE: |
return GOODNESS_CASE_FONTFAMILY; |
break; |
default: |
/* No suitable fontname found */ |
break; |
} |
} |
return GOODNESS_NO_FONTNAME; |
} |
|
static int find_weight_penalty(PMWLOGFONT want, PMWLOGFONT have) |
{ |
int weight_diff; |
|
weight_diff = want->lfWeight - have->lfWeight; |
if ( weight_diff < 0 ) |
weight_diff = -weight_diff; |
return weight_diff * GOODNESS_WEIGHT_DIFF; |
} |
|
static int find_slant_penalty(PMWLOGFONT want, PMWLOGFONT have) |
{ |
/* See if slant is acceptable */ |
|
if ( !want->lfItalic && !want->lfRoman && !want->lfOblique ) { |
/* Try to default to Romans if not specified */ |
if ( have->lfItalic || have->lfOblique ) |
return GOODNESS_CLOSE_SLANT; |
return 0; |
} |
|
if ( want->lfItalic && have->lfItalic ) |
return 0; |
|
if ( want->lfRoman && have->lfRoman ) |
return 0; |
|
if ( want->lfOblique && have->lfOblique ) |
return 0; |
|
/* No perfect match for the slant, try "closest" one */ |
|
if ( want->lfItalic && have->lfOblique ) |
return GOODNESS_CLOSE_SLANT; |
|
if ( want->lfOblique && have->lfItalic ) |
return GOODNESS_CLOSE_SLANT; |
|
return GOODNESS_BAD_SLANT; |
} |
|
static int find_spacing_penalty(PMWLOGFONT want, PMWLOGFONT have) |
{ |
if ( want->lfProportional && have->lfProportional ) |
return 0; |
if ( want->lfMonospace && have->lfMonospace ) |
return 0; |
if ( want->lfMonospace || want->lfProportional ) |
return GOODNESS_WRONG_SPACING; |
|
return 0; /* No special desires */ |
} |
|
static int find_serif_penalty(PMWLOGFONT want, PMWLOGFONT have) |
{ |
if ( !want->lfSerif && !want->lfSansSerif ) |
return 0; |
if ( want->lfSerif && have->lfSerif ) |
return 0; |
if ( want->lfSansSerif && have->lfSansSerif ) |
return 0; |
|
return GOODNESS_WRONG_SERIFSTYLE; |
} |
|
static int find_smallcaps_penalty(PMWLOGFONT want, PMWLOGFONT have) |
{ |
if ( !want->lfSmallCaps && !have->lfSmallCaps ) |
return 0; |
if ( want->lfSmallCaps && have->lfSmallCaps ) |
return 0; |
if ( want->lfSmallCaps ) |
return GOODNESS_WANTED_SMALLCAPS_NOT_AVAILABLE; |
return GOODNESS_ONLY_SMALLCAPS_AVAILABLE; |
} |
|
|
static void test_font_goodness(const char *foundry, const char *family, |
const char *fontname, const PMWLOGFONT lf, |
struct available_font *af, |
struct available_font **best, int *goodness) |
{ |
int penalty = 0; |
|
penalty += find_foundry_penalty(af,foundry); |
penalty += find_family_penalty(af,family); |
|
/* Test Fontname, but only if there is no family */ |
if ( family == 0 ) |
penalty += find_fontname_penalty(af,fontname); |
|
if ( lf != 0 ) { |
/* Check logical font attributes */ |
penalty += find_weight_penalty(lf,&af->lf); |
penalty += find_slant_penalty(lf,&af->lf); |
penalty += find_spacing_penalty(lf,&af->lf); |
penalty += find_serif_penalty(lf,&af->lf); |
penalty += find_smallcaps_penalty(lf,&af->lf); |
} |
|
/* See if this font is better than the previous one */ |
if ( *goodness < 0 || penalty < *goodness ) { |
/* Yes, this font is better; change to it */ |
*best = af; |
*goodness = penalty; |
} |
} |
|
static struct available_font *find_suitable_font(const char *foundry, |
const char *fontname, |
const PMWLOGFONT plogfont) |
{ |
struct available_font *af, *best; |
char *family; |
int goodness; |
|
/* |
* Try to find a font that matches the name specified as the |
* desired font (and foundry if possible). If we find a |
* suitable font, we will use the family name when trying to |
* find a font that matches the MWLOGFONT attributes. This |
* makes it possible to ask for an Italic version of |
* "Times Roman" and the expected thing will happen (get the |
* font "Times Italic"). |
*/ |
goodness = -1; |
for ( af = best = all_fonts; af != 0; af = af->next ) { |
test_font_naming(foundry,fontname,af,&best,&goodness); |
} |
family = 0; |
if ( goodness != -1 ) { |
/* A font with a name that kind of matched was found, |
* make a note of its family. If it has no family, we |
* can't make any use of the |
*/ |
family = best->family; |
} |
|
/* |
* Try to find the closest font that matches the font family |
* we have established. If no family was found above, all |
* fonts will be considered. |
*/ |
goodness = -1; |
for ( af = best = all_fonts; af != 0; af = af->next ) { |
test_font_goodness(foundry,family,fontname,plogfont,af, |
&best,&goodness); |
} |
|
return best; |
} |
|
|
int select_font(const PMWLOGFONT plogfont, char *physname) |
{ |
struct available_font *font; |
char fndry[128], *foundry; |
const char *fontname; |
char *tmp; |
int t, comma; |
int fontclass = 0; |
int found_font = 0; |
|
fontname = plogfont->lfFaceName; |
|
for ( t=0; t < 20; t++ ) { |
/* Only follow 20 aliases deep, assume failure if more ... */ |
|
/* Find foundry for the current font */ |
foundry = NULL; |
if ( (tmp = index(fontname,',')) != NULL ) { |
/* |
* We have a font name like T1,c0934345 or |
* Adobe,Times (e.g. it includes foundry or |
* rendering method). Separate them here. |
*/ |
comma = tmp - fontname; |
tmp++; |
strncpy(fndry,fontname,comma); |
fndry[comma] = '\0'; |
foundry = fndry; |
fontname = tmp; |
} |
|
fontclass = decode_font_class(foundry); |
|
if ( plogfont == NULL && fontclass == 0 ) |
fontclass = MWLF_CLASS_BUILTIN; |
|
if ( fontclass ) { |
/* The font is a "physical" font, use it directly */ |
strcpy(physname,fontname); |
return fontclass; |
} |
|
if ( found_font ) { |
/* Oops, should not get here, unless a font definition |
* resulted in a non-existent font, e.g. the fontclass |
* is unknown. |
*/ |
goto default_font; |
} |
|
font = find_suitable_font(foundry,fontname,plogfont); |
|
if ( font == NULL ) { |
goto default_font; |
} |
|
if ( !font->alias ) |
found_font = 1; |
|
fontname = font->lf.lfFaceName; |
} |
|
default_font: |
|
strcpy(physname, MWFONT_SYSTEM_VAR); |
return MWLF_CLASS_BUILTIN; |
} |
|
/* This function can be used to clear the existing, possibly default list |
* of fonts on the system. This is typically done before reading a |
* configuration file that defines the available fonts. |
*/ |
void |
GdClearFontList(void) |
{ |
struct available_font *font, *next_font; |
|
font = all_fonts; |
while ( font != 0 ) { |
next_font = font->next; |
if ( font->foundry != 0 ) |
free(font->foundry); |
if ( font->family != 0 ) |
free(font->family); |
free(font->fontname); |
free(font); |
font = next_font; |
} |
|
all_fonts = 0; |
} |
|
/* This function will add a font to the list of available fonts. |
* The physical name is the name as used by the underlying font |
* rendering engine. |
*/ |
int |
GdAddFont(char *fndry, char *fmly, char *fontname, PMWLOGFONT lf, |
unsigned int flags) |
{ |
struct available_font *font, *walk; |
int fontclass = 0; |
char *physname = lf->lfFaceName; |
|
if ( !strncmp(physname,"T1,",3) ) { |
#ifdef HAVE_T1LIB_SUPPORT |
/* Can handle Type 1 fonts */ |
physname += 3; |
fontclass = MWLF_CLASS_T1LIB; |
goto do_font; |
#else |
/* Can't handle Type 1 fonts */ |
return -1; |
#endif |
} |
|
if ( !strncmp(physname,"FT,",3) ) { |
#if HAVE_FREETYPE_SUPPORT |
/* Can handle FreeType fonts */ |
physname += 3; |
fontclass = MWLF_CLASS_FREETYPE; |
goto do_font; |
#else |
/* Can't handle Type 1 fonts */ |
return -1; |
#endif |
} |
|
if ( !strncmp(physname,"MWF,",4) ) { |
/* This is a Microwindows built in font */ |
physname += 4; |
fontclass = MWLF_CLASS_BUILTIN; |
goto do_font; |
} |
|
/* Only aliases does not need to use T1, FT or MWF description */ |
if ( !(flags & MWLF_FLAGS_ALIAS) ) |
return -1; |
|
do_font: |
|
font = malloc(sizeof(*font)); |
if ( font == 0 ) |
return -1; |
|
font->foundry = 0; |
if ( strcmp("-",fndry) ) { |
font->foundry = strdup(fndry); |
if ( font->foundry == 0 ) { |
free(font); |
return -1; |
} |
} |
|
font->family = 0; |
if ( strcmp("-",fmly) ) { |
font->family = strdup(fmly); |
if ( font->family == 0 ) { |
free(font->foundry); |
free(font); |
return -1; |
} |
} |
|
font->fontname = strdup(fontname); |
if ( font->fontname == 0 ) { |
free(font->foundry); |
free(font->family); |
free(font); |
return -1; |
} |
|
memcpy(&font->lf,lf,sizeof(*lf)); |
|
printf("Adding font: '%s' '%s' '%s' '%s'\n",font->foundry, |
font->family,font->fontname,font->lf.lfFaceName); |
|
font->next = 0; |
font->alias = (flags & MWLF_FLAGS_ALIAS) ? 1 : 0; |
font->fontclass = fontclass; |
|
/* Stupid append at end of list code */ |
if ( all_fonts == 0 ) { |
all_fonts = font; |
} else { |
for ( walk = all_fonts; walk->next != 0; walk = walk->next ) |
; |
walk->next = font; |
} |
|
return 0; |
} |
|
/* |
* These functions are used to set attributes in a logical font |
* structure, called through a table of function pointers. |
*/ |
static void font_set_light(PMWLOGFONT lf) |
{ lf->lfWeight = MWLF_WEIGHT_LIGHT; } |
static void font_set_regular(PMWLOGFONT lf) |
{ lf->lfWeight = MWLF_WEIGHT_REGULAR; } |
static void font_set_medium(PMWLOGFONT lf) |
{ lf->lfWeight = MWLF_WEIGHT_MEDIUM; } |
static void font_set_demibold(PMWLOGFONT lf) |
{ lf->lfWeight = MWLF_WEIGHT_DEMIBOLD; } |
static void font_set_bold(PMWLOGFONT lf) |
{ lf->lfWeight = MWLF_WEIGHT_BOLD; } |
static void font_set_black(PMWLOGFONT lf) |
{ lf->lfWeight = MWLF_WEIGHT_BLACK; } |
|
static void font_set_italic(PMWLOGFONT lf) { lf->lfItalic = 1; } |
static void font_set_roman(PMWLOGFONT lf) { lf->lfRoman = 1; } |
static void font_set_oblique(PMWLOGFONT lf) { lf->lfOblique = 1; } |
|
static void font_set_normal(PMWLOGFONT lf) |
{ lf->lfPitch = MWLF_PITCH_NORMAL; } |
static void font_set_semicondensed(PMWLOGFONT lf) |
{ lf->lfPitch = MWLF_PITCH_SEMICONDENSED; } |
static void font_set_condensed(PMWLOGFONT lf) |
{ lf->lfPitch = MWLF_PITCH_CONDENSED; } |
|
static void font_set_serif(PMWLOGFONT lf) { lf->lfSerif = 1; } |
static void font_set_sansserif(PMWLOGFONT lf) { lf->lfSansSerif = 1; } |
static void font_set_monospace(PMWLOGFONT lf) { lf->lfMonospace = 1; } |
static void font_set_proportional(PMWLOGFONT lf) { lf->lfProportional = 1; } |
|
int config_font(char *file, int line, int argc, char *argv[]) |
{ |
unsigned int flags = 0; |
MWLOGFONT lf; |
char tmp[512]; |
char *p, *q, *fndry, *family, *fontname; |
int size, t; |
|
static struct { |
char *name; |
void (*function)(PMWLOGFONT lf); |
} attrs[] = { |
/* Weight */ |
{ "Light", font_set_light }, |
{ "Regular", font_set_regular }, |
{ "Medium", font_set_medium }, |
{ "DemiBold", font_set_demibold }, |
{ "Demibold", font_set_demibold }, |
{ "Bold", font_set_bold }, |
{ "Black", font_set_black }, |
|
/* Slant */ |
{ "Italic", font_set_italic }, |
{ "Italics", font_set_italic }, |
{ "Roman", font_set_roman }, |
{ "Oblique", font_set_oblique }, |
|
/* Width */ |
{ "Normal", font_set_normal }, |
{ "Semicondensed", font_set_semicondensed }, |
{ "Condensed", font_set_condensed }, |
|
/* Class */ |
{ "Serif", font_set_serif }, |
{ "Sans-serif", font_set_sansserif }, |
{ "Monospace", font_set_monospace }, |
{ "Proportional", font_set_proportional }, |
|
{ 0, 0 } |
}; |
|
MWLF_Clear(&lf); |
|
if ( argc != 6 ) { |
fprintf(stderr,"Bad font description %s:%d\n",file,line); |
return 1; |
} |
|
if ( !strcmp("alias",argv[1]) ) { |
flags |= MWLF_FLAGS_ALIAS; |
fndry = "-"; |
} else { |
fndry = argv[1]; |
} |
|
family = argv[2]; |
fontname = argv[3]; |
strcpy(lf.lfFaceName,argv[5]); |
p = argv[4]; |
|
while ( *p != '\0' ) { |
/* Parse attributes */ |
q = strchr(p,','); |
if ( q != 0 ) { |
size = q - p; |
strncpy(tmp,p,size); |
tmp[size] = '\0'; |
p = q + 1; |
} else { |
strcpy(tmp,p); |
p += strlen(tmp); |
} |
|
for ( t = 0; attrs[t].name != 0; t++ ) { |
if ( !strcmp(attrs[t].name,tmp) ) { |
attrs[t].function(&lf); |
goto next; |
} |
} |
|
fprintf(stderr,"No such font attribute '%s' in %s:%d\n", |
tmp,file,line); |
return 1; |
|
next: ; |
} |
|
GdAddFont(fndry,family,fontname,&lf,flags); |
|
return 0; |
} |
|
/* |
* Handle a single configuration line entery. Arguments as for |
* function 'main(int argc, char **argv)' -- argv[0] is name of |
* original configuration file. Return negative value for error, |
* zero for OK. |
*/ |
|
int config_line(char *file, int line, int argc, char *argv[]) |
{ |
if ( !argc ) |
return 0; /* Empty line */ |
|
if ( argv[0][0] == '#' ) |
return 0; /* Comment line */ |
|
if ( !strcmp("font", argv[0]) ) |
return config_font(file,line,argc,argv); |
|
if ( !strcmp("clear-fonts", argv[0]) ) { |
GdClearFontList(); |
return 0; |
} |
|
return -1; |
} |
|
|
/* |
* Read (one of) the configuration files. |
*/ |
#define MAXCONFIGLINESIZE 1024 |
#define MAXCONFIGELEMENTS 64 |
|
int read_configfile(char *file) |
{ |
FILE *cf; |
char buffer[MAXCONFIGLINESIZE+1]; |
char *args[MAXCONFIGELEMENTS+1]; |
unsigned char *p; |
int argc, s, rc, t, line; |
|
if ( (cf = fopen(file,"r")) == 0 ) { |
fprintf(stderr,"Unable to read config file '%s'\n",file); |
return -1; |
} |
|
line = 0; |
while ( !feof(cf) ) { |
if ( fgets(buffer,1000,cf) == 0 ) |
break; |
line++; |
s = strlen(buffer) - 1; |
while ( s >= 0 && buffer[s] == '\n' ) |
buffer[s--] = '\0'; |
p = (unsigned char *)buffer; |
argc = 0; |
for ( t=0; t < MAXCONFIGELEMENTS; t++ ) { |
while ( *p != '\0' && isspace(*p) ) |
p++; |
if ( *p == '\"' ) { |
/* Quoted string */ |
p++; |
args[t] = p; |
argc++; |
while ( *p != '\0' && *p != '\"' ) |
p++; |
if ( *p == '\0' ) { |
fprintf(stderr,"Unbalanced quotes in %s:%d\n", |
file,line); |
break; |
} |
*p++ = '\0'; |
} else { |
if ( *p == '\0' ) |
break; |
args[t] = p; |
argc++; |
while ( *p != '\0' && !isspace(*p) ) |
p++; |
*p++ = '\0'; |
} |
} |
#if 0 |
{ |
int t; |
for ( t=0; t < argc; t++ ) |
printf("#%d: '%s'\n",t,args[t]); |
} |
#endif |
rc = config_line(file, line, argc, args); |
if ( rc < 0 ) |
return rc; |
} |
|
fclose(cf); |
|
return 0; |
} |
#endif /* FONTMAPPER*/ |
/devlist.c
0,0 → 1,59
#include <stdio.h> |
#include <stdlib.h> |
#include "device.h" |
/* |
* linked list routines |
* |
* 1/28/98 g haerr |
* Copyright (c) 1999 Greg Haerr <greg@censoft.com> |
*/ |
|
void * |
GdItemAlloc(unsigned int size) |
{ |
return (void *)calloc(size, 1); |
} |
|
/* insert at tail of list*/ |
void |
GdListAdd(PMWLISTHEAD pHead,PMWLIST pItem) |
{ |
if( pHead->tail) { |
pItem->prev = pHead->tail; |
pHead->tail->next = pItem; |
} else |
pItem->prev = NULL; |
pItem->next = NULL; |
pHead->tail = pItem; |
if( !pHead->head) |
pHead->head = pItem; |
} |
|
/* insert at head of list*/ |
void |
GdListInsert(PMWLISTHEAD pHead,PMWLIST pItem) |
{ |
if( pHead->head) { |
pItem->next = pHead->head; |
pHead->head->prev = pItem; |
} else |
pItem->next = NULL; |
pItem->prev = NULL; |
pHead->head = pItem; |
if( !pHead->head) |
pHead->head = pItem; |
} |
|
void |
GdListRemove(PMWLISTHEAD pHead,PMWLIST pItem) |
{ |
if( pItem->next) |
pItem->next->prev = pItem->prev; |
if( pItem->prev) |
pItem->prev->next = pItem->next; |
if( pHead->head == pItem) |
pHead->head = pItem->next; |
if( pHead->tail == pItem) |
pHead->tail = pItem->prev; |
pItem->next = pItem->prev = NULL; |
} |
/devtimer.c
0,0 → 1,215
/* |
* Copyright (c) 2000 Alex Holden <alex@linuxhacker.org> |
* |
* This file implements the device independant timer functions. |
* |
* When a part of the server wishes to set a timer, it should call the |
* GdAddTimer() function with the timeout parameter set to the number of |
* milliseconds before the timer should activate, the callback argument |
* set to the function which should be called when the timer expires, and |
* the arg argument set to the (void * type) argument which should be supplied |
* to the timer handler function. The GdAddTimer() returns a pointer to the |
* timer structure * which was created (or NULL if the creation failed for |
* some reason). The prototype for the callback function should look like: |
* void callbackfn(void *arg); |
* |
* If a part of the server wishes to destroy a timer before it has expired |
* (it is not necessary to do so after the timer has expired, as the timer |
* structure is automatically destroyed after the callback function is called), |
* it should call the GdDestroyTimer() function with the address of the timer |
* structure (which was returned by GdAddTimer()). |
* |
* If a part of the server wishes to destroy a timer but does not know the |
* address of it's timer structure, it can call GdFindTimer() with the |
* callback argument as a parameter. The argument must be unique to that |
* timer (the address of a structure or function is probably a good choice). |
* This function returns the address of the first timer found with that |
* argument, or NULL if no matching timer was found. |
* |
* The main select() loop needs to be called with a timeout obtained using the |
* GdGetNextTimeout(). GdGetNextTimeout() is called with the event loop |
* timeout in ms, and fills in the specified timeout structure, which should |
* be used as the argument to the select() call. The timeout returned by the |
* GdGetNextTimeout() call is decided by looking through the timer list for |
* the timer with the shortest amount of time remaining, and also at the |
* maximum delay parameter. If there are no timers on the timer list and the |
* timeout argument is 0, it will return FALSE, otherwise it will return TRUE. |
* |
* When the main select() loop times out, the GdTimeout() function should be |
* called. This will go through the timer list and call the callback functions |
* of all timers which have expired, then remove them from the timer list. At |
* the same time, you should check the value of the maximum timeout parameter |
* to see if it has expired (in which case you can then return to the client |
* with a timeout event). This function returns TRUE if the timeout specified in |
* the last GdGetNextTimeout() call has expired, or FALSE otherwise. |
* |
* Note that no guarantees can be made as to when exactly the timer callback |
* will be called as it depends on how often the GdTimeout() function is |
* called and how long any other timeouts in the queue before you take to |
* complete. Especially in the case where the client is linked into the server, |
* the client must call into the server on a regular basis, otherwise the |
* timers may run late. |
*/ |
|
#include <stdio.h> |
#include <unistd.h> |
#include <stdlib.h> |
#include "device.h" |
|
static MWTIMER *timerlist = NULL; |
static struct timeval mainloop_timeout; |
static struct timeval current_time; |
|
static void calculate_timeval(struct timeval *tv, MWTIMEOUT to); |
static signed long time_to_expiry(struct timeval *t); |
|
MWTIMER *GdAddTimer(MWTIMEOUT timeout, MWTIMERCB callback, void *arg) |
{ |
MWTIMER *newtimer; |
|
if(!(newtimer = malloc(sizeof(MWTIMER)))) return NULL; |
|
gettimeofday(¤t_time, NULL); |
|
if(timerlist) timerlist->prev = newtimer; |
|
calculate_timeval(&newtimer->timeout, timeout); |
newtimer->callback = callback; |
newtimer->arg = arg; |
newtimer->next = timerlist; |
newtimer->prev = NULL; |
newtimer->type = MWTIMER_ONESHOT; |
newtimer->period = timeout; |
timerlist = newtimer; |
|
return newtimer; |
} |
|
MWTIMER *GdAddPeriodicTimer(MWTIMEOUT timeout, MWTIMERCB callback, void *arg) |
{ |
MWTIMER *newtimer; |
|
if(!(newtimer = malloc(sizeof(MWTIMER)))) return NULL; |
|
gettimeofday (¤t_time, NULL); |
|
if (timerlist) timerlist->prev = newtimer; |
|
calculate_timeval (&newtimer->timeout, timeout); |
newtimer->callback = callback; |
newtimer->arg = arg; |
newtimer->next = timerlist; |
newtimer->prev = NULL; |
newtimer->type = MWTIMER_PERIODIC; |
newtimer->period = timeout; |
timerlist = newtimer; |
|
return newtimer; |
} |
|
void GdDestroyTimer(MWTIMER *timer) |
{ |
if(timer->next) timer->next->prev = timer->prev; |
if(timer->prev) timer->prev->next = timer->next; |
if(timer == timerlist) { |
if(timer->next) timerlist = timer->next; |
else timerlist = timer->prev; |
} |
free(timer); |
} |
|
MWTIMER *GdFindTimer(void *arg) |
{ |
MWTIMER *t = timerlist; |
|
while(t) { |
if(t->arg == arg) break; |
t = t->next; |
} |
|
return t; |
} |
|
MWBOOL GdGetNextTimeout(struct timeval *tv, MWTIMEOUT timeout) |
{ |
signed long i, lowest_timeout; |
MWTIMER *t = timerlist; |
|
if(!timeout && !timerlist) return FALSE; |
|
gettimeofday(¤t_time, NULL); |
|
if(timeout) { |
calculate_timeval(&mainloop_timeout, timeout); |
lowest_timeout = time_to_expiry(&mainloop_timeout); |
} else { |
lowest_timeout = time_to_expiry(&t->timeout); |
mainloop_timeout.tv_sec = -1; |
t = t->next; |
} |
|
while(t) { |
i = time_to_expiry(&t->timeout); |
if(i < lowest_timeout) lowest_timeout = i; |
t = t->next; |
} |
|
if(lowest_timeout <= 0) { |
tv->tv_sec = 0; |
tv->tv_usec = 0; |
} else { |
tv->tv_sec = lowest_timeout / 1000; |
tv->tv_usec = (lowest_timeout % 1000) * 1000; |
} |
|
return TRUE; |
} |
|
MWBOOL GdTimeout(void) |
{ |
MWTIMER *n, *t = timerlist; |
|
gettimeofday(¤t_time, NULL); |
|
while(t) { |
n = t->next; |
if(time_to_expiry(&t->timeout) <= 0) { |
t->callback(t->arg); |
if (t->type == MWTIMER_ONESHOT) |
{ |
/* One shot timer, is finished delete it now */ |
GdDestroyTimer(t); |
} |
else |
{ |
/* Periodic timer needs to be reset */ |
calculate_timeval (&t->timeout, t->period); |
} |
} |
t = n; |
} |
|
if(mainloop_timeout.tv_sec > 0 || mainloop_timeout.tv_usec > 0) |
if(time_to_expiry(&mainloop_timeout) <= 0) |
return TRUE; |
|
return FALSE; |
} |
|
static void calculate_timeval(struct timeval *tv, MWTIMEOUT to) |
{ |
tv->tv_sec = current_time.tv_sec + (to / 1000); |
tv->tv_usec = current_time.tv_usec + ((to % 1000) * 1000); |
if(tv->tv_usec > 1000000) { |
tv->tv_sec++; |
tv->tv_usec -= 1000000; |
} |
} |
|
static signed long time_to_expiry(struct timeval *t) |
{ |
MWTIMEOUT ret = (((t->tv_sec - current_time.tv_sec) * 1000) + |
((t->tv_usec - current_time.tv_usec) / 1000)); |
|
return ret; |
} |
/devimage.c
0,0 → 1,2460
#define FASTJPEG 1 /* =1 for temp quick jpeg 8bpp display */ |
#ifdef __ECOS |
// Why isn't this handled in the global config file? |
#undef HAVE_MMAP |
#else |
#define HAVE_MMAP 1 /* =1 to use mmap if available */ |
#endif |
|
#if defined(HAVE_FILEIO) /* temp for entire file*/ |
|
/* |
* Copyright (c) 2000, 2001 Greg Haerr <greg@censoft.com> |
* Portions Copyright (c) 2000 Martin Jolicoeur <martinj@visuaide.com> |
* Portions Copyright (c) 2000 Alex Holden <alex@linuxhacker.org> |
* Portions Copyright (c) Independant JPEG group (ijg) |
* |
* Image load/cache/resize/display routines |
* |
* GIF, BMP, JPEG, PPM, PGM, PBM, PNG, and XPM formats are supported. |
* JHC: Instead of working with a file, we work with a buffer |
* (either provided by the user or through mmap). This |
* improves speed, and provides a mechanism by which the |
* client can send image data directly to the engine |
*/ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <unistd.h> |
#include <fcntl.h> |
#include <ctype.h> |
#include <sys/types.h> |
#include <sys/stat.h> |
|
#ifdef HAVE_MMAP |
#include <sys/mman.h> |
#endif |
|
#include "device.h" |
#include "swap.h" |
|
/* cached image list*/ |
typedef struct { |
MWLIST link; /* link list*/ |
int id; /* image id*/ |
PMWIMAGEHDR pimage; /* image data*/ |
PSD psd; /* FIXME shouldn't need this*/ |
} IMAGEITEM, *PIMAGEITEM; |
|
static MWLISTHEAD imagehead; /* global image list*/ |
static int nextimageid = 1; |
|
typedef struct { /* structure for reading images from buffer */ |
void *start; /* The pointer to the beginning of the buffer */ |
int offset; /* The current offset within the buffer */ |
int size; /* The total size of the buffer */ |
} buffer_t; |
|
|
static void ComputePitch(int bpp, int width, int *pitch, int *bytesperpixel); |
#if defined(HAVE_BMP_SUPPORT) |
static int LoadBMP(buffer_t *src, PMWIMAGEHDR pimage); |
#endif |
#if defined(HAVE_JPEG_SUPPORT) |
static int LoadJPEG(buffer_t *src, PMWIMAGEHDR pimage, PSD psd, |
MWBOOL fast_grayscale); |
#endif |
#if defined(HAVE_PNG_SUPPORT) |
static int LoadPNG(buffer_t *src, PMWIMAGEHDR pimage); |
#endif |
#if defined(HAVE_GIF_SUPPORT) |
static int LoadGIF(buffer_t *src, PMWIMAGEHDR pimage); |
#endif |
#if defined(HAVE_PNM_SUPPORT) |
static int LoadPNM(buffer_t *src, PMWIMAGEHDR pimage); |
#endif |
#if defined(HAVE_XPM_SUPPORT) |
static int LoadXPM(buffer_t *src, PMWIMAGEHDR pimage, PSD psd) ; |
#endif |
|
/* |
* Buffered input functions to replace stdio functions |
*/ |
static void |
binit(void *in, int size, buffer_t *dest) |
{ |
dest->start = in; |
dest->offset = 0; |
dest->size = size; |
} |
|
static int |
bseek(buffer_t *buffer, int offset, int whence) |
{ |
int new; |
|
switch(whence) { |
case SEEK_SET: |
if (offset >= buffer->size || offset < 0) |
return(-1); |
buffer->offset = offset; |
return(0); |
|
case SEEK_CUR: |
new = buffer->offset + offset; |
if (new >= buffer->size || new < 0) |
return(-1); |
buffer->offset = new; |
return(0); |
|
case SEEK_END: |
if (offset >= buffer->size || offset > 0) |
return(-1); |
buffer->offset = (buffer->size - 1) - offset; |
return(0); |
|
default: |
return(-1); |
} |
} |
|
static int |
bread(buffer_t *buffer, void *dest, int size) |
{ |
int copysize = size; |
|
if (buffer->offset == buffer->size) |
return(0); |
|
if (buffer->offset + size > buffer->size) |
copysize = (buffer->size - buffer->offset); |
|
memcpy((void *)dest, (void *)(buffer->start + buffer->offset),copysize); |
|
buffer->offset += copysize; |
return(copysize); |
} |
|
static int |
bgetc(buffer_t *buffer) |
{ |
int ch; |
|
if (buffer->offset == buffer->size) |
return(EOF); |
|
ch = *((unsigned char *) (buffer->start + buffer->offset)); |
buffer->offset++; |
return(ch); |
} |
|
static char * |
bgets(buffer_t *buffer, char *dest, int size) |
{ |
int i,o; |
int copysize = size - 1; |
|
if (buffer->offset == buffer->size) |
return(0); |
|
if (buffer->offset + copysize > buffer->size) |
copysize = buffer->size - buffer->offset; |
|
for(o=0, i=buffer->offset; i < buffer->offset + copysize; i++, o++) { |
dest[o] = *((char *) (buffer->start + i)); |
if (dest[o] == '\n') |
break; |
} |
|
buffer->offset = i + 1; |
dest[o + 1] = 0; |
|
return(dest); |
} |
|
static int |
beof(buffer_t *buffer) |
{ |
return (buffer->offset == buffer->size); |
} |
|
/* |
* Image decoding and display |
* NOTE: This routine and APIs will change in subsequent releases. |
* |
* Decodes and loads a graphics file, then resizes to width/height, |
* then displays image at x, y |
* If width/height == -1, don't resize, use image size. |
* Clipping is not currently supported, just stretch/shrink to fit. |
* |
*/ |
|
static int GdDecodeImage(PSD psd, buffer_t *src, int flags); |
|
int |
GdLoadImageFromBuffer(PSD psd, void *buffer, int size, int flags) |
{ |
buffer_t src; |
binit(buffer, size, &src); |
|
return(GdDecodeImage(psd, &src, flags)); |
} |
|
void |
GdDrawImageFromBuffer(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, |
MWCOORD height, void *buffer, int size, int flags) |
{ |
int id; |
buffer_t src; |
|
binit(buffer, size, &src); |
id = GdDecodeImage(psd, &src, flags); |
|
if (id) { |
GdDrawImageToFit(psd, x, y, width, height, id); |
GdFreeImage(id); |
} |
} |
|
void |
GdDrawImageFromFile(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, |
MWCOORD height, char *path, int flags) |
{ |
int id; |
|
id = GdLoadImageFromFile(psd, path, flags); |
if (id) { |
GdDrawImageToFit(psd, x, y, width, height, id); |
GdFreeImage(id); |
} |
} |
|
int |
GdLoadImageFromFile(PSD psd, char *path, int flags) |
{ |
int fd, id; |
struct stat s; |
void *buffer = 0; |
buffer_t src; |
|
fd = open(path, O_RDONLY); |
if (fd <= 0) { |
EPRINTF("GdLoadImageFromFile: can't open image: %s\n", path); |
return(0); |
} |
|
fstat(fd, &s); |
|
#ifdef HAVE_MMAP |
buffer = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
|
if (!buffer) { |
EPRINTF("GdLoadImageFromFile: Couldn't map image %s\n", path); |
close(fd); |
return(0); |
} |
#else |
buffer = malloc(s.st_size); |
if (!buffer) { |
EPRINTF("GdLoadImageFromFile: Couldn't load image %s\n", path); |
close(fd); |
return(0); |
} |
|
if (read(fd, buffer, s.st_size) != s.st_size) { |
EPRINTF("GdLoadImageFromFile: Couldn't load image %s\n", path); |
close(fd); |
return(0); |
} |
#endif |
|
binit(buffer, s.st_size, &src); |
id = GdDecodeImage(psd, &src, flags); |
|
#ifdef HAVE_MMAP |
munmap(buffer, s.st_size); |
#else |
free(buffer); |
#endif |
|
close(fd); |
return(id); |
} |
|
static int |
GdDecodeImage(PSD psd, buffer_t * src, int flags) |
{ |
int loadOK = 0; |
PMWIMAGEHDR pimage; |
PIMAGEITEM pItem; |
|
/* allocate image struct*/ |
pimage = (PMWIMAGEHDR)malloc(sizeof(MWIMAGEHDR)); |
if(!pimage) { |
return 0; |
} |
pimage->imagebits = NULL; |
pimage->palette = NULL; |
pimage->transcolor = -1L; |
|
#if defined(HAVE_BMP_SUPPORT) |
if (loadOK == 0) |
loadOK = LoadBMP(src, pimage); |
#endif |
#if defined(HAVE_GIF_SUPPORT) |
if (loadOK == 0) |
loadOK = LoadGIF(src, pimage); |
#endif |
#if defined(HAVE_JPEG_SUPPORT) |
if (loadOK == 0) |
loadOK = LoadJPEG(src, pimage, psd, flags); |
#endif |
#if defined(HAVE_PNG_SUPPORT) |
if (loadOK == 0) |
loadOK = LoadPNG(src, pimage); |
#endif |
#if defined(HAVE_PNM_SUPPORT) |
if(loadOK == 0) |
loadOK = LoadPNM(src, pimage); |
#endif |
#if defined(HAVE_XPM_SUPPORT) |
if (loadOK == 0) |
loadOK = LoadXPM(src, pimage, psd); |
#endif |
|
if (loadOK == 0) { |
EPRINTF("GdLoadImageFromFile: unknown image type:\n"); |
// EPRINTF("GdLoadImageFromFile: unknown image type: \n", path); |
goto err; /* image loading error*/ |
} |
if (loadOK != 1) |
goto err; /* image loading error*/ |
|
/* allocate id*/ |
pItem = GdItemNew(IMAGEITEM); |
if (!pItem) |
goto err; |
pItem->id = nextimageid++; |
pItem->pimage = pimage; |
pItem->psd = psd; |
GdListAdd(&imagehead, &pItem->link); |
|
return pItem->id; |
|
err: |
free(pimage); |
return 0; /* image loading error*/ |
} |
|
static PIMAGEITEM |
findimage(int id) |
{ |
PMWLIST p; |
PIMAGEITEM pimagelist; |
|
for (p=imagehead.head; p; p=p->next) { |
pimagelist = GdItemAddr(p, IMAGEITEM, link); |
if (pimagelist->id == id) |
return pimagelist; |
} |
return NULL; |
} |
|
void |
GdDrawImageToFit(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, MWCOORD height, |
int id) |
{ |
PIMAGEITEM pItem; |
PMWIMAGEHDR pimage; |
|
pItem = findimage(id); |
if (!pItem) |
return; |
pimage = pItem->pimage; |
|
/* |
* Display image, possibly stretch/shrink to resize |
*/ |
if (height < 0) |
height = pimage->height; |
if (width < 0) |
width = pimage->width; |
|
if (height != pimage->height || width != pimage->width) { |
MWCLIPRECT rcDst; |
MWIMAGEHDR image2; |
|
/* create similar image, different width/height*/ |
|
image2.width = width; |
image2.height = height; |
image2.planes = pimage->planes; |
image2.bpp = pimage->bpp; |
ComputePitch(pimage->bpp, width, &image2.pitch, |
&image2.bytesperpixel); |
image2.compression = pimage->compression; |
image2.palsize = pimage->palsize; |
image2.palette = pimage->palette; /* already allocated*/ |
image2.transcolor = pimage->transcolor; |
if( (image2.imagebits = malloc(image2.pitch*height)) == NULL) { |
EPRINTF("GdDrawImageToFit: no memory\n"); |
return; |
} |
|
rcDst.x = 0; |
rcDst.y = 0; |
rcDst.width = width; |
rcDst.height = height; |
|
/* Stretch full soruce to destination rectangle*/ |
GdStretchImage(pimage, NULL, &image2, &rcDst); |
GdDrawImage(psd, x, y, &image2); |
free(image2.imagebits); |
} else |
GdDrawImage(psd, x, y, pimage); |
} |
|
void |
GdFreeImage(int id) |
{ |
PIMAGEITEM pItem; |
PMWIMAGEHDR pimage; |
|
pItem = findimage(id); |
if (pItem) { |
GdListRemove(&imagehead, &pItem->link); |
pimage = pItem->pimage; |
|
/* delete image bits*/ |
if(pimage->imagebits) |
free(pimage->imagebits); |
if(pimage->palette) |
free(pimage->palette); |
|
free(pimage); |
GdItemFree(pItem); |
} |
} |
|
MWBOOL |
GdGetImageInfo(int id, PMWIMAGEINFO pii) |
{ |
PMWIMAGEHDR pimage; |
PIMAGEITEM pItem; |
int i; |
|
pItem = findimage(id); |
if (!pItem) { |
memset(pii, 0, sizeof(*pii)); |
return FALSE; |
} |
pimage = pItem->pimage; |
pii->id = id; |
pii->width = pimage->width; |
pii->height = pimage->height; |
pii->planes = pimage->planes; |
pii->bpp = pimage->bpp; |
pii->pitch = pimage->pitch; |
pii->bytesperpixel = pimage->bytesperpixel; |
pii->compression = pimage->compression; |
pii->palsize = pimage->palsize; |
if (pimage->palsize) { |
if (pimage->palette) { |
for (i=0; i<pimage->palsize; ++i) |
pii->palette[i] = pimage->palette[i]; |
} else { |
/* FIXME handle jpeg's without palette*/ |
GdGetPalette(pItem->psd, 0, pimage->palsize, |
pii->palette); |
} |
} |
return TRUE; |
} |
|
#define PIX2BYTES(n) (((n)+7)/8) |
/* |
* compute image line size and bytes per pixel |
* from bits per pixel and width |
*/ |
static void |
ComputePitch(int bpp, int width, int *pitch, int *bytesperpixel) |
{ |
int linesize; |
int bytespp = 1; |
|
if(bpp == 1) |
linesize = PIX2BYTES(width); |
else if(bpp <= 4) |
linesize = PIX2BYTES(width<<2); |
else if(bpp <= 8) |
linesize = width; |
else if(bpp <= 16) { |
linesize = width * 2; |
bytespp = 2; |
} else if(bpp <= 24) { |
linesize = width * 3; |
bytespp = 3; |
} else { |
linesize = width * 4; |
bytespp = 4; |
} |
|
/* rows are DWORD right aligned*/ |
*pitch = (linesize + 3) & ~3; |
*bytesperpixel = bytespp; |
} |
|
/* |
* StretchImage - Resize an image |
* |
* Major portions from SDL Simple DirectMedia Layer by Sam Lantinga |
* Copyright (C) 1997, 1998, 1999, 2000 Sam Lantinga <slouken@devolution.com> |
* This a stretch blit implementation based on ideas given to me by |
* Tomasz Cejner - thanks! :) |
*/ |
/* |
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Library General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
|
This library 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 |
Library General Public License for more details. |
|
You should have received a copy of the GNU Library General Public |
License along with this library; if not, write to the Free |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
|
#define DEFINE_COPY_ROW(name, type) \ |
static void name(type *src, int src_w, type *dst, int dst_w) \ |
{ \ |
int i; \ |
int pos, inc; \ |
type pixel = 0; \ |
\ |
pos = 0x10000; \ |
inc = (src_w << 16) / dst_w; \ |
for ( i=dst_w; i>0; --i ) { \ |
while ( pos >= 0x10000L ) { \ |
pixel = *src++; \ |
pos -= 0x10000L; \ |
} \ |
*dst++ = pixel; \ |
pos += inc; \ |
} \ |
} |
|
DEFINE_COPY_ROW(copy_row1, unsigned char) |
DEFINE_COPY_ROW(copy_row2, unsigned short) |
DEFINE_COPY_ROW(copy_row4, unsigned long) |
|
static void copy_row3(unsigned char *src, int src_w, unsigned char *dst, |
int dst_w) |
{ |
int i; |
int pos, inc; |
unsigned char r = 0; |
unsigned char g = 0; |
unsigned char b = 0; |
|
pos = 0x10000; |
inc = (src_w << 16) / dst_w; |
for ( i=dst_w; i>0; --i ) { |
while ( pos >= 0x10000L ) { |
b = *src++; |
g = *src++; |
r = *src++; |
pos -= 0x10000L; |
} |
*dst++ = b; |
*dst++ = g; |
*dst++ = r; |
pos += inc; |
} |
} |
|
/* Perform a stretch blit between two image structs of the same format.*/ |
void |
GdStretchImage(PMWIMAGEHDR src, MWCLIPRECT *srcrect, PMWIMAGEHDR dst, |
MWCLIPRECT *dstrect) |
{ |
int pos, inc; |
int bytesperpixel; |
int dst_maxrow; |
int src_row, dst_row; |
MWUCHAR *srcp = 0; |
MWUCHAR *dstp; |
MWCLIPRECT full_src; |
MWCLIPRECT full_dst; |
|
if ( src->bytesperpixel != dst->bytesperpixel ) { |
EPRINTF("GdStretchImage: bytesperpixel mismatch\n"); |
return; |
} |
|
/* Verify the blit rectangles */ |
if ( srcrect ) { |
if ( (srcrect->x < 0) || (srcrect->y < 0) || |
((srcrect->x+srcrect->width) > src->width) || |
((srcrect->y+srcrect->height) > src->height) ) { |
EPRINTF("GdStretchImage: invalid source rect\n"); |
return; |
} |
} else { |
full_src.x = 0; |
full_src.y = 0; |
full_src.width = src->width; |
full_src.height = src->height; |
srcrect = &full_src; |
} |
if ( dstrect ) { |
/* if stretching to nothing, return*/ |
if (!dstrect->width || !dstrect->height) |
return; |
if ( (dstrect->x < 0) || (dstrect->y < 0) || |
((dstrect->x+dstrect->width) > dst->width) || |
((dstrect->y+dstrect->height) > dst->height) ) { |
EPRINTF("GdStretchImage: invalid dest rect\n"); |
return; |
} |
} else { |
full_dst.x = 0; |
full_dst.y = 0; |
full_dst.width = dst->width; |
full_dst.height = dst->height; |
dstrect = &full_dst; |
} |
|
/* Set up the data... */ |
pos = 0x10000; |
inc = (srcrect->height << 16) / dstrect->height; |
src_row = srcrect->y; |
dst_row = dstrect->y; |
bytesperpixel = dst->bytesperpixel; |
|
/* Perform the stretch blit */ |
for ( dst_maxrow = dst_row+dstrect->height; dst_row<dst_maxrow; |
++dst_row ) { |
dstp = (MWUCHAR *)dst->imagebits + (dst_row*dst->pitch) |
+ (dstrect->x*bytesperpixel); |
while ( pos >= 0x10000L ) { |
srcp = (MWUCHAR *)src->imagebits + (src_row*src->pitch) |
+ (srcrect->x*bytesperpixel); |
++src_row; |
pos -= 0x10000L; |
} |
|
switch (bytesperpixel) { |
case 1: |
copy_row1(srcp, srcrect->width, dstp, dstrect->width); |
break; |
case 2: |
copy_row2((unsigned short *)srcp, srcrect->width, |
(unsigned short *)dstp, dstrect->width); |
break; |
case 3: |
copy_row3(srcp, srcrect->width, dstp, dstrect->width); |
break; |
case 4: |
copy_row4((unsigned long *)srcp, srcrect->width, |
(unsigned long *)dstp, dstrect->width); |
break; |
} |
|
pos += inc; |
} |
} |
|
#if defined(HAVE_FILEIO) && defined(HAVE_JPEG_SUPPORT) |
#include "jpeglib.h" |
/* |
* JPEG decompression routine |
* |
* JPEG support must be enabled (see README.txt in contrib/jpeg) |
* |
* SOME FINE POINTS: (from libjpeg) |
* In the below code, we ignored the return value of jpeg_read_scanlines, |
* which is the number of scanlines actually read. We could get away with |
* this because we asked for only one line at a time and we weren't using |
* a suspending data source. See libjpeg.doc for more info. |
* |
* We cheated a bit by calling alloc_sarray() after jpeg_start_decompress(); |
* we should have done it beforehand to ensure that the space would be |
* counted against the JPEG max_memory setting. In some systems the above |
* code would risk an out-of-memory error. However, in general we don't |
* know the output image dimensions before jpeg_start_decompress(), unless we |
* call jpeg_calc_output_dimensions(). See libjpeg.doc for more about this. |
* |
* Scanlines are returned in the same order as they appear in the JPEG file, |
* which is standardly top-to-bottom. If you must emit data bottom-to-top, |
* you can use one of the virtual arrays provided by the JPEG memory manager |
* to invert the data. See wrbmp.c for an example. |
* |
* As with compression, some operating modes may require temporary files. |
* On some systems you may need to set up a signal handler to ensure that |
* temporary files are deleted if the program is interrupted. See libjpeg.doc. |
*/ |
static int |
LoadJPEG(buffer_t *src, PMWIMAGEHDR pimage, PSD psd, MWBOOL fast_grayscale) |
{ |
int i; |
int ret = 2; /* image load error*/ |
unsigned char magic[4]; |
|
#if FASTJPEG |
extern MWPALENTRY mwstdpal8[256]; |
#else |
MWPALENTRY palette[256]; |
#endif |
|
struct jpeg_source_mgr smgr; |
struct jpeg_decompress_struct cinfo; |
struct jpeg_error_mgr jerr; |
|
static void init_source(j_compress_ptr dinfo) { |
smgr.next_input_byte = src->start; |
smgr.bytes_in_buffer = src->size; |
} |
|
static void fill_input_buffer(j_compress_ptr dinfo) { |
return; |
} |
|
static void skip_input_data(j_compress_ptr dinfo, int num_bytes) { |
if (num_bytes >= src->size) return; |
smgr.next_input_byte += num_bytes; |
smgr.bytes_in_buffer -= num_bytes; |
} |
|
static int resync_to_restart(j_compress_ptr dinfo, int desired ) { |
return(jpeg_resync_to_restart(dinfo, desired)); |
} |
|
static void term_source(j_compress_ptr dinfo) { |
return; |
} |
|
/* first determine if JPEG file since decoder will error if not*/ |
bseek(src, 0, SEEK_SET); |
|
if (!bread(src, magic, 2)) |
return(0); |
|
if (magic[0] != 0xFF || magic[1] != 0xD8) |
return(0); /* not JPEG image*/ |
|
|
bread(src, magic, 4); |
bread(src, magic, 4); |
|
if (strncmp(magic, "JFIF", 4) != 0) |
return(0); /* not JPEG image*/ |
|
bread(src, 0, SEEK_SET); |
pimage->imagebits = NULL; |
pimage->palette = NULL; |
|
/* Step 1: allocate and initialize JPEG decompression object */ |
|
/* We set up the normal JPEG error routines. */ |
cinfo.err = jpeg_std_error (&jerr); |
|
/* Now we can initialize the JPEG decompression object. */ |
jpeg_create_decompress (&cinfo); |
|
|
/* Step 2: Setup the source manager */ |
|
smgr.init_source = init_source; |
smgr.fill_input_buffer = fill_input_buffer; |
smgr.skip_input_data = skip_input_data; |
smgr.resync_to_restart = resync_to_restart; |
smgr.term_source = term_source; |
|
cinfo.src = &smgr; |
|
/* Step 2: specify data source (eg, a file) */ |
/* jpeg_stdio_src (&cinfo, fp); */ |
|
/* Step 3: read file parameters with jpeg_read_header() */ |
jpeg_read_header (&cinfo, TRUE); |
|
/* Step 4: set parameters for decompression */ |
cinfo.out_color_space = fast_grayscale? JCS_GRAYSCALE: JCS_RGB; |
cinfo.quantize_colors = FALSE; |
|
#if FASTJPEG |
goto fastjpeg; |
#endif |
if (!fast_grayscale) |
{ |
if (psd->pixtype == MWPF_PALETTE) |
{ |
fastjpeg: |
cinfo.quantize_colors = TRUE; |
|
#if FASTJPEG |
cinfo.actual_number_of_colors = 256; |
#else |
/* Get system palette */ |
cinfo.actual_number_of_colors = |
GdGetPalette(psd, 0, psd->ncolors, palette); |
#endif |
|
/* Allocate jpeg colormap space */ |
cinfo.colormap = (*cinfo.mem->alloc_sarray) |
((j_common_ptr) &cinfo, JPOOL_IMAGE, |
(JDIMENSION)cinfo.actual_number_of_colors, |
(JDIMENSION)3); |
|
/* Set colormap from system palette */ |
for(i = 0; i < cinfo.actual_number_of_colors; ++i) |
{ |
#if FASTJPEG |
cinfo.colormap[0][i] = mwstdpal8[i].r; |
cinfo.colormap[1][i] = mwstdpal8[i].g; |
cinfo.colormap[2][i] = mwstdpal8[i].b; |
#else |
cinfo.colormap[0][i] = palette[i].r; |
cinfo.colormap[1][i] = palette[i].g; |
cinfo.colormap[2][i] = palette[i].b; |
#endif |
} |
} |
} |
else |
{ |
/* Grayscale output asked */ |
cinfo.quantize_colors = TRUE; |
cinfo.out_color_space = JCS_GRAYSCALE; |
cinfo.desired_number_of_colors = psd->ncolors; |
} |
jpeg_calc_output_dimensions(&cinfo); |
|
pimage->width = cinfo.output_width; |
pimage->height = cinfo.output_height; |
pimage->planes = 1; |
#if FASTJPEG |
pimage->bpp = 8; |
#else |
pimage->bpp = (fast_grayscale || psd->pixtype == MWPF_PALETTE)? |
8: cinfo.output_components*8; |
#endif |
ComputePitch(pimage->bpp, pimage->width, &pimage->pitch, |
&pimage->bytesperpixel); |
pimage->compression = MWIMAGE_RGB; /* RGB not BGR order*/ |
pimage->palsize = (pimage->bpp == 8)? 256: 0; |
pimage->imagebits = malloc(pimage->pitch * pimage->height); |
if(!pimage->imagebits) |
goto err; |
pimage->palette = NULL; |
#if FASTJPEG |
if(pimage->bpp == 8) { |
pimage->palette = malloc(256*sizeof(MWPALENTRY)); |
if(!pimage->palette) |
goto err; |
for (i=0; i<256; ++i) |
pimage->palette[i] = mwstdpal8[i]; |
} |
#endif |
|
/* Step 5: Start decompressor */ |
jpeg_start_decompress (&cinfo); |
|
/* Step 6: while (scan lines remain to be read) */ |
while(cinfo.output_scanline < cinfo.output_height) { |
JSAMPROW rowptr[1]; |
rowptr[0] = (JSAMPROW)(pimage->imagebits + |
cinfo.output_scanline * pimage->pitch); |
jpeg_read_scanlines (&cinfo, rowptr, 1); |
} |
ret = 1; |
|
err: |
/* Step 7: Finish decompression */ |
jpeg_finish_decompress (&cinfo); |
|
/* Step 8: Release JPEG decompression object */ |
jpeg_destroy_decompress (&cinfo); |
|
/* May want to check to see whether any corrupt-data |
* warnings occurred (test whether jerr.pub.num_warnings is nonzero). |
*/ |
return ret; |
} |
#endif /* defined(HAVE_FILEIO) && defined(HAVE_JPEG_SUPPORT)*/ |
|
#if defined(HAVE_FILEIO) && defined(HAVE_PNG_SUPPORT) |
#include "png.h" |
/* png_jmpbuf() macro is not defined prior to libpng-1.0.6*/ |
#ifndef png_jmpbuf |
#define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) |
#endif |
/* |
* Load a PNG file. |
* Currently for simplicity we get the PNG library to convert the file to |
* 24 bit RGB format with no alpha channel information even if we could |
* potentially store the image more efficiently by taking note of the image |
* type and depth and acting accordingly. Similarly, > 8 bits per channel, |
* gamma correction, etc. are not supported. |
*/ |
static int |
LoadPNG(buffer_t * src, PMWIMAGEHDR pimage) |
{ |
unsigned char hdr[8], **rows; |
png_structp state; |
png_infop pnginfo; |
png_uint_32 width, height; |
int bit_depth, colourtype, i; |
|
bseek(src, 0L, 0); |
|
if(bread(src, hdr, 8) != 8) return 0; |
|
if(png_sig_cmp(hdr, 0, 8)) return 0; |
|
if(!(state = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, |
NULL, NULL))) goto nomem; |
|
if(!(pnginfo = png_create_info_struct(state))) { |
png_destroy_read_struct(&state, NULL, NULL); |
goto nomem; |
} |
|
if(setjmp(png_jmpbuf(state))) { |
png_destroy_read_struct(&state, &pnginfo, NULL); |
return 2; |
} |
|
png_init_io(state, fp); |
png_set_sig_bytes(state, 8); |
png_read_info(state, pnginfo); |
png_get_IHDR(state, pnginfo, &width, &height, &bit_depth, &colourtype, |
NULL, NULL, NULL); |
|
pimage->width = width; |
pimage->height = height; |
pimage->bpp = 24; |
pimage->planes = 1; |
ComputePitch(pimage->bpp, pimage->width, &pimage->pitch, |
&pimage->bytesperpixel); |
pimage->compression = MWIMAGE_RGB; |
if(!(pimage->imagebits = malloc(pimage->pitch * pimage->height))) { |
png_destroy_read_struct(&state, &pnginfo, NULL); |
goto nomem; |
} |
if(!(rows = malloc(pimage->height * sizeof(unsigned char *)))) { |
png_destroy_read_struct(&state, &pnginfo, NULL); |
goto nomem; |
} |
for(i = 0; i < pimage->height; i++) |
rows[i] = pimage->imagebits + (i * pimage->pitch); |
|
png_set_expand(state); |
if(bit_depth == 16) |
png_set_strip_16(state); |
if(colourtype & PNG_COLOR_MASK_ALPHA) |
png_set_strip_alpha(state); |
if(colourtype == PNG_COLOR_TYPE_GRAY || |
colourtype == PNG_COLOR_TYPE_GRAY_ALPHA) |
png_set_gray_to_rgb(state); |
|
png_read_image(state, rows); |
|
png_read_end(state, NULL); |
free(rows); |
png_destroy_read_struct(&state, &pnginfo, NULL); |
|
return 1; |
|
nomem: |
EPRINTF("LoadPNG: Out of memory\n"); |
return 2; |
} |
#endif /* defined(HAVE_FILEIO) && defined(HAVE_PNG_SUPPORT)*/ |
|
#if defined(HAVE_FILEIO) && defined(HAVE_BMP_SUPPORT) |
/* BMP stuff*/ |
#define BI_RGB 0L |
#define BI_RLE8 1L |
#define BI_RLE4 2L |
#define BI_BITFIELDS 3L |
|
typedef unsigned char BYTE; |
typedef unsigned short WORD; |
typedef unsigned long DWORD; |
typedef long LONG; |
|
typedef struct { |
/* BITMAPFILEHEADER*/ |
BYTE bfType[2]; |
DWORD bfSize; |
WORD bfReserved1; |
WORD bfReserved2; |
DWORD bfOffBits; |
} BMPFILEHEAD; |
|
#define FILEHEADSIZE 14 |
|
/* windows style*/ |
typedef struct { |
/* BITMAPINFOHEADER*/ |
DWORD BiSize; |
DWORD BiWidth; |
DWORD BiHeight; |
WORD BiPlanes; |
WORD BiBitCount; |
DWORD BiCompression; |
DWORD BiSizeImage; |
DWORD BiXpelsPerMeter; |
DWORD BiYpelsPerMeter; |
DWORD BiClrUsed; |
DWORD BiClrImportant; |
} BMPINFOHEAD; |
|
#define INFOHEADSIZE 40 |
|
/* os/2 style*/ |
typedef struct { |
/* BITMAPCOREHEADER*/ |
DWORD bcSize; |
WORD bcWidth; |
WORD bcHeight; |
WORD bcPlanes; |
WORD bcBitCount; |
} BMPCOREHEAD; |
|
#define COREHEADSIZE 12 |
|
static int DecodeRLE8(MWUCHAR *buf, buffer_t *src); |
static int DecodeRLE4(MWUCHAR *buf, buffer_t *src); |
static void put4(int b); |
|
/* |
* BMP decoding routine |
*/ |
|
/* Changed by JHC to allow a buffer instead of a filename */ |
|
static int |
LoadBMP(buffer_t *src, PMWIMAGEHDR pimage) |
{ |
int h, i, compression; |
int headsize; |
MWUCHAR *imagebits; |
BMPFILEHEAD bmpf; |
BMPINFOHEAD bmpi; |
BMPCOREHEAD bmpc; |
MWUCHAR headbuffer[INFOHEADSIZE]; |
|
bseek(src, 0, SEEK_SET); |
|
pimage->imagebits = NULL; |
pimage->palette = NULL; |
|
/* read BMP file header*/ |
if (bread(src, &headbuffer, FILEHEADSIZE) != FILEHEADSIZE) |
return(0); |
|
bmpf.bfType[0] = headbuffer[0]; |
bmpf.bfType[1] = headbuffer[1]; |
|
/* Is it really a bmp file ? */ |
if (*(WORD*)&bmpf.bfType[0] != wswap(0x4D42)) /* 'BM' */ |
return 0; /* not bmp image*/ |
|
/*bmpf.bfSize = dwswap(dwread(&headbuffer[2]));*/ |
bmpf.bfOffBits = dwswap(dwread(&headbuffer[10])); |
|
/* Read remaining header size */ |
if (bread(src,&headsize,sizeof(DWORD)) != sizeof(DWORD)) |
return 0; /* not bmp image*/ |
headsize = dwswap(headsize); |
|
/* might be windows or os/2 header */ |
if(headsize == COREHEADSIZE) { |
|
/* read os/2 header */ |
if(bread(src, &headbuffer, COREHEADSIZE-sizeof(DWORD)) != |
COREHEADSIZE-sizeof(DWORD)) |
return 0; /* not bmp image*/ |
|
/* Get data */ |
bmpc.bcWidth = wswap(*(WORD*)&headbuffer[0]); |
bmpc.bcHeight = wswap(*(WORD*)&headbuffer[2]); |
bmpc.bcPlanes = wswap(*(WORD*)&headbuffer[4]); |
bmpc.bcBitCount = wswap(*(WORD*)&headbuffer[6]); |
|
pimage->width = (int)bmpc.bcWidth; |
pimage->height = (int)bmpc.bcHeight; |
pimage->bpp = bmpc.bcBitCount; |
if (pimage->bpp <= 8) |
pimage->palsize = 1 << pimage->bpp; |
else pimage->palsize = 0; |
compression = BI_RGB; |
} else { |
/* read windows header */ |
if(bread(src, &headbuffer, INFOHEADSIZE-sizeof(DWORD)) != |
INFOHEADSIZE-sizeof(DWORD)) |
return 0; /* not bmp image*/ |
|
/* Get data */ |
bmpi.BiWidth = dwswap(*(DWORD*)&headbuffer[0]); |
bmpi.BiHeight = dwswap(*(DWORD*)&headbuffer[4]); |
bmpi.BiPlanes = wswap(*(WORD*)&headbuffer[8]); |
bmpi.BiBitCount = wswap(*(WORD*)&headbuffer[10]); |
bmpi.BiCompression = dwswap(*(DWORD*)&headbuffer[12]); |
bmpi.BiSizeImage = dwswap(*(DWORD*)&headbuffer[16]); |
bmpi.BiXpelsPerMeter = dwswap(*(DWORD*)&headbuffer[20]); |
bmpi.BiYpelsPerMeter = dwswap(*(DWORD*)&headbuffer[24]); |
bmpi.BiClrUsed = dwswap(*(DWORD*)&headbuffer[28]); |
bmpi.BiClrImportant = dwswap(*(DWORD*)&headbuffer[32]); |
|
pimage->width = (int)bmpi.BiWidth; |
pimage->height = (int)bmpi.BiHeight; |
pimage->bpp = bmpi.BiBitCount; |
pimage->palsize = (int)bmpi.BiClrUsed; |
if (pimage->palsize > 256) |
pimage->palsize = 0; |
else if(pimage->palsize == 0 && pimage->bpp <= 8) |
pimage->palsize = 1 << pimage->bpp; |
compression = bmpi.BiCompression; |
} |
pimage->compression = MWIMAGE_BGR; /* right side up, BGR order*/ |
pimage->planes = 1; |
|
/* currently only 1, 4, 8 and 24 bpp bitmaps*/ |
if(pimage->bpp > 8 && pimage->bpp != 24) { |
EPRINTF("LoadBMP: image bpp not 1, 4, 8 or 24\n"); |
return 2; /* image loading error*/ |
} |
|
/* compute byte line size and bytes per pixel*/ |
ComputePitch(pimage->bpp, pimage->width, &pimage->pitch, |
&pimage->bytesperpixel); |
|
/* Allocate image */ |
if( (pimage->imagebits = malloc(pimage->pitch*pimage->height)) == NULL) |
goto err; |
if( (pimage->palette = malloc(256*sizeof(MWPALENTRY))) == NULL) |
goto err; |
|
/* get colormap*/ |
if(pimage->bpp <= 8) { |
for(i=0; i<pimage->palsize; i++) { |
pimage->palette[i].b = bgetc(src); |
pimage->palette[i].g = bgetc(src); |
pimage->palette[i].r = bgetc(src); |
if(headsize != COREHEADSIZE) |
bgetc(src); |
} |
} |
|
/* decode image data*/ |
bseek(src, bmpf.bfOffBits, SEEK_SET); |
|
h = pimage->height; |
/* For every row ... */ |
while (--h >= 0) { |
/* turn image rightside up*/ |
imagebits = pimage->imagebits + h*pimage->pitch; |
|
/* Get row data from file */ |
if(compression == BI_RLE8) { |
if(!DecodeRLE8(imagebits, src)) |
break; |
} else if(compression == BI_RLE4) { |
if(!DecodeRLE4(imagebits, src)) |
break; |
} else { |
if(bread(src, imagebits, pimage->pitch) != |
pimage->pitch) |
goto err; |
} |
} |
return 1; /* bmp image ok*/ |
|
err: |
EPRINTF("LoadBMP: image loading error\n"); |
if(pimage->imagebits) |
free(pimage->imagebits); |
if(pimage->palette) |
free(pimage->palette); |
return 2; /* bmp image error*/ |
} |
|
/* |
* Decode one line of RLE8, return 0 when done with all bitmap data |
*/ |
static int |
DecodeRLE8(MWUCHAR *buf, buffer_t *src) |
{ |
int c, n; |
MWUCHAR * p = buf; |
|
for( ;;) { |
switch( n = bgetc(src)) { |
case EOF: |
return( 0); |
case 0: /* 0 = escape*/ |
switch( n = bgetc(src)) { |
case 0: /* 0 0 = end of current scan line*/ |
return( 1); |
case 1: /* 0 1 = end of data*/ |
return( 1); |
case 2: /* 0 2 xx yy delta mode NOT SUPPORTED*/ |
(void)bgetc(src); |
(void)bgetc(src); |
continue; |
default: /* 0 3..255 xx nn uncompressed data*/ |
for( c=0; c<n; c++) |
*p++ = bgetc(src); |
if( n & 1) |
(void)bgetc(src); |
continue; |
} |
default: |
c = bgetc(src); |
while( n--) |
*p++ = c; |
continue; |
} |
} |
} |
|
/* |
* Decode one line of RLE4, return 0 when done with all bitmap data |
*/ |
static MWUCHAR *p; |
static int once; |
|
static void |
put4(int b) |
{ |
static int last; |
|
last = (last << 4) | b; |
if( ++once == 2) { |
*p++ = last; |
once = 0; |
} |
} |
|
static int |
DecodeRLE4(MWUCHAR *buf, buffer_t *src) |
{ |
int c, n, c1, c2; |
|
p = buf; |
once = 0; |
c1 = 0; |
|
for( ;;) { |
switch( n = bgetc(src)) { |
case EOF: |
return( 0); |
case 0: /* 0 = escape*/ |
switch( n = bgetc(src)) { |
case 0: /* 0 0 = end of current scan line*/ |
if( once) |
put4( 0); |
return( 1); |
case 1: /* 0 1 = end of data*/ |
if( once) |
put4( 0); |
return( 1); |
case 2: /* 0 2 xx yy delta mode NOT SUPPORTED*/ |
(void)bgetc(src); |
(void)bgetc(src); |
continue; |
default: /* 0 3..255 xx nn uncompressed data*/ |
c2 = (n+3) & ~3; |
for( c=0; c<c2; c++) { |
if( (c & 1) == 0) |
c1 = bgetc(src); |
if( c < n) |
put4( (c1 >> 4) & 0x0f); |
c1 <<= 4; |
} |
continue; |
} |
default: |
c = bgetc(src); |
c1 = (c >> 4) & 0x0f; |
c2 = c & 0x0f; |
for( c=0; c<n; c++) |
put4( (c&1)? c2: c1); |
continue; |
} |
} |
} |
#endif /* defined(HAVE_FILEIO) && defined(HAVE_BMP_SUPPORT)*/ |
|
#if 0 |
void print_image(PMWIMAGEHDR image) |
{ |
int i; |
|
DPRINTF("Image:\n\n"); |
DPRINTF("height: %d\n", image->height); |
DPRINTF("width: %d\n", image->width); |
DPRINTF("planes: %d\n", image->planes); |
DPRINTF("bpp: %d\n", image->bpp); |
DPRINTF("compression: %d\n", image->compression); |
DPRINTF("palsize: %d\n", image->palsize); |
|
for (i=0;i<image->palsize;i++) |
DPRINTF("palette: %d, %d, %d\n", image->palette[i].r, |
image->palette[i].g, image->palette[i].b); |
|
for(i=0;i<(image->width*image->height);i++) |
DPRINTF("imagebits: %d\n", image->imagebits[i]); |
} |
#endif |
|
#if defined(HAVE_FILEIO) && defined(HAVE_GIF_SUPPORT) |
/* Code for GIF decoding has been adapted from XPaint: */ |
/* +-------------------------------------------------------------------+ */ |
/* | Copyright 1990, 1991, 1993 David Koblas. | */ |
/* | Copyright 1996 Torsten Martinsen. | */ |
/* | Permission to use, copy, modify, and distribute this software | */ |
/* | and its documentation for any purpose and without fee is hereby | */ |
/* | granted, provided that the above copyright notice appear in all | */ |
/* | copies and that both that copyright notice and this permission | */ |
/* | notice appear in supporting documentation. This software is | */ |
/* | provided "as is" without express or implied warranty. | */ |
/* +-------------------------------------------------------------------+ */ |
/* Portions Copyright (C) 1999 Sam Lantinga*/ |
/* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */ |
/* |
This library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Library General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
|
This library 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 |
Library General Public License for more details. |
|
You should have received a copy of the GNU Library General Public |
License along with this library; if not, write to the Free |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
/* GIF stuff*/ |
/* |
* GIF decoding routine |
*/ |
#define MAXCOLORMAPSIZE 256 |
#define MAX_LWZ_BITS 12 |
#define INTERLACE 0x40 |
#define LOCALCOLORMAP 0x80 |
|
#define CM_RED 0 |
#define CM_GREEN 1 |
#define CM_BLUE 2 |
|
#define BitSet(byte, bit) (((byte) & (bit)) == (bit)) |
#define ReadOK(src,buffer,len) bread(src, buffer, len) |
#define LM_to_uint(a,b) (((b)<<8)|(a)) |
|
struct { |
unsigned int Width; |
unsigned int Height; |
unsigned char ColorMap[3][MAXCOLORMAPSIZE]; |
unsigned int BitPixel; |
unsigned int ColorResolution; |
unsigned int Background; |
unsigned int AspectRatio; |
int GrayScale; |
} GifScreen; |
|
static struct { |
int transparent; |
int delayTime; |
int inputFlag; |
int disposal; |
} Gif89; |
|
static int ReadColorMap(buffer_t *src, int number, |
unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag); |
static int DoExtension(buffer_t *src, int label); |
static int GetDataBlock(buffer_t *src, unsigned char *buf); |
static int GetCode(buffer_t *src, int code_size, int flag); |
static int LWZReadByte(buffer_t *src, int flag, int input_code_size); |
static int ReadImage(buffer_t *src, PMWIMAGEHDR pimage, int len, int height, int, |
unsigned char cmap[3][MAXCOLORMAPSIZE], |
int gray, int interlace, int ignore); |
|
static int |
LoadGIF(buffer_t *src, PMWIMAGEHDR pimage) |
{ |
unsigned char buf[16]; |
unsigned char c; |
unsigned char localColorMap[3][MAXCOLORMAPSIZE]; |
int grayScale; |
int useGlobalColormap; |
int bitPixel; |
int imageCount = 0; |
char version[4]; |
int imageNumber = 1; |
int ok = 0; |
|
bseek(src, 0, SEEK_SET); |
|
pimage->imagebits = NULL; |
pimage->palette = NULL; |
|
if (!ReadOK(src, buf, 6)) |
return 0; /* not gif image*/ |
if (strncmp((char *) buf, "GIF", 3) != 0) |
return 0; |
strncpy(version, (char *) buf + 3, 3); |
version[3] = '\0'; |
|
if (strcmp(version, "87a") != 0 && strcmp(version, "89a") != 0) { |
EPRINTF("LoadGIF: GIF version number not 87a or 89a\n"); |
return 2; /* image loading error*/ |
} |
Gif89.transparent = -1; |
Gif89.delayTime = -1; |
Gif89.inputFlag = -1; |
Gif89.disposal = 0; |
|
if (!ReadOK(src, buf, 7)) { |
EPRINTF("LoadGIF: bad screen descriptor\n"); |
return 2; /* image loading error*/ |
} |
GifScreen.Width = LM_to_uint(buf[0], buf[1]); |
GifScreen.Height = LM_to_uint(buf[2], buf[3]); |
GifScreen.BitPixel = 2 << (buf[4] & 0x07); |
GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1); |
GifScreen.Background = buf[5]; |
GifScreen.AspectRatio = buf[6]; |
|
if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */ |
if (ReadColorMap(src, GifScreen.BitPixel, GifScreen.ColorMap, |
&GifScreen.GrayScale)) { |
EPRINTF("LoadGIF: bad global colormap\n"); |
return 2; /* image loading error*/ |
} |
} |
|
do { |
if (!ReadOK(src, &c, 1)) { |
EPRINTF("LoadGIF: EOF on image data\n"); |
goto done; |
} |
if (c == ';') { /* GIF terminator */ |
if (imageCount < imageNumber) { |
EPRINTF("LoadGIF: no image %d of %d\n", imageNumber,imageCount); |
goto done; |
} |
} |
if (c == '!') { /* Extension */ |
if (!ReadOK(src, &c, 1)) { |
EPRINTF("LoadGIF: EOF on extension function code\n"); |
goto done; |
} |
DoExtension(src, c); |
continue; |
} |
if (c != ',') { /* Not a valid start character */ |
continue; |
} |
++imageCount; |
|
if (!ReadOK(src, buf, 9)) { |
EPRINTF("LoadGIF: bad image size\n"); |
goto done; |
} |
useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP); |
|
bitPixel = 1 << ((buf[8] & 0x07) + 1); |
|
if (!useGlobalColormap) { |
if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) { |
EPRINTF("LoadGIF: bad local colormap\n"); |
goto done; |
} |
ok = ReadImage(src, pimage, LM_to_uint(buf[4], buf[5]), |
LM_to_uint(buf[6], buf[7]), |
bitPixel, localColorMap, grayScale, |
BitSet(buf[8], INTERLACE), |
imageCount != imageNumber); |
} else { |
ok = ReadImage(src, pimage, LM_to_uint(buf[4], buf[5]), |
LM_to_uint(buf[6], buf[7]), |
GifScreen.BitPixel, GifScreen.ColorMap, |
GifScreen.GrayScale, BitSet(buf[8], INTERLACE), |
imageCount != imageNumber); |
} |
} while (ok == 0); |
|
/* set transparent color, if any*/ |
pimage->transcolor = Gif89.transparent; |
|
if (ok) |
return 1; /* image load ok*/ |
|
done: |
if (pimage->imagebits) |
free(pimage->imagebits); |
if (pimage->palette) |
free(pimage->palette); |
return 2; /* image load error*/ |
} |
|
static int |
ReadColorMap(buffer_t *src, int number, unsigned char buffer[3][MAXCOLORMAPSIZE], |
int *gray) |
{ |
int i; |
unsigned char rgb[3]; |
int flag; |
|
flag = TRUE; |
|
for (i = 0; i < number; ++i) { |
if (!ReadOK(src, rgb, sizeof(rgb))) |
return 1; |
buffer[CM_RED][i] = rgb[0]; |
buffer[CM_GREEN][i] = rgb[1]; |
buffer[CM_BLUE][i] = rgb[2]; |
flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]); |
} |
|
#if 0 |
if (flag) |
*gray = (number == 2) ? PBM_TYPE : PGM_TYPE; |
else |
*gray = PPM_TYPE; |
#else |
*gray = 0; |
#endif |
|
return FALSE; |
} |
|
static int |
DoExtension(buffer_t *src, int label) |
{ |
static unsigned char buf[256]; |
|
switch (label) { |
case 0x01: /* Plain Text Extension */ |
break; |
case 0xff: /* Application Extension */ |
break; |
case 0xfe: /* Comment Extension */ |
while (GetDataBlock(src, (unsigned char *) buf) != 0); |
return FALSE; |
case 0xf9: /* Graphic Control Extension */ |
GetDataBlock(src, (unsigned char *) buf); |
Gif89.disposal = (buf[0] >> 2) & 0x7; |
Gif89.inputFlag = (buf[0] >> 1) & 0x1; |
Gif89.delayTime = LM_to_uint(buf[1], buf[2]); |
if ((buf[0] & 0x1) != 0) |
Gif89.transparent = buf[3]; |
|
while (GetDataBlock(src, (unsigned char *) buf) != 0); |
return FALSE; |
default: |
break; |
} |
|
while (GetDataBlock(src, (unsigned char *) buf) != 0); |
|
return FALSE; |
} |
|
static int ZeroDataBlock = FALSE; |
|
static int |
GetDataBlock(buffer_t *src, unsigned char *buf) |
{ |
unsigned char count; |
|
if (!ReadOK(src, &count, 1)) |
return -1; |
ZeroDataBlock = count == 0; |
|
if ((count != 0) && (!ReadOK(src, buf, count))) |
return -1; |
return count; |
} |
|
static int |
GetCode(buffer_t *src, int code_size, int flag) |
{ |
static unsigned char buf[280]; |
static int curbit, lastbit, done, last_byte; |
int i, j, ret; |
unsigned char count; |
|
if (flag) { |
curbit = 0; |
lastbit = 0; |
done = FALSE; |
return 0; |
} |
if ((curbit + code_size) >= lastbit) { |
if (done) { |
if (curbit >= lastbit) |
EPRINTF("LoadGIF: bad decode\n"); |
return -1; |
} |
buf[0] = buf[last_byte - 2]; |
buf[1] = buf[last_byte - 1]; |
|
if ((count = GetDataBlock(src, &buf[2])) == 0) |
done = TRUE; |
|
last_byte = 2 + count; |
curbit = (curbit - lastbit) + 16; |
lastbit = (2 + count) * 8; |
} |
ret = 0; |
for (i = curbit, j = 0; j < code_size; ++i, ++j) |
ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j; |
|
curbit += code_size; |
|
return ret; |
} |
|
static int |
LWZReadByte(buffer_t *src, int flag, int input_code_size) |
{ |
int code, incode; |
register int i; |
static int fresh = FALSE; |
static int code_size, set_code_size; |
static int max_code, max_code_size; |
static int firstcode, oldcode; |
static int clear_code, end_code; |
static int table[2][(1 << MAX_LWZ_BITS)]; |
static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp; |
|
if (flag) { |
set_code_size = input_code_size; |
code_size = set_code_size + 1; |
clear_code = 1 << set_code_size; |
end_code = clear_code + 1; |
max_code_size = 2 * clear_code; |
max_code = clear_code + 2; |
|
GetCode(src, 0, TRUE); |
|
fresh = TRUE; |
|
for (i = 0; i < clear_code; ++i) { |
table[0][i] = 0; |
table[1][i] = i; |
} |
for (; i < (1 << MAX_LWZ_BITS); ++i) |
table[0][i] = table[1][0] = 0; |
|
sp = stack; |
|
return 0; |
} else if (fresh) { |
fresh = FALSE; |
do { |
firstcode = oldcode = GetCode(src, code_size, FALSE); |
} while (firstcode == clear_code); |
return firstcode; |
} |
if (sp > stack) |
return *--sp; |
|
while ((code = GetCode(src, code_size, FALSE)) >= 0) { |
if (code == clear_code) { |
for (i = 0; i < clear_code; ++i) { |
table[0][i] = 0; |
table[1][i] = i; |
} |
for (; i < (1 << MAX_LWZ_BITS); ++i) |
table[0][i] = table[1][i] = 0; |
code_size = set_code_size + 1; |
max_code_size = 2 * clear_code; |
max_code = clear_code + 2; |
sp = stack; |
firstcode = oldcode = GetCode(src, code_size, FALSE); |
return firstcode; |
} else if (code == end_code) { |
int count; |
unsigned char buf[260]; |
|
if (ZeroDataBlock) |
return -2; |
|
while ((count = GetDataBlock(src, buf)) > 0); |
|
if (count != 0) { |
/* |
* EPRINTF("missing EOD in data stream (common occurence)"); |
*/ |
} |
return -2; |
} |
incode = code; |
|
if (code >= max_code) { |
*sp++ = firstcode; |
code = oldcode; |
} |
while (code >= clear_code) { |
*sp++ = table[1][code]; |
if (code == table[0][code]) |
EPRINTF("LoadGIF: circular table entry\n"); |
code = table[0][code]; |
} |
|
*sp++ = firstcode = table[1][code]; |
|
if ((code = max_code) < (1 << MAX_LWZ_BITS)) { |
table[0][code] = oldcode; |
table[1][code] = firstcode; |
++max_code; |
if ((max_code >= max_code_size) && |
(max_code_size < (1 << MAX_LWZ_BITS))) { |
max_code_size *= 2; |
++code_size; |
} |
} |
oldcode = incode; |
|
if (sp > stack) |
return *--sp; |
} |
return code; |
} |
|
static int |
ReadImage(buffer_t* src, PMWIMAGEHDR pimage, int len, int height, int cmapSize, |
unsigned char cmap[3][MAXCOLORMAPSIZE], |
int gray, int interlace, int ignore) |
{ |
unsigned char c; |
int i, v; |
int xpos = 0, ypos = 0, pass = 0; |
|
/* |
* Initialize the compression routines |
*/ |
if (!ReadOK(src, &c, 1)) { |
EPRINTF("LoadGIF: EOF on image data\n"); |
return 0; |
} |
if (LWZReadByte(src, TRUE, c) < 0) { |
EPRINTF("LoadGIF: error reading image\n"); |
return 0; |
} |
|
/* |
* If this is an "uninteresting picture" ignore it. |
*/ |
if (ignore) { |
while (LWZReadByte(src, FALSE, c) >= 0); |
return 0; |
} |
/*image = ImageNewCmap(len, height, cmapSize);*/ |
pimage->width = len; |
pimage->height = height; |
pimage->planes = 1; |
pimage->bpp = 8; |
ComputePitch(8, len, &pimage->pitch, &pimage->bytesperpixel); |
pimage->compression = 0; |
pimage->palsize = cmapSize; |
pimage->palette = malloc(256*sizeof(MWPALENTRY)); |
pimage->imagebits = malloc(height*pimage->pitch); |
if(!pimage->imagebits || !pimage->palette) |
return 0; |
|
for (i = 0; i < cmapSize; i++) { |
/*ImageSetCmap(image, i, cmap[CM_RED][i], |
cmap[CM_GREEN][i], cmap[CM_BLUE][i]);*/ |
pimage->palette[i].r = cmap[CM_RED][i]; |
pimage->palette[i].g = cmap[CM_GREEN][i]; |
pimage->palette[i].b = cmap[CM_BLUE][i]; |
} |
|
while ((v = LWZReadByte(src, FALSE, c)) >= 0) { |
pimage->imagebits[ypos * pimage->pitch + xpos] = v; |
|
++xpos; |
if (xpos == len) { |
xpos = 0; |
if (interlace) { |
switch (pass) { |
case 0: |
case 1: |
ypos += 8; |
break; |
case 2: |
ypos += 4; |
break; |
case 3: |
ypos += 2; |
break; |
} |
|
if (ypos >= height) { |
++pass; |
switch (pass) { |
case 1: |
ypos = 4; |
break; |
case 2: |
ypos = 2; |
break; |
case 3: |
ypos = 1; |
break; |
default: |
goto fini; |
} |
} |
} else { |
++ypos; |
} |
} |
if (ypos >= height) |
break; |
} |
|
fini: |
return 1; |
} |
#endif /* defined(HAVE_FILEIO) && defined(HAVE_GIF_SUPPORT)*/ |
|
#if defined(HAVE_FILEIO) && defined(HAVE_PNM_SUPPORT) |
enum { |
PNM_TYPE_NOTPNM, |
PNM_TYPE_PBM, |
PNM_TYPE_PGM, |
PNM_TYPE_PPM |
}; |
static int LoadPNM(buffer_t *src, PMWIMAGEHDR pimage) |
{ |
char buf[256], *p; |
int type = PNM_TYPE_NOTPNM, binary = 0, gothdrs = 0, scale = 0; |
int ch, x = 0, y = 0, i, n, mask, col1, col2, col3; |
|
bseek(src, 0L, 0); |
|
if(!bgets(src,buf, 4)) return 0; |
|
if(!strcmp("P1\n", buf)) type = PNM_TYPE_PBM; |
else if(!strcmp("P2\n", buf)) type = PNM_TYPE_PGM; |
else if(!strcmp("P3\n", buf)) type = PNM_TYPE_PPM; |
else if(!strcmp("P4\n", buf)) { |
type = PNM_TYPE_PBM; |
binary = 1; |
} |
else if(!strcmp("P5\n", buf)) { |
type = PNM_TYPE_PGM; |
binary = 1; |
} |
else if(!strcmp("P6\n", buf)) { |
type = PNM_TYPE_PPM; |
binary = 1; |
} |
|
if(type == PNM_TYPE_NOTPNM) return 0; |
|
n = 0; |
while((p = bgets(src, buf, 256))) { |
if(*buf == '#') continue; |
if(type == PNM_TYPE_PBM) { |
if(sscanf(buf, "%i %i", &pimage->width, |
&pimage->height) == 2) { |
pimage->bpp = 1; |
gothdrs = 1; |
if(!(pimage->palette = malloc( |
sizeof(MWPALENTRY) * 2))) { |
EPRINTF("Out of memory\n"); |
return 2; |
} |
pimage->palsize = 2; |
pimage->palette[0].r = 0xff; |
pimage->palette[0].g = 0xff; |
pimage->palette[0].b = 0xff; |
pimage->palette[1].r = 0; |
pimage->palette[1].g = 0; |
pimage->palette[1].b = 0; |
} |
break; |
} |
if((type == PNM_TYPE_PGM) || (type == PNM_TYPE_PPM)) { |
if(!n++) { |
if(sscanf(buf, "%i %i", &pimage->width, |
&pimage->height) != 2) break; |
} else { |
if(sscanf(buf, "%i", &i) != 1) break; |
pimage->bpp = 24; |
if(i > 255) { |
EPRINTF("LoadPNM: PPM files must be " |
"24bpp\n"); |
return 2; |
} |
for(scale = 7, n = 2; scale; scale--, n *= 2) |
if(i < n) break; |
gothdrs = 1; |
break; |
} |
} |
} |
|
if(!gothdrs) { |
EPRINTF("LoadPNM: bad image headers\n"); |
if(pimage->palette) free(pimage->palette); |
return 2; |
} |
|
pimage->planes = 1; |
ComputePitch(pimage->bpp, pimage->width, &pimage->pitch, |
&pimage->bytesperpixel); |
pimage->compression = MWIMAGE_RGB; |
if(!(pimage->imagebits = malloc(pimage->pitch * pimage->height))) { |
EPRINTF("LoadPNM: couldn't allocate memory for image\n"); |
if(pimage->palette) free(pimage->palette); |
return 2; |
} |
|
p = pimage->imagebits; |
|
if(type == PNM_TYPE_PBM) { |
if(binary) { |
x = 0; |
y = 0; |
while((ch = bgetc(src)) != EOF) { |
for(i = 0; i < 8; i++) { |
mask = 0x80 >> i; |
if(ch & mask) *p |= mask; |
else *p &= ~mask; |
if(++x == pimage->width) { |
if(++y == pimage->height) |
return 1; |
p = pimage->imagebits - 1 + |
(y * pimage->pitch); |
x = 0; |
break; |
} |
} |
p++; |
} |
} else { |
n = 0; |
while((ch = bgetc(src)) != EOF) { |
if(isspace(ch)) continue; |
mask = 0x80 >> n; |
if(ch == '1') *p |= mask; |
else if(ch == '0') *p &= ~mask; |
else goto baddata; |
if(++n == 8) { |
n = 0; |
p++; |
} |
if(++x == pimage->width) { |
if(++y == pimage->height) |
return 1; |
p = pimage->imagebits + |
(y * pimage->pitch); |
n = 0; |
x = 0; |
} |
} |
} |
} else { |
while(1) { |
if(type == PNM_TYPE_PGM) { |
if(binary) { |
if((ch = bgetc(src)) == EOF) |
goto baddata; |
} else { |
// if(fscanf(fp, "%i", &ch) != 1) |
goto baddata; |
} |
*p++ = ch << scale; |
*p++ = ch << scale; |
*p++ = ch << scale; |
} else { |
if(binary) { |
if(((col1 = bgetc(src)) == EOF) || |
((col2 = bgetc(src)) == EOF) || |
((col3 = bgetc(src)) == EOF)) |
goto baddata; |
} else { |
// if(fscanf(fp, "%i %i %i", &col1, &col2, |
// &col3) != 3) |
goto baddata; |
} |
*p++ = col1 << scale; |
*p++ = col2 << scale; |
*p++ = col3 << scale; |
} |
if(++x == pimage->width) { |
if(++y == pimage->height) return 1; |
p = pimage->imagebits + (y * pimage->pitch); |
x = 0; |
} |
} |
} |
|
baddata: |
EPRINTF("LoadPNM: bad image data\n"); |
free(pimage->imagebits); |
if(pimage->palette) free(pimage->palette); |
return 2; |
} |
#endif /* defined(HAVE_FILEIO) && defined(HAVE_PNM_SUPPORT) */ |
|
#if defined(HAVE_FILEIO) && defined(HAVE_XPM_SUPPORT) |
struct xpm_cmap { |
char mapstr[3]; |
long palette_entry; |
long color; |
struct xpm_cmap *next; |
}; |
|
|
static long XPM_parse_color(char *color) |
{ |
/* This will parse the string into a color value of some sort */ |
|
if (color[0] != '#') |
{ |
if (!strcmp(color, "None")) |
return(-1); /* Transparent */ |
else |
return(0); /* If its an X color, then we bail */ |
} |
else |
{ |
/* This is ugly! */ |
|
char *sptr = color + 1; |
char rstr[5], gstr[5], bstr[5]; |
long r,g,b; |
|
switch(strlen(sptr)) |
{ |
case 6: |
return(strtol(sptr, NULL, 16)); |
|
case 9: /* RRRGGGBBB */ |
strncpy(rstr, sptr, 3); |
strncpy(gstr, sptr + 3, 3); |
strncpy(bstr, sptr + 6, 3); |
|
rstr[3] = 0; |
gstr[3] = 0; |
bstr[3] = 0; |
|
r = strtol(rstr, NULL, 16) >> 4; |
g = strtol(gstr, NULL, 16) >> 4; |
b = strtol(bstr, NULL, 16) >> 4; |
|
return( (long) ( r << 16 | g << 8 | b)); |
|
case 12: |
strncpy(rstr, sptr, 4); |
strncpy(gstr, sptr + 4, 4); |
strncpy(bstr, sptr + 8, 4); |
|
rstr[4] = 0; |
gstr[4] = 0; |
bstr[4] = 0; |
|
r = strtol(rstr, NULL, 16) >> 8; |
g = strtol(gstr, NULL, 16) >> 8; |
b = strtol(bstr, NULL, 16) >> 8; |
|
return( (long) ( (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF))); |
} |
} |
|
return(0); |
} |
|
/* A series of status indicators that let us know whats going on */ |
/* It could be an enum if you want */ |
|
#define LOAD_HEADER 1 |
#define LOAD_COLORS 2 |
#define LOAD_PALETTE 3 |
#define LOAD_PIXELS 4 |
#define LOAD_DONE 5 |
|
/* The magic that "should" indicate an XPM (does it really?) */ |
#define XPM_MAGIC "/* XPM */" |
#define XPM_TRANSCOLOR 0x01000000 |
|
static int LoadXPM(buffer_t *src, PMWIMAGEHDR pimage, PSD psd) |
{ |
struct xpm_cmap *colorheap = 0; /* A "heap" of color structs */ |
struct xpm_cmap *colormap[256]; /* A quick hash of 256 spots for colors */ |
|
unsigned char *imageptr = 0; |
|
MWSCREENINFO sinfo; |
|
char xline[300]; |
char dline[300]; |
|
char *c; |
int a; |
|
int col, row, colors, cpp; |
int in_color = 0; |
int read_xline = 0; |
|
int status = LOAD_HEADER; |
|
/* Very first thing, get the screen info */ |
GdGetScreenInfo(psd, &sinfo); |
|
for(a = 0; a < 256; a++) |
colormap[a] = 0; |
|
pimage->imagebits = NULL; |
pimage->palette = NULL; |
|
/* Start over at the beginning with the file */ |
bseek(src, 0, SEEK_SET); |
|
bgets(src, xline, 300); |
|
/* Chop the EOL */ |
xline[strlen(xline) - 1] = 0; |
|
/* Check the magic */ |
if (strncmp(xline, XPM_MAGIC, sizeof(XPM_MAGIC))) return(0); |
|
while(!beof(src)) |
{ |
/* Get the next line from the file */ |
bgets(src,xline, 300); |
xline[strlen(xline) - 1] = 0; |
|
/* Check it out */ |
if (xline[0] == '/' && xline[1] == '*') /* Comment */ |
continue; |
|
if (xline[0] != '\"') |
continue; |
|
/* remove the quotes from the line */ |
for(c = xline + 1, a = 0; *c != '\"' && *c != 0; c++, a++) |
dline[a] = *c; |
|
dline[a] = 0; |
|
/* Is it the header? */ |
if (status == LOAD_HEADER) |
{ |
sscanf(dline, "%i %i %i %i", &col, &row, &colors, &cpp); |
|
pimage->width = col; |
pimage->height = row; |
pimage->planes = 1; |
|
if (sinfo.bpp <= 8) |
{ |
pimage->bpp = sinfo.bpp; |
pimage->compression = 0; |
pimage->transcolor = -1; |
} |
else |
{ |
pimage->bpp = 32; |
pimage->transcolor = XPM_TRANSCOLOR; |
pimage->compression = MWIMAGE_BGR; |
} |
|
pimage->palsize = colors; |
|
ComputePitch(pimage->bpp, col, &pimage->pitch, &pimage->bytesperpixel); |
|
pimage->imagebits = malloc(pimage->pitch * pimage->height); |
imageptr = (unsigned char *) pimage->imagebits; |
|
/* Allocate enough room for all the colors */ |
colorheap = (struct xpm_cmap *) malloc(colors * sizeof(struct xpm_cmap)); |
|
/* Allocate the palette space (if required) */ |
|
if (sinfo.bpp <= 8) |
pimage->palette = malloc(256*sizeof(MWPALENTRY)); |
|
if (!colorheap) |
{ |
EPRINTF("Couldn't allocate any memory for the colors\n"); |
return(0); |
} |
|
status = LOAD_COLORS; |
in_color = 0; |
continue; |
} |
|
/* Are we in load colors? */ |
if (status == LOAD_COLORS) |
{ |
struct xpm_cmap *n; |
|
char tstr[5]; |
char cstr[256]; |
|
unsigned char m; |
|
c = dline; |
|
/* Go at at least 1 charater, and then count until we have |
two spaces in a row */ |
|
strncpy(tstr, c, cpp); |
|
c += cpp; |
for(; *c == '\t' || *c == ' '; c++); /* Skip over whitespace */ |
|
/* FIXME: We assume that a 'c' follows. What if it doesn't? */ |
c +=2; |
|
tstr[cpp] = 0; |
|
/* Now we put it into the array for easy lookup */ |
/* We base it off the first charater, even though */ |
/* there may be up to 4 */ |
|
m = tstr[0]; |
|
if (colormap[m]) |
{ |
n = colormap[m]; |
|
while(n->next) n = n->next; |
n->next = &colorheap[in_color]; |
n = n->next; |
} |
else |
{ |
colormap[m] = &colorheap[in_color]; |
n = colormap[m]; |
} |
|
n->next = 0; |
|
/* Record the string */ |
strncpy(n->mapstr, tstr, cpp); |
n->mapstr[cpp] = 0; |
|
/* Now record the palette entry */ |
n->palette_entry = (long) in_color; |
|
/* This is the color */ |
sscanf(c, "%65535s", cstr); |
|
/* Turn it into a real value */ |
n->color = XPM_parse_color(cstr); |
|
/* If we are in palette mode, then we need to */ |
/* load the palette (duh..) */ |
|
if (sinfo.bpp <= 8) |
{ |
if (n->color == -1) |
{ |
pimage->transcolor = in_color; |
n->color = -1; |
} |
|
pimage->palette[in_color].r = (n->color >> 16) & 0xFF; |
pimage->palette[in_color].g = (n->color >> 8) & 0xFF; |
pimage->palette[in_color].b = n->color & 0xFF; |
} |
else |
{ |
if (n->color == -1) |
n->color = XPM_TRANSCOLOR; |
} |
|
if (++in_color == colors) |
{ |
read_xline = 0; |
status = LOAD_PIXELS; |
} |
|
continue; |
} |
|
if (status == LOAD_PIXELS) |
{ |
int bytecount = 0; |
int bitcount = 0; |
long dwordcolor = 0; |
int i; |
char pxlstr[3]; |
|
c = dline; |
|
while(*c) |
{ |
unsigned char z = 0; |
|
if (cpp == 1) |
{ |
z = *c; |
|
if (!colormap[z]) |
{ |
EPRINTF("No color entry for (%c)\n", z); |
return(0); |
} |
|
if (sinfo.bpp <= 8) |
dwordcolor = (long) colormap[z]->palette_entry; |
else |
dwordcolor = colormap[z]->color; |
|
c++; |
} |
else |
{ |
struct xpm_cmap *n; |
|
/* We grab the largest possible, and then compare */ |
|
strncpy(pxlstr, c, cpp); |
z = pxlstr[0]; |
|
if (!colormap[z]) |
{ |
EPRINTF("No color entry for (%s)\n", pxlstr); |
return(0); |
} |
|
n = colormap[z]; |
|
while(n) |
{ |
if (!strncmp(n->mapstr, pxlstr, cpp)) |
break; |
|
n = n->next; |
} |
|
if (!n) |
{ |
EPRINTF("No color found for (%s)\n", pxlstr); |
return(0); |
} |
|
if (sinfo.bpp <= 8) |
dwordcolor = (long) n->palette_entry; |
else |
dwordcolor = n->color; |
|
c += cpp; |
} |
|
/* |
* This ugly thing is needed to ensure that we |
* work well in all modes. |
*/ |
switch(sinfo.bpp) |
{ |
case 2: |
if (bitcount == 0) |
imageptr[0] = 0; |
|
imageptr[0] |= (dwordcolor & 0x3) << (4 - bitcount); |
bitcount++; |
|
if (bitcount == 4) |
{ |
imageptr++; |
bytecount += pimage->bytesperpixel; |
bitcount = 0; |
} |
|
break; |
|
case 4: |
if (bitcount == 0) |
imageptr[0] = 0; |
|
imageptr[0] |= (dwordcolor & 0xF) << (2 - bitcount); |
bitcount++; |
|
if (bitcount == 2) |
{ |
imageptr++; |
bytecount += pimage->bytesperpixel; |
bitcount = 0; |
} |
|
break; |
|
case 8: |
case 16: |
case 24: |
case 32: |
|
for(i = 0; i < pimage->bytesperpixel; i++) |
imageptr[i] = (dwordcolor >> (8 * i)) & 0xFF; |
|
imageptr += pimage->bytesperpixel; |
bytecount += pimage->bytesperpixel; |
break; |
|
#ifdef NOTUSED |
case 8: |
imageptr[0] = (unsigned char) (dwordcolor & 0xFF); |
imageptr += pimage->bytesperpixel; |
bytecount += pimage->bytesperpixel; |
break; |
|
case 16: |
case 24: |
case 32: |
imageptr[0] = (unsigned char) (dwordcolor >> 24) & 0xFF; |
imageptr[1] = (unsigned char) (dwordcolor >> 16) & 0xFF; |
imageptr[2] = (unsigned char) (dwordcolor >> 8) & 0xFF; |
imageptr[3] = (unsigned char) (dwordcolor & 0xFF); |
imageptr += pimage->bytesperpixel; |
bytecount += pimage->bytesperpixel; |
break; |
#endif |
} |
} |
|
/* Pad to the end of the line */ |
if (bytecount < pimage->pitch) |
for(i = 0; i < (pimage->pitch - bytecount); i++) |
*imageptr++ = 0x00; |
|
read_xline++; |
|
if (read_xline == row) |
status = LOAD_DONE; |
|
continue; |
} |
} |
|
free(colorheap); |
|
if (status != LOAD_DONE) |
return(-1); |
return(1); |
} |
#endif /* defined(HAVE_FILEIO) && defined(HAVE_XPM_SUPPORT)*/ |
|
#endif /* defined(HAVE_FILEIO)*/ |
/devpoly.c
0,0 → 1,636
#include <stdio.h> |
#include <stdlib.h> |
#include "device.h" |
/* |
* Microwindows polygon outline and fill routines. |
* Copyright (c) 1999, 2000, 2001 Greg Haerr <greg@censoft.com> |
* Portions Copyright (c) 1991 David I. Bell |
* |
* There are currently three implementations of the polygon |
* fill routine. The version from X11 most properly |
* fills polygons that must also be outlined as well. All are |
* controlled with #if directive in this file. |
*/ |
|
/* extern definitions*/ |
void drawpoint(PSD psd,MWCOORD x, MWCOORD y); |
void drawrow(PSD psd,MWCOORD x1,MWCOORD x2,MWCOORD y); |
extern int gr_mode; /* drawing mode */ |
|
/* Draw a polygon in the foreground color, applying clipping if necessary. |
* The polygon is only closed if the first point is repeated at the end. |
* Some care is taken to plot the endpoints correctly if the current |
* drawing mode is XOR. However, internal crossings are not handled |
* correctly. |
*/ |
void |
GdPoly(PSD psd, int count, MWPOINT *points) |
{ |
MWCOORD firstx; |
MWCOORD firsty; |
MWBOOL didline; |
|
if (count < 2) |
return; |
firstx = points->x; |
firsty = points->y; |
didline = FALSE; |
|
while (count-- > 1) { |
if (didline && (gr_mode == MWMODE_XOR)) |
drawpoint(psd, points->x, points->y); |
/* note: change to drawline*/ |
GdLine(psd, points[0].x, points[0].y, points[1].x, points[1].y, TRUE); |
points++; |
didline = TRUE; |
} |
if (gr_mode == MWMODE_XOR) { |
points--; |
if (points->x == firstx && points->y == firsty) |
drawpoint(psd, points->x, points->y); |
} |
GdFixCursor(psd); |
} |
|
#if 1 /* improved convex polygon fill routine*/ |
/*********************************************************** |
Copyright (c) 1987 X Consortium |
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
of this software and associated documentation files (the "Software"), to deal |
in the Software without restriction, including without limitation the rights |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the Software is |
furnished to do so, subject to the following conditions: |
|
The above copyright notice and this permission notice shall be included in |
all copies or substantial portions of the Software. |
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
Except as contained in this notice, the name of the X Consortium shall not be |
used in advertising or otherwise to promote the sale, use or other dealings |
in this Software without prior written authorization from the X Consortium. |
|
|
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. |
|
All Rights Reserved |
|
Permission to use, copy, modify, and distribute this software and its |
documentation for any purpose and without fee is hereby granted, |
provided that the above copyright notice appear in all copies and that |
both that copyright notice and this permission notice appear in |
supporting documentation, and that the name of Digital not be |
used in advertising or publicity pertaining to distribution of the |
software without specific, written prior permission. |
|
DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
SOFTWARE. |
******************************************************************/ |
|
/* |
* Written by Brian Kelleher; Dec. 1985. |
* Adapted for Microwindows Sep 2001 by Greg Haerr <greg@censoft.com> |
* |
* Fill a convex polygon in the fg color, with clipping. |
* If the given polygon |
* is not convex, then the result is undefined. |
* The algorithm is to order the edges from smallest |
* y to largest by partitioning the array into a left |
* edge list and a right edge list. The algorithm used |
* to traverse each edge is an extension of Bresenham's |
* line algorithm with y as the major axis. |
* |
* This file contains a few macros to help track |
* the edge of a filled object. The object is assumed |
* to be filled in scanline order, and thus the |
* algorithm used is an extension of Bresenham's line |
* drawing algorithm which assumes that y is always the |
* major axis. |
* |
* In scan converting polygons, we want to choose those pixels |
* which are inside the polygon. Thus, we add .5 to the starting |
* x coordinate for both left and right edges. Now we choose the |
* first pixel which is inside the pgon for the left edge and the |
* first pixel which is outside the pgon for the right edge. |
* Draw the left pixel, but not the right. |
* |
* How to add .5 to the starting x coordinate: |
* If the edge is moving to the right, then subtract dy from the |
* error term from the general form of the algorithm. |
* If the edge is moving to the left, then add dy to the error term. |
* |
* The reason for the difference between edges moving to the left |
* and edges moving to the right is simple: If an edge is moving |
* to the right, then we want the algorithm to flip immediately. |
* If it is moving to the left, then we don't want it to flip until |
* we traverse an entire pixel. |
*/ |
#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ |
int dx; /* local storage */ \ |
\ |
/* \ |
* if the edge is horizontal, then it is ignored \ |
* and assumed not to be processed. Otherwise, do this stuff. \ |
*/ \ |
if ((dy) != 0) { \ |
xStart = (x1); \ |
dx = (x2) - xStart; \ |
if (dx < 0) { \ |
m = dx / (dy); \ |
m1 = m - 1; \ |
incr1 = -2 * dx + 2 * (dy) * m1; \ |
incr2 = -2 * dx + 2 * (dy) * m; \ |
d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ |
} else { \ |
m = dx / (dy); \ |
m1 = m + 1; \ |
incr1 = 2 * dx - 2 * (dy) * m1; \ |
incr2 = 2 * dx - 2 * (dy) * m; \ |
d = -2 * m * (dy) + 2 * dx; \ |
} \ |
} \ |
} |
|
#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ |
if (m1 > 0) { \ |
if (d > 0) { \ |
minval += m1; \ |
d += incr1; \ |
} \ |
else { \ |
minval += m; \ |
d += incr2; \ |
} \ |
} else {\ |
if (d >= 0) { \ |
minval += m1; \ |
d += incr1; \ |
} \ |
else { \ |
minval += m; \ |
d += incr2; \ |
} \ |
} \ |
} |
|
/* |
* Find the index of the point with the smallest y. |
*/ |
static int |
getPolyYBounds(MWPOINT *pts, int n, int *by, int *ty) |
{ |
MWPOINT *ptMin; |
int ymin, ymax; |
MWPOINT *ptsStart = pts; |
|
ptMin = pts; |
ymin = ymax = (pts++)->y; |
|
while (--n > 0) { |
if (pts->y < ymin) |
{ |
ptMin = pts; |
ymin = pts->y; |
} |
if(pts->y > ymax) |
ymax = pts->y; |
|
pts++; |
} |
|
*by = ymin; |
*ty = ymax; |
return(ptMin-ptsStart); |
} |
|
void |
GdFillPoly(PSD psd, int count, MWPOINT *pointtable) |
{ |
MWCOORD xl = 0, xr = 0; /* x vals of left and right edges */ |
int dl = 0, dr = 0; /* decision variables */ |
int ml = 0, m1l = 0; /* left edge slope and slope+1 */ |
int mr = 0, m1r = 0; /* right edge slope and slope+1 */ |
int incr1l = 0, incr2l = 0; /* left edge error increments */ |
int incr1r = 0, incr2r = 0; /* right edge error increments */ |
int dy; /* delta y */ |
MWCOORD y; /* current scanline */ |
int left, right; /* indices to first endpoints */ |
int i; /* loop counter */ |
int nextleft, nextright; /* indices to second endpoints */ |
MWPOINT *ptsOut, *FirstPoint;/* output buffer */ |
MWCOORD *width, *FirstWidth;/* output buffer */ |
int imin; /* index of smallest vertex (in y)*/ |
int ymin; /* y-extents of polygon */ |
int ymax; |
|
/* |
* find leftx, bottomy, rightx, topy, and the index |
* of bottomy. |
*/ |
imin = getPolyYBounds(pointtable, count, &ymin, &ymax); |
|
dy = ymax - ymin + 1; |
if ((count < 3) || (dy < 0)) |
return; |
ptsOut = FirstPoint = (MWPOINT *)ALLOCA(sizeof(MWPOINT) * dy); |
width = FirstWidth = (MWCOORD *)ALLOCA(sizeof(MWCOORD) * dy); |
if(!FirstPoint || !FirstWidth) |
{ |
if (FirstWidth) FREEA(FirstWidth); |
if (FirstPoint) FREEA(FirstPoint); |
return; |
} |
|
nextleft = nextright = imin; |
y = pointtable[nextleft].y; |
|
/* |
* loop through all edges of the polygon |
*/ |
do { |
/* |
* add a left edge if we need to |
*/ |
if (pointtable[nextleft].y == y) { |
left = nextleft; |
|
/* |
* find the next edge, considering the end |
* conditions of the array. |
*/ |
nextleft++; |
if (nextleft >= count) |
nextleft = 0; |
|
/* |
* now compute all of the random information |
* needed to run the iterative algorithm. |
*/ |
BRESINITPGON(pointtable[nextleft].y-pointtable[left].y, |
pointtable[left].x,pointtable[nextleft].x, |
xl, dl, ml, m1l, incr1l, incr2l); |
} |
|
/* |
* add a right edge if we need to |
*/ |
if (pointtable[nextright].y == y) { |
right = nextright; |
|
/* |
* find the next edge, considering the end |
* conditions of the array. |
*/ |
nextright--; |
if (nextright < 0) |
nextright = count-1; |
|
/* |
* now compute all of the random information |
* needed to run the iterative algorithm. |
*/ |
BRESINITPGON(pointtable[nextright].y-pointtable[right].y, |
pointtable[right].x,pointtable[nextright].x, |
xr, dr, mr, m1r, incr1r, incr2r); |
} |
|
/* |
* generate scans to fill while we still have |
* a right edge as well as a left edge. |
*/ |
i = MWMIN(pointtable[nextleft].y, pointtable[nextright].y) - y; |
/* in case we're called with non-convex polygon */ |
if(i < 0) |
{ |
FREEA(FirstWidth); |
FREEA(FirstPoint); |
return; |
} |
while (i-- > 0) |
{ |
ptsOut->y = y; |
|
/* |
* reverse the edges if necessary |
*/ |
if (xl < xr) |
{ |
*(width++) = xr - xl; |
(ptsOut++)->x = xl; |
} |
else |
{ |
*(width++) = xl - xr; |
(ptsOut++)->x = xr; |
} |
y++; |
|
/* increment down the edges */ |
BRESINCRPGON(dl, xl, ml, m1l, incr1l, incr2l); |
BRESINCRPGON(dr, xr, mr, m1r, incr1r, incr2r); |
} |
} while (y != ymax); |
|
/* |
* Finally, fill the spans |
*/ |
i = ptsOut-FirstPoint; |
ptsOut = FirstPoint; |
width = FirstWidth; |
while (--i >= 0) { |
/* calc x extent from width*/ |
int e = *width++ - 1; |
if (e >= 0) { |
drawrow(psd, ptsOut->x, ptsOut->x + e, ptsOut->y); |
} |
++ptsOut; |
} |
|
FREEA(FirstWidth); |
FREEA(FirstPoint); |
GdFixCursor(psd); |
} |
#endif |
|
#if 0 /* original convex only polygon fill routine*/ |
/* |
* Fill a polygon in the foreground color, applying clipping if necessary. |
* The last point may be a duplicate of the first point, but this is |
* not required. |
* Note: this routine currently only correctly fills convex polygons. |
*/ |
|
/* Utility routine for filling polygons. Find the intersection point (if |
* any) of a horizontal line with an arbitrary line, and extend the current |
* minimum and maximum x values as needed to include the intersection point. |
* Input parms: |
* y row to check for intersection |
* x1, y1 first endpoint |
* x2, y2 second enpoint |
* minxptr address of current minimum x |
* maxxptr address of current maximum x |
*/ |
static void |
extendrow(MWCOORD y,MWCOORD x1,MWCOORD y1,MWCOORD x2,MWCOORD y2, |
MWCOORD *minxptr,MWCOORD *maxxptr) |
{ |
MWCOORD x; /* x coordinate of intersection */ |
typedef long NUM; |
NUM num; /* numerator of fraction */ |
|
/* First make sure the specified line segment includes the specified |
* row number. If not, then there is no intersection. |
*/ |
if (((y < y1) || (y > y2)) && ((y < y2) || (y > y1))) |
return; |
|
/* If a horizontal line, then check the two endpoints. */ |
if (y1 == y2) { |
if (*minxptr > x1) *minxptr = x1; |
if (*minxptr > x2) *minxptr = x2; |
if (*maxxptr < x1) *maxxptr = x1; |
if (*maxxptr < x2) *maxxptr = x2; |
return; |
} |
|
/* If a vertical line, then check the x coordinate. */ |
if (x1 == x2) { |
if (*minxptr > x1) *minxptr = x1; |
if (*maxxptr < x1) *maxxptr = x1; |
return; |
} |
|
/* An arbitrary line. Calculate the intersection point using the |
* formula x = x1 + (y - y1) * (x2 - x1) / (y2 - y1). |
*/ |
num = ((NUM) (y - y1)) * (x2 - x1); |
x = x1 + num / (y2 - y1); |
if (*minxptr > x) *minxptr = x; |
if (*maxxptr < x) *maxxptr = x; |
} |
|
void |
GdFillPoly(PSD psd, int count, MWPOINT *points) |
{ |
MWPOINT *pp; /* current point */ |
MWCOORD miny; /* minimum row */ |
MWCOORD maxy; /* maximum row */ |
MWCOORD minx; /* minimum column */ |
MWCOORD maxx; /* maximum column */ |
int i; /* counter */ |
|
if (count <= 0) |
return; |
|
/* First determine the minimum and maximum rows for the polygon. */ |
pp = points; |
miny = pp->y; |
maxy = pp->y; |
for (i = count; i-- > 0; pp++) { |
if (miny > pp->y) miny = pp->y; |
if (maxy < pp->y) maxy = pp->y; |
} |
if (miny < 0) |
miny = 0; |
if (maxy >= psd->yvirtres) |
maxy = psd->yvirtres - 1; |
if (miny > maxy) |
return; |
|
/* Now for each row, scan the list of points and determine the |
* minimum and maximum x coordinate for each line, and plot the row. |
* The last point connects with the first point automatically. |
*/ |
for (; miny <= maxy; miny++) { |
minx = MAX_MWCOORD; |
maxx = MIN_MWCOORD; |
pp = points; |
for (i = count; --i > 0; pp++) |
extendrow(miny, pp[0].x, pp[0].y, pp[1].x, pp[1].y, |
&minx, &maxx); |
extendrow(miny, pp[0].x, pp[0].y, points[0].x, points[0].y, |
&minx, &maxx); |
|
if (minx <= maxx) |
drawrow(psd, minx, maxx, miny); |
} |
GdFixCursor(psd); |
} |
#endif |
|
#if 0 /* irregular polygon fill, uses edge table, malloc, qsort*/ |
/* |
* Fill a polygon in the foreground color, applying clipping if necessary. |
* The last point may be a duplicate of the first point, but this is |
* not required. |
* Note: this routine correctly draws convex, concave, regular, |
* and irregular polygons. |
*/ |
#define USE_FLOAT HAVEFLOAT /* set to use floating point*/ |
|
#define swap(a,b) do { a ^= b; b ^= a; a ^= b; } while (0) |
|
typedef struct { |
int x1, y1, x2, y2; |
#if USE_FLOAT |
double x, m; |
#else |
int cx, fn, mn, d; |
#endif |
} edge_t; |
|
static int |
edge_cmp(const void *lvp, const void *rvp) |
{ |
/* convert from void pointers to structure pointers */ |
const edge_t *lp = (const edge_t *)lvp; |
const edge_t *rp = (const edge_t *)rvp; |
|
/* if the minimum y values are different, sort on minimum y */ |
if (lp->y1 != rp->y1) |
return lp->y1 - rp->y1; |
|
/* if the current x values are different, sort on current x */ |
#if USE_FLOAT |
if (lp->x < rp->x) |
return -1; |
else if (lp->x > rp->x) |
return +1; |
#else |
if (lp->cx != rp->cx) |
return lp->cx - rp->cx; |
#endif |
|
/* otherwise they are equal */ |
return 0; |
} |
|
void |
GdFillPoly(PSD psd, int count, MWPOINT * pointtable) |
{ |
edge_t *get; /* global edge table */ |
int nge = 0; /* num global edges */ |
int cge = 0; /* cur global edge */ |
|
edge_t *aet; /* active edge table */ |
int nae = 0; /* num active edges */ |
|
int i, y; |
|
if (count < 3) { |
/* error, polygons require at least three edges (a triangle) */ |
return; |
} |
get = (edge_t *) calloc(count, sizeof(edge_t)); |
aet = (edge_t *) calloc(count, sizeof(edge_t)); |
|
if ((get == 0) || (aet == 0)) { |
/* error, couldn't allocate one or both of the needed tables */ |
if (get) |
free(get); |
if (aet) |
free(aet); |
return; |
} |
/* setup the global edge table */ |
for (i = 0; i < count; ++i) { |
get[nge].x1 = pointtable[i].x; |
get[nge].y1 = pointtable[i].y; |
get[nge].x2 = pointtable[(i + 1) % count].x; |
get[nge].y2 = pointtable[(i + 1) % count].y; |
if (get[nge].y1 != get[nge].y2) { |
if (get[nge].y1 > get[nge].y2) { |
swap(get[nge].x1, get[nge].x2); |
swap(get[nge].y1, get[nge].y2); |
} |
#if USE_FLOAT |
get[nge].x = get[nge].x1; |
get[nge].m = get[nge].x2 - get[nge].x1; |
get[nge].m /= get[nge].y2 - get[nge].y1; |
#else |
get[nge].cx = get[nge].x1; |
get[nge].mn = get[nge].x2 - get[nge].x1; |
get[nge].d = get[nge].y2 - get[nge].y1; |
get[nge].fn = get[nge].mn / 2; |
#endif |
++nge; |
} |
} |
|
qsort(get, nge, sizeof(get[0]), edge_cmp); |
|
/* start with the lowest y in the table */ |
y = get[0].y1; |
|
do { |
|
/* add edges to the active table from the global table */ |
while ((nge > 0) && (get[cge].y1 == y)) { |
aet[nae] = get[cge++]; |
--nge; |
aet[nae++].y1 = 0; |
} |
|
qsort(aet, nae, sizeof(aet[0]), edge_cmp); |
|
/* using odd parity, render alternating line segments */ |
for (i = 1; i < nae; i += 2) { |
#if USE_FLOAT |
int l = (int)aet[i - 1].x; |
int r = (int)aet[i].x; |
#else |
int l = (int)aet[i - 1].cx; |
int r = (int)aet[i].cx; |
#endif |
if (r > l) |
drawrow(psd, l, r - 1, y); |
} |
|
/* prepare for the next scan line */ |
++y; |
|
/* remove inactive edges from the active edge table */ |
/* or update the current x position of active edges */ |
for (i = 0; i < nae; ++i) { |
if (aet[i].y2 == y) |
aet[i--] = aet[--nae]; |
else { |
#if USE_FLOAT |
aet[i].x += aet[i].m; |
#else |
aet[i].fn += aet[i].mn; |
if (aet[i].fn < 0) { |
aet[i].cx += aet[i].fn / aet[i].d - 1; |
aet[i].fn %= aet[i].d; |
aet[i].fn += aet[i].d; |
} |
if (aet[i].fn >= aet[i].d) { |
aet[i].cx += aet[i].fn / aet[i].d; |
aet[i].fn %= aet[i].d; |
} |
#endif |
} |
} |
|
/* keep doing this while there are any edges left */ |
} while ((nae > 0) || (nge > 0)); |
|
/* all done, free the edge tables */ |
free(get); |
free(aet); |
|
GdFixCursor(psd); |
} |
#endif |
/devpalgray4.c
0,0 → 1,23
/* |
* 4bpp (16 color) grayscale palette |
*/ |
#include "device.h" |
|
MWPALENTRY mwstdpal4[16] = { |
RGBDEF( 0, 0, 0 ), |
RGBDEF( 17, 17, 17 ), |
RGBDEF( 34, 34, 34 ), |
RGBDEF( 51, 51, 51 ), |
RGBDEF( 68, 68, 68 ), |
RGBDEF( 85, 85, 85 ), |
RGBDEF( 102, 102, 102 ), |
RGBDEF( 119, 119, 119 ), |
RGBDEF( 136, 136, 136 ), |
RGBDEF( 153, 153, 153 ), |
RGBDEF( 170, 170, 170 ), |
RGBDEF( 187, 187, 187 ), |
RGBDEF( 204, 204, 204 ), |
RGBDEF( 221, 221, 221 ), |
RGBDEF( 238, 238, 238 ), |
RGBDEF( 255, 255, 255 ) |
}; |
/devrgn.c
0,0 → 1,1426
/* |
* Portions Copyright (c) 1999, 2000 Greg Haerr <greg@censoft.com> |
* Somewhat less shamelessly ripped from the Wine distribution |
* |
* Device-independent multi-rectangle clipping routines. |
* |
* GDI region objects. Shamelessly ripped out from the X11 distribution |
* Thanks for the nice licence. |
* |
* Copyright 1993, 1994, 1995 Alexandre Julliard |
* Modifications and additions: Copyright 1998 Huw Davies |
*/ |
/************************************************************************ |
|
Copyright (c) 1987, 1988 X Consortium |
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
of this software and associated documentation files (the "Software"), to deal |
in the Software without restriction, including without limitation the rights |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the Software is |
furnished to do so, subject to the following conditions: |
|
The above copyright notice and this permission notice shall be included in |
all copies or substantial portions of the Software. |
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
Except as contained in this notice, the name of the X Consortium shall not be |
used in advertising or otherwise to promote the sale, use or other dealings |
in this Software without prior written authorization from the X Consortium. |
|
|
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. |
|
All Rights Reserved |
|
Permission to use, copy, modify, and distribute this software and its |
documentation for any purpose and without fee is hereby granted, |
provided that the above copyright notice appear in all copies and that |
both that copyright notice and this permission notice appear in |
supporting documentation, and that the name of Digital not be |
used in advertising or publicity pertaining to distribution of the |
software without specific, written prior permission. |
|
DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
SOFTWARE. |
|
************************************************************************/ |
/* |
* The functions in this file implement the Region abstraction, similar to one |
* used in the X11 sample server. A Region is simply an area, as the name |
* implies, and is implemented as a "y-x-banded" array of rectangles. To |
* explain: Each Region is made up of a certain number of rectangles sorted |
* by y coordinate first, and then by x coordinate. |
* |
* Furthermore, the rectangles are banded such that every rectangle with a |
* given upper-left y coordinate (y1) will have the same lower-right y |
* coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it |
* will span the entire vertical distance of the band. This means that some |
* areas that could be merged into a taller rectangle will be represented as |
* several shorter rectangles to account for shorter rectangles to its left |
* or right but within its "vertical scope". |
* |
* An added constraint on the rectangles is that they must cover as much |
* horizontal area as possible. E.g. no two rectangles in a band are allowed |
* to touch. |
* |
* Whenever possible, bands will be merged together to cover a greater vertical |
* distance (and thus reduce the number of rectangles). Two bands can be merged |
* only if the bottom of one touches the top of the other and they have |
* rectangles in the same places (of the same width, of course). This maintains |
* the y-x-banding that's so nice to have... |
*/ |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include "device.h" |
|
typedef void (*voidProcp)(); |
|
/* 1 if two RECTs overlap. |
* 0 if two RECTs do not overlap. |
*/ |
#define EXTENTCHECK(r1, r2) \ |
((r1)->right > (r2)->left && \ |
(r1)->left < (r2)->right && \ |
(r1)->bottom > (r2)->top && \ |
(r1)->top < (r2)->bottom) |
|
/* |
* Check to see if there is enough memory in the present region. |
*/ |
#define MEMCHECK(reg, rect, firstrect){\ |
if ((reg)->numRects >= ((reg)->size - 1)){\ |
(firstrect) = realloc(\ |
(firstrect), (2 * (sizeof(MWRECT)) * ((reg)->size)));\ |
if ((firstrect) == 0)\ |
return;\ |
(reg)->size *= 2;\ |
(rect) = &(firstrect)[(reg)->numRects];\ |
}\ |
} |
|
#define REGION_NOT_EMPTY(pReg) pReg->numRects |
|
#define EMPTY_REGION(pReg) { \ |
(pReg)->numRects = 0; \ |
(pReg)->extents.left = (pReg)->extents.top = 0; \ |
(pReg)->extents.right = (pReg)->extents.bottom = 0; \ |
(pReg)->type = MWREGION_NULL; \ |
} |
|
#define INRECT(r, x, y) \ |
( ( ((r).right > x)) && \ |
( ((r).left <= x)) && \ |
( ((r).bottom > y)) && \ |
( ((r).top <= y)) ) |
|
/* return TRUE if point is in region*/ |
MWBOOL |
GdPtInRegion(MWCLIPREGION *rgn, MWCOORD x, MWCOORD y) |
{ |
int i; |
|
if (rgn->numRects > 0 && INRECT(rgn->extents, x, y)) |
for (i = 0; i < rgn->numRects; i++) |
if (INRECT (rgn->rects[i], x, y)) |
return TRUE; |
return FALSE; |
} |
|
/* return whether rectangle is all in, partly in, or out of region*/ |
int |
GdRectInRegion(MWCLIPREGION *rgn, const MWRECT *rect) |
{ |
MWRECT * pCurRect; |
MWRECT * pRectEnd; |
MWCOORD rx, ry; |
MWBOOL partIn, partOut; |
|
/* this is (just) a useful optimization */ |
if (!rgn->numRects || !EXTENTCHECK(&rgn->extents, rect)) |
return MWRECT_OUT; |
|
partOut = FALSE; |
partIn = FALSE; |
rx = rect->left; |
ry = rect->top; |
|
/* |
* can stop when both partOut and partIn are TRUE, |
* or we reach rect->bottom |
*/ |
for (pCurRect = rgn->rects, pRectEnd = pCurRect + rgn->numRects; |
pCurRect < pRectEnd; pCurRect++) { |
|
if (pCurRect->bottom <= ry) |
continue; /* not far enough down yet*/ |
|
if (pCurRect->top > ry) { |
partOut = TRUE; /* missed part of rectangle above */ |
if (partIn || (pCurRect->top >= rect->bottom)) |
break; |
ry = pCurRect->top; /* x guaranteed to be == rect->left */ |
} |
|
if (pCurRect->right <= rx) |
continue; /* not far enough over yet */ |
|
if (pCurRect->left > rx) { |
partOut = TRUE; /* missed part of rectangle to left */ |
if (partIn) |
break; |
} |
|
if (pCurRect->left < rect->right) { |
partIn = TRUE; /* definitely overlap */ |
if (partOut) |
break; |
} |
|
if (pCurRect->right >= rect->right) { |
ry = pCurRect->bottom; /* finished with this band */ |
if (ry >= rect->bottom) |
break; |
rx = rect->left; /* reset x out to left again */ |
} else { |
/* |
* Because boxes in a band are maximal width, if the first box |
* to overlap the rectangle doesn't completely cover it in that |
* band, the rectangle must be partially out, since some of it |
* will be uncovered in that band. partIn will have been set true |
* by now... |
*/ |
break; |
} |
} |
|
return(partIn ? ((ry < rect->bottom) ? MWRECT_PARTIN : MWRECT_ALLIN) : |
MWRECT_OUT); |
} |
|
#if 0000 |
/* Returns TRUE if rect is at least partly inside rgn*/ |
MWBOOL |
GdRectInRegion(MWCLIPREGION *rgn, const MWRECT *rect) |
{ |
MWRECT *pCurRect, *pRectEnd; |
MWBOOL ret = FALSE; |
|
/* this is (just) a useful optimization */ |
if ((rgn->numRects > 0) && EXTENTCHECK(&rgn->extents, rect)) |
{ |
for (pCurRect = rgn->rects, pRectEnd = pCurRect + |
rgn->numRects; pCurRect < pRectEnd; pCurRect++) |
{ |
if (pCurRect->bottom <= rect->top) |
continue; /* not far enough down yet */ |
|
if (pCurRect->top >= rect->bottom) { |
ret = FALSE; /* too far down */ |
break; |
} |
|
if (pCurRect->right <= rect->left) |
continue; /* not far enough over yet */ |
|
if (pCurRect->left >= rect->right) { |
continue; |
} |
|
ret = TRUE; |
break; |
} |
} |
return ret; |
} |
#endif |
|
static MWBOOL |
EQUALRECT(MWRECT *r1, MWRECT *r2) |
{ |
return ((r1->left == r2->left) && (r1->right == r2->right) && |
(r1->top == r2->top) && (r1->bottom == r2->bottom)); |
} |
|
MWBOOL |
GdEqualRegion(MWCLIPREGION *r1, MWCLIPREGION *r2) |
{ |
int i; |
|
if (r1->numRects != r2->numRects) |
return FALSE; |
if (r1->numRects == 0) |
return TRUE; |
if (!EQUALRECT(&r1->extents, &r2->extents)) |
return FALSE; |
for (i = 0; i < r1->numRects; i++) { |
if (!EQUALRECT(r1->rects + i, r2->rects + i)) |
return FALSE; |
} |
return TRUE; |
} |
|
MWBOOL |
GdEmptyRegion(MWCLIPREGION *rgn) |
{ |
return rgn->numRects == 0; |
} |
|
/* |
* Create a new empty MWCLIPREGION. |
*/ |
MWCLIPREGION * |
GdAllocRegion(void) |
{ |
MWCLIPREGION *rgn; |
|
if ((rgn = malloc(sizeof( MWCLIPREGION )))) |
{ |
if ((rgn->rects = malloc(sizeof( MWRECT )))) |
{ |
rgn->size = 1; |
EMPTY_REGION(rgn); |
return rgn; |
} |
free(rgn); |
} |
return NULL; |
} |
|
MWCLIPREGION * |
GdAllocRectRegion(MWCOORD left, MWCOORD top, MWCOORD right, MWCOORD bottom) |
{ |
MWCLIPREGION *rgn; |
|
rgn = GdAllocRegion(); |
if (rgn) |
GdSetRectRegion(rgn, left, top, right, bottom); |
return rgn; |
} |
|
MWCLIPREGION * |
GdAllocRectRegionIndirect(MWRECT *prc) |
{ |
return GdAllocRectRegion(prc->left, prc->top, prc->right, prc->bottom); |
} |
|
void |
GdSetRectRegion(MWCLIPREGION *rgn, MWCOORD left, MWCOORD top, MWCOORD right, |
MWCOORD bottom) |
{ |
if (left != right && top != bottom) { |
rgn->rects->left = rgn->extents.left = left; |
rgn->rects->top = rgn->extents.top = top; |
rgn->rects->right = rgn->extents.right = right; |
rgn->rects->bottom = rgn->extents.bottom = bottom; |
rgn->numRects = 1; |
rgn->type = MWREGION_SIMPLE; |
} else |
EMPTY_REGION(rgn); |
} |
|
void |
GdSetRectRegionIndirect(MWCLIPREGION *rgn, MWRECT *prc) |
{ |
GdSetRectRegion(rgn, prc->left, prc->top, prc->right, prc->bottom); |
} |
|
void |
GdDestroyRegion(MWCLIPREGION *rgn) |
{ |
if(rgn) { |
free(rgn->rects); |
free(rgn); |
} |
} |
|
void |
GdOffsetRegion(MWCLIPREGION *rgn, MWCOORD x, MWCOORD y) |
{ |
int nbox = rgn->numRects; |
MWRECT *pbox = rgn->rects; |
|
if(nbox && (x || y)) { |
while(nbox--) { |
pbox->left += x; |
pbox->right += x; |
pbox->top += y; |
pbox->bottom += y; |
pbox++; |
} |
rgn->extents.left += x; |
rgn->extents.right += x; |
rgn->extents.top += y; |
rgn->extents.bottom += y; |
} |
} |
|
/* get bounding box for region, return region type*/ |
int |
GdGetRegionBox(MWCLIPREGION *rgn, MWRECT *prc) |
{ |
*prc = rgn->extents; |
return rgn->type; |
} |
|
/*********************************************************************** |
* GdUnionRectWithRegion |
* Adds a rectangle to a MWCLIPREGION |
*/ |
void |
GdUnionRectWithRegion(const MWRECT *rect, MWCLIPREGION *rgn) |
{ |
MWCLIPREGION region; |
|
region.rects = ®ion.extents; |
region.numRects = 1; |
region.size = 1; |
region.type = MWREGION_SIMPLE; |
region.extents = *rect; |
GdUnionRegion(rgn, rgn, ®ion); |
} |
|
/*********************************************************************** |
* GdSubtractRectWithRegion |
* Subtracts a rectangle from a MWCLIPREGION |
*/ |
void |
GdSubtractRectFromRegion(const MWRECT *rect, MWCLIPREGION *rgn) |
{ |
MWCLIPREGION region; |
|
region.rects = ®ion.extents; |
region.numRects = 1; |
region.size = 1; |
region.type = MWREGION_SIMPLE; |
region.extents = *rect; |
GdSubtractRegion(rgn, rgn, ®ion); |
} |
|
|
/*********************************************************************** |
* GdCopyRegion |
*/ |
void |
GdCopyRegion(MWCLIPREGION *dst, MWCLIPREGION *src) |
{ |
if (dst != src) /* don't want to copy to itself */ |
{ |
if (dst->size < src->numRects) |
{ |
if (! (dst->rects = realloc( dst->rects, src->numRects * sizeof(MWRECT)))) |
return; |
dst->size = src->numRects; |
} |
dst->numRects = src->numRects; |
dst->extents.left = src->extents.left; |
dst->extents.top = src->extents.top; |
dst->extents.right = src->extents.right; |
dst->extents.bottom = src->extents.bottom; |
dst->type = src->type; |
|
memcpy((char *) dst->rects, (char *) src->rects, |
(int) (src->numRects * sizeof(MWRECT))); |
} |
} |
|
|
/*********************************************************************** |
* REGION_SetExtents |
* Re-calculate the extents of a region |
*/ |
static void |
REGION_SetExtents (MWCLIPREGION *pReg) |
{ |
MWRECT *pRect, *pRectEnd, *pExtents; |
|
if (pReg->numRects == 0) |
{ |
pReg->extents.left = 0; |
pReg->extents.top = 0; |
pReg->extents.right = 0; |
pReg->extents.bottom = 0; |
return; |
} |
|
pExtents = &pReg->extents; |
pRect = pReg->rects; |
pRectEnd = &pRect[pReg->numRects - 1]; |
|
/* |
* Since pRect is the first rectangle in the region, it must have the |
* smallest top and since pRectEnd is the last rectangle in the region, |
* it must have the largest bottom, because of banding. Initialize left and |
* right from pRect and pRectEnd, resp., as good things to initialize them |
* to... |
*/ |
pExtents->left = pRect->left; |
pExtents->top = pRect->top; |
pExtents->right = pRectEnd->right; |
pExtents->bottom = pRectEnd->bottom; |
|
while (pRect <= pRectEnd) |
{ |
if (pRect->left < pExtents->left) |
pExtents->left = pRect->left; |
if (pRect->right > pExtents->right) |
pExtents->right = pRect->right; |
pRect++; |
} |
} |
|
|
/*********************************************************************** |
* REGION_Coalesce |
* |
* Attempt to merge the rects in the current band with those in the |
* previous one. Used only by REGION_RegionOp. |
* |
* Results: |
* The new index for the previous band. |
* |
* Side Effects: |
* If coalescing takes place: |
* - rectangles in the previous band will have their bottom fields |
* altered. |
* - pReg->numRects will be decreased. |
* |
*/ |
static MWCOORD |
REGION_Coalesce ( |
MWCLIPREGION *pReg, /* Region to coalesce */ |
MWCOORD prevStart, /* Index of start of previous band */ |
MWCOORD curStart /* Index of start of current band */ |
) { |
MWRECT *pPrevRect; /* Current rect in previous band */ |
MWRECT *pCurRect; /* Current rect in current band */ |
MWRECT *pRegEnd; /* End of region */ |
MWCOORD curNumRects; /* Number of rectangles in current band */ |
MWCOORD prevNumRects; /* Number of rectangles in previous band */ |
MWCOORD bandtop; /* top coordinate for current band */ |
|
pRegEnd = &pReg->rects[pReg->numRects]; |
|
pPrevRect = &pReg->rects[prevStart]; |
prevNumRects = curStart - prevStart; |
|
/* |
* Figure out how many rectangles are in the current band. Have to do |
* this because multiple bands could have been added in REGION_RegionOp |
* at the end when one region has been exhausted. |
*/ |
pCurRect = &pReg->rects[curStart]; |
bandtop = pCurRect->top; |
for (curNumRects = 0; |
(pCurRect != pRegEnd) && (pCurRect->top == bandtop); |
curNumRects++) |
{ |
pCurRect++; |
} |
|
if (pCurRect != pRegEnd) |
{ |
/* |
* If more than one band was added, we have to find the start |
* of the last band added so the next coalescing job can start |
* at the right place... (given when multiple bands are added, |
* this may be pointless -- see above). |
*/ |
pRegEnd--; |
while (pRegEnd[-1].top == pRegEnd->top) |
{ |
pRegEnd--; |
} |
curStart = pRegEnd - pReg->rects; |
pRegEnd = pReg->rects + pReg->numRects; |
} |
|
if ((curNumRects == prevNumRects) && (curNumRects != 0)) { |
pCurRect -= curNumRects; |
/* |
* The bands may only be coalesced if the bottom of the previous |
* matches the top scanline of the current. |
*/ |
if (pPrevRect->bottom == pCurRect->top) |
{ |
/* |
* Make sure the bands have rects in the same places. This |
* assumes that rects have been added in such a way that they |
* cover the most area possible. I.e. two rects in a band must |
* have some horizontal space between them. |
*/ |
do |
{ |
if ((pPrevRect->left != pCurRect->left) || |
(pPrevRect->right != pCurRect->right)) |
{ |
/* |
* The bands don't line up so they can't be coalesced. |
*/ |
return (curStart); |
} |
pPrevRect++; |
pCurRect++; |
prevNumRects -= 1; |
} while (prevNumRects != 0); |
|
pReg->numRects -= curNumRects; |
pCurRect -= curNumRects; |
pPrevRect -= curNumRects; |
|
/* |
* The bands may be merged, so set the bottom of each rect |
* in the previous band to that of the corresponding rect in |
* the current band. |
*/ |
do |
{ |
pPrevRect->bottom = pCurRect->bottom; |
pPrevRect++; |
pCurRect++; |
curNumRects -= 1; |
} while (curNumRects != 0); |
|
/* |
* If only one band was added to the region, we have to backup |
* curStart to the start of the previous band. |
* |
* If more than one band was added to the region, copy the |
* other bands down. The assumption here is that the other bands |
* came from the same region as the current one and no further |
* coalescing can be done on them since it's all been done |
* already... curStart is already in the right place. |
*/ |
if (pCurRect == pRegEnd) |
{ |
curStart = prevStart; |
} |
else |
{ |
do |
{ |
*pPrevRect++ = *pCurRect++; |
} while (pCurRect != pRegEnd); |
} |
|
} |
} |
return (curStart); |
} |
|
/*********************************************************************** |
* REGION_RegionOp |
* |
* Apply an operation to two regions. Called by GdUnion, |
* GdXor, GdSubtract, GdIntersect... |
* |
* Results: |
* None. |
* |
* Side Effects: |
* The new region is overwritten. |
* |
* Notes: |
* The idea behind this function is to view the two regions as sets. |
* Together they cover a rectangle of area that this function divides |
* into horizontal bands where points are covered only by one region |
* or by both. For the first case, the nonOverlapFunc is called with |
* each the band and the band's upper and lower extents. For the |
* second, the overlapFunc is called to process the entire band. It |
* is responsible for clipping the rectangles in the band, though |
* this function provides the boundaries. |
* At the end of each band, the new region is coalesced, if possible, |
* to reduce the number of rectangles in the region. |
* |
*/ |
static void |
REGION_RegionOp( |
MWCLIPREGION *newReg, /* Place to store result */ |
MWCLIPREGION *reg1, /* First region in operation */ |
MWCLIPREGION *reg2, /* 2nd region in operation */ |
void (*overlapFunc)(), /* Function to call for over-lapping bands */ |
void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */ |
void (*nonOverlap2Func)() /* Function to call for non-overlapping bands in region 2 */ |
) { |
MWRECT *r1; /* Pointer into first region */ |
MWRECT *r2; /* Pointer into 2d region */ |
MWRECT *r1End; /* End of 1st region */ |
MWRECT *r2End; /* End of 2d region */ |
MWCOORD ybot; /* Bottom of intersection */ |
MWCOORD ytop; /* Top of intersection */ |
MWRECT *oldRects; /* Old rects for newReg */ |
MWCOORD prevBand; /* Index of start of |
* previous band in newReg */ |
MWCOORD curBand; /* Index of start of current |
* band in newReg */ |
MWRECT *r1BandEnd; /* End of current band in r1 */ |
MWRECT *r2BandEnd; /* End of current band in r2 */ |
MWCOORD top; /* Top of non-overlapping band */ |
MWCOORD bot; /* Bottom of non-overlapping band */ |
|
/* |
* Initialization: |
* set r1, r2, r1End and r2End appropriately, preserve the important |
* parts of the destination region until the end in case it's one of |
* the two source regions, then mark the "new" region empty, allocating |
* another array of rectangles for it to use. |
*/ |
r1 = reg1->rects; |
r2 = reg2->rects; |
r1End = r1 + reg1->numRects; |
r2End = r2 + reg2->numRects; |
|
|
/* |
* newReg may be one of the src regions so we can't empty it. We keep a |
* note of its rects pointer (so that we can free them later), preserve its |
* extents and simply set numRects to zero. |
*/ |
|
oldRects = newReg->rects; |
newReg->numRects = 0; |
|
/* |
* Allocate a reasonable number of rectangles for the new region. The idea |
* is to allocate enough so the individual functions don't need to |
* reallocate and copy the array, which is time consuming, yet we don't |
* have to worry about using too much memory. I hope to be able to |
* nuke the Xrealloc() at the end of this function eventually. |
*/ |
newReg->size = MWMAX(reg1->numRects,reg2->numRects) * 2; |
|
if (! (newReg->rects = malloc( sizeof(MWRECT) * newReg->size ))) |
{ |
newReg->size = 0; |
return; |
} |
|
/* |
* Initialize ybot and ytop. |
* In the upcoming loop, ybot and ytop serve different functions depending |
* on whether the band being handled is an overlapping or non-overlapping |
* band. |
* In the case of a non-overlapping band (only one of the regions |
* has points in the band), ybot is the bottom of the most recent |
* intersection and thus clips the top of the rectangles in that band. |
* ytop is the top of the next intersection between the two regions and |
* serves to clip the bottom of the rectangles in the current band. |
* For an overlapping band (where the two regions intersect), ytop clips |
* the top of the rectangles of both regions and ybot clips the bottoms. |
*/ |
if (reg1->extents.top < reg2->extents.top) |
ybot = reg1->extents.top; |
else |
ybot = reg2->extents.top; |
|
/* |
* prevBand serves to mark the start of the previous band so rectangles |
* can be coalesced into larger rectangles. qv. miCoalesce, above. |
* In the beginning, there is no previous band, so prevBand == curBand |
* (curBand is set later on, of course, but the first band will always |
* start at index 0). prevBand and curBand must be indices because of |
* the possible expansion, and resultant moving, of the new region's |
* array of rectangles. |
*/ |
prevBand = 0; |
|
do |
{ |
curBand = newReg->numRects; |
|
/* |
* This algorithm proceeds one source-band (as opposed to a |
* destination band, which is determined by where the two regions |
* intersect) at a time. r1BandEnd and r2BandEnd serve to mark the |
* rectangle after the last one in the current band for their |
* respective regions. |
*/ |
r1BandEnd = r1; |
while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top)) |
{ |
r1BandEnd++; |
} |
|
r2BandEnd = r2; |
while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top)) |
{ |
r2BandEnd++; |
} |
|
/* |
* First handle the band that doesn't intersect, if any. |
* |
* Note that attention is restricted to one band in the |
* non-intersecting region at once, so if a region has n |
* bands between the current position and the next place it overlaps |
* the other, this entire loop will be passed through n times. |
*/ |
if (r1->top < r2->top) |
{ |
top = MWMAX(r1->top,ybot); |
bot = MWMIN(r1->bottom,r2->top); |
|
if ((top != bot) && (nonOverlap1Func != (void (*)())NULL)) |
{ |
(* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot); |
} |
|
ytop = r2->top; |
} |
else if (r2->top < r1->top) |
{ |
top = MWMAX(r2->top,ybot); |
bot = MWMIN(r2->bottom,r1->top); |
|
if ((top != bot) && (nonOverlap2Func != (void (*)())NULL)) |
{ |
(* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot); |
} |
|
ytop = r1->top; |
} |
else |
{ |
ytop = r1->top; |
} |
|
/* |
* If any rectangles got added to the region, try and coalesce them |
* with rectangles from the previous band. Note we could just do |
* this test in miCoalesce, but some machines incur a not |
* inconsiderable cost for function calls, so... |
*/ |
if (newReg->numRects != curBand) |
{ |
prevBand = REGION_Coalesce (newReg, prevBand, curBand); |
} |
|
/* |
* Now see if we've hit an intersecting band. The two bands only |
* intersect if ybot > ytop |
*/ |
ybot = MWMIN(r1->bottom, r2->bottom); |
curBand = newReg->numRects; |
if (ybot > ytop) |
{ |
(* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot); |
|
} |
|
if (newReg->numRects != curBand) |
{ |
prevBand = REGION_Coalesce (newReg, prevBand, curBand); |
} |
|
/* |
* If we've finished with a band (bottom == ybot) we skip forward |
* in the region to the next band. |
*/ |
if (r1->bottom == ybot) |
{ |
r1 = r1BandEnd; |
} |
if (r2->bottom == ybot) |
{ |
r2 = r2BandEnd; |
} |
} while ((r1 != r1End) && (r2 != r2End)); |
|
/* |
* Deal with whichever region still has rectangles left. |
*/ |
curBand = newReg->numRects; |
if (r1 != r1End) |
{ |
if (nonOverlap1Func != (void (*)())NULL) |
{ |
do |
{ |
r1BandEnd = r1; |
while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top)) |
{ |
r1BandEnd++; |
} |
(* nonOverlap1Func) (newReg, r1, r1BandEnd, |
MWMAX(r1->top,ybot), r1->bottom); |
r1 = r1BandEnd; |
} while (r1 != r1End); |
} |
} |
else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL)) |
{ |
do |
{ |
r2BandEnd = r2; |
while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top)) |
{ |
r2BandEnd++; |
} |
(* nonOverlap2Func) (newReg, r2, r2BandEnd, |
MWMAX(r2->top,ybot), r2->bottom); |
r2 = r2BandEnd; |
} while (r2 != r2End); |
} |
|
if (newReg->numRects != curBand) |
{ |
(void) REGION_Coalesce (newReg, prevBand, curBand); |
} |
|
/* |
* A bit of cleanup. To keep regions from growing without bound, |
* we shrink the array of rectangles to match the new number of |
* rectangles in the region. This never goes to 0, however... |
* |
* Only do this stuff if the number of rectangles allocated is more than |
* twice the number of rectangles in the region (a simple optimization...). |
*/ |
if (newReg->numRects < (newReg->size >> 1)) |
{ |
if (REGION_NOT_EMPTY(newReg)) |
{ |
MWRECT *prev_rects = newReg->rects; |
newReg->size = newReg->numRects; |
newReg->rects = realloc( newReg->rects, sizeof(MWRECT) * newReg->size ); |
if (! newReg->rects) |
newReg->rects = prev_rects; |
} |
else |
{ |
/* |
* No point in doing the extra work involved in an Xrealloc if |
* the region is empty |
*/ |
newReg->size = 1; |
free( newReg->rects ); |
newReg->rects = malloc( sizeof(MWRECT) ); |
} |
} |
free( oldRects ); |
} |
|
/*********************************************************************** |
* Region Intersection |
***********************************************************************/ |
|
|
/*********************************************************************** |
* REGION_IntersectO |
* |
* Handle an overlapping band for REGION_Intersect. |
* |
* Results: |
* None. |
* |
* Side Effects: |
* Rectangles may be added to the region. |
* |
*/ |
static void |
REGION_IntersectO(MWCLIPREGION *pReg, MWRECT *r1, MWRECT *r1End, |
MWRECT *r2, MWRECT *r2End, MWCOORD top, MWCOORD bottom) |
|
{ |
MWCOORD left, right; |
MWRECT *pNextRect; |
|
pNextRect = &pReg->rects[pReg->numRects]; |
|
while ((r1 != r1End) && (r2 != r2End)) |
{ |
left = MWMAX(r1->left, r2->left); |
right = MWMIN(r1->right, r2->right); |
|
/* |
* If there's any overlap between the two rectangles, add that |
* overlap to the new region. |
* There's no need to check for subsumption because the only way |
* such a need could arise is if some region has two rectangles |
* right next to each other. Since that should never happen... |
*/ |
if (left < right) |
{ |
MEMCHECK(pReg, pNextRect, pReg->rects); |
pNextRect->left = left; |
pNextRect->top = top; |
pNextRect->right = right; |
pNextRect->bottom = bottom; |
pReg->numRects += 1; |
pNextRect++; |
} |
|
/* |
* Need to advance the pointers. Shift the one that extends |
* to the right the least, since the other still has a chance to |
* overlap with that region's next rectangle, if you see what I mean. |
*/ |
if (r1->right < r2->right) |
{ |
r1++; |
} |
else if (r2->right < r1->right) |
{ |
r2++; |
} |
else |
{ |
r1++; |
r2++; |
} |
} |
} |
|
/*********************************************************************** |
* GdIntersectRegion |
*/ |
void |
GdIntersectRegion(MWCLIPREGION *newReg, MWCLIPREGION *reg1, MWCLIPREGION *reg2) |
{ |
/* check for trivial reject */ |
if ( (!(reg1->numRects)) || (!(reg2->numRects)) || |
(!EXTENTCHECK(®1->extents, ®2->extents))) |
newReg->numRects = 0; |
else |
REGION_RegionOp (newReg, reg1, reg2, |
(voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL); |
|
/* |
* Can't alter newReg's extents before we call miRegionOp because |
* it might be one of the source regions and miRegionOp depends |
* on the extents of those regions being the same. Besides, this |
* way there's no checking against rectangles that will be nuked |
* due to coalescing, so we have to examine fewer rectangles. |
*/ |
REGION_SetExtents(newReg); |
newReg->type = (newReg->numRects) ? MWREGION_COMPLEX : MWREGION_NULL ; |
} |
|
/*********************************************************************** |
* Region Union |
***********************************************************************/ |
|
/*********************************************************************** |
* REGION_UnionNonO |
* |
* Handle a non-overlapping band for the union operation. Just |
* Adds the rectangles into the region. Doesn't have to check for |
* subsumption or anything. |
* |
* Results: |
* None. |
* |
* Side Effects: |
* pReg->numRects is incremented and the final rectangles overwritten |
* with the rectangles we're passed. |
* |
*/ |
static void |
REGION_UnionNonO(MWCLIPREGION *pReg,MWRECT *r,MWRECT *rEnd,MWCOORD top, |
MWCOORD bottom) |
{ |
MWRECT *pNextRect; |
|
pNextRect = &pReg->rects[pReg->numRects]; |
|
while (r != rEnd) |
{ |
MEMCHECK(pReg, pNextRect, pReg->rects); |
pNextRect->left = r->left; |
pNextRect->top = top; |
pNextRect->right = r->right; |
pNextRect->bottom = bottom; |
pReg->numRects += 1; |
pNextRect++; |
r++; |
} |
} |
|
/*********************************************************************** |
* REGION_UnionO |
* |
* Handle an overlapping band for the union operation. Picks the |
* left-most rectangle each time and merges it into the region. |
* |
* Results: |
* None. |
* |
* Side Effects: |
* Rectangles are overwritten in pReg->rects and pReg->numRects will |
* be changed. |
* |
*/ |
static void |
REGION_UnionO(MWCLIPREGION *pReg, MWRECT *r1, MWRECT *r1End, |
MWRECT *r2, MWRECT *r2End, MWCOORD top, MWCOORD bottom) |
{ |
MWRECT *pNextRect; |
|
pNextRect = &pReg->rects[pReg->numRects]; |
|
#define MERGERECT(r) \ |
if ((pReg->numRects != 0) && \ |
(pNextRect[-1].top == top) && \ |
(pNextRect[-1].bottom == bottom) && \ |
(pNextRect[-1].right >= r->left)) \ |
{ \ |
if (pNextRect[-1].right < r->right) \ |
{ \ |
pNextRect[-1].right = r->right; \ |
} \ |
} \ |
else \ |
{ \ |
MEMCHECK(pReg, pNextRect, pReg->rects); \ |
pNextRect->top = top; \ |
pNextRect->bottom = bottom; \ |
pNextRect->left = r->left; \ |
pNextRect->right = r->right; \ |
pReg->numRects += 1; \ |
pNextRect += 1; \ |
} \ |
r++; |
|
while ((r1 != r1End) && (r2 != r2End)) |
{ |
if (r1->left < r2->left) |
{ |
MERGERECT(r1); |
} |
else |
{ |
MERGERECT(r2); |
} |
} |
|
if (r1 != r1End) |
{ |
do |
{ |
MERGERECT(r1); |
} while (r1 != r1End); |
} |
else while (r2 != r2End) |
{ |
MERGERECT(r2); |
} |
} |
|
/*********************************************************************** |
* GdUnionRegion |
*/ |
void |
GdUnionRegion(MWCLIPREGION *newReg, MWCLIPREGION *reg1, MWCLIPREGION *reg2) |
{ |
/* checks all the simple cases */ |
|
/* |
* Region 1 and 2 are the same or region 1 is empty |
*/ |
if ( (reg1 == reg2) || (!(reg1->numRects)) ) |
{ |
if (newReg != reg2) |
GdCopyRegion(newReg, reg2); |
return; |
} |
|
/* |
* if nothing to union (region 2 empty) |
*/ |
if (!(reg2->numRects)) |
{ |
if (newReg != reg1) |
GdCopyRegion(newReg, reg1); |
return; |
} |
|
/* |
* Region 1 completely subsumes region 2 |
*/ |
if ((reg1->numRects == 1) && |
(reg1->extents.left <= reg2->extents.left) && |
(reg1->extents.top <= reg2->extents.top) && |
(reg1->extents.right >= reg2->extents.right) && |
(reg1->extents.bottom >= reg2->extents.bottom)) |
{ |
if (newReg != reg1) |
GdCopyRegion(newReg, reg1); |
return; |
} |
|
/* |
* Region 2 completely subsumes region 1 |
*/ |
if ((reg2->numRects == 1) && |
(reg2->extents.left <= reg1->extents.left) && |
(reg2->extents.top <= reg1->extents.top) && |
(reg2->extents.right >= reg1->extents.right) && |
(reg2->extents.bottom >= reg1->extents.bottom)) |
{ |
if (newReg != reg2) |
GdCopyRegion(newReg, reg2); |
return; |
} |
|
REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO, |
(voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO); |
|
newReg->extents.left = MWMIN(reg1->extents.left, reg2->extents.left); |
newReg->extents.top = MWMIN(reg1->extents.top, reg2->extents.top); |
newReg->extents.right = MWMAX(reg1->extents.right, reg2->extents.right); |
newReg->extents.bottom = MWMAX(reg1->extents.bottom, reg2->extents.bottom); |
newReg->type = (newReg->numRects) ? MWREGION_COMPLEX : MWREGION_NULL ; |
} |
|
/*********************************************************************** |
* Region Subtraction |
***********************************************************************/ |
|
/*********************************************************************** |
* REGION_SubtractNonO1 |
* |
* Deal with non-overlapping band for subtraction. Any parts from |
* region 2 we discard. Anything from region 1 we add to the region. |
* |
* Results: |
* None. |
* |
* Side Effects: |
* pReg may be affected. |
* |
*/ |
static void |
REGION_SubtractNonO1(MWCLIPREGION *pReg, MWRECT *r, MWRECT *rEnd, |
MWCOORD top, MWCOORD bottom) |
{ |
MWRECT *pNextRect; |
|
pNextRect = &pReg->rects[pReg->numRects]; |
|
while (r != rEnd) |
{ |
MEMCHECK(pReg, pNextRect, pReg->rects); |
pNextRect->left = r->left; |
pNextRect->top = top; |
pNextRect->right = r->right; |
pNextRect->bottom = bottom; |
pReg->numRects += 1; |
pNextRect++; |
r++; |
} |
} |
|
|
/*********************************************************************** |
* REGION_SubtractO |
* |
* Overlapping band subtraction. x1 is the left-most point not yet |
* checked. |
* |
* Results: |
* None. |
* |
* Side Effects: |
* pReg may have rectangles added to it. |
* |
*/ |
static void |
REGION_SubtractO(MWCLIPREGION *pReg, MWRECT *r1, MWRECT *r1End, |
MWRECT *r2, MWRECT *r2End, MWCOORD top, MWCOORD bottom) |
{ |
MWRECT *pNextRect; |
MWCOORD left; |
|
left = r1->left; |
pNextRect = &pReg->rects[pReg->numRects]; |
|
while ((r1 != r1End) && (r2 != r2End)) |
{ |
if (r2->right <= left) |
{ |
/* |
* Subtrahend missed the boat: go to next subtrahend. |
*/ |
r2++; |
} |
else if (r2->left <= left) |
{ |
/* |
* Subtrahend preceeds minuend: nuke left edge of minuend. |
*/ |
left = r2->right; |
if (left >= r1->right) |
{ |
/* |
* Minuend completely covered: advance to next minuend and |
* reset left fence to edge of new minuend. |
*/ |
r1++; |
if (r1 != r1End) |
left = r1->left; |
} |
else |
{ |
/* |
* Subtrahend now used up since it doesn't extend beyond |
* minuend |
*/ |
r2++; |
} |
} |
else if (r2->left < r1->right) |
{ |
/* |
* Left part of subtrahend covers part of minuend: add uncovered |
* part of minuend to region and skip to next subtrahend. |
*/ |
MEMCHECK(pReg, pNextRect, pReg->rects); |
pNextRect->left = left; |
pNextRect->top = top; |
pNextRect->right = r2->left; |
pNextRect->bottom = bottom; |
pReg->numRects += 1; |
pNextRect++; |
left = r2->right; |
if (left >= r1->right) |
{ |
/* |
* Minuend used up: advance to new... |
*/ |
r1++; |
if (r1 != r1End) |
left = r1->left; |
} |
else |
{ |
/* |
* Subtrahend used up |
*/ |
r2++; |
} |
} |
else |
{ |
/* |
* Minuend used up: add any remaining piece before advancing. |
*/ |
if (r1->right > left) |
{ |
MEMCHECK(pReg, pNextRect, pReg->rects); |
pNextRect->left = left; |
pNextRect->top = top; |
pNextRect->right = r1->right; |
pNextRect->bottom = bottom; |
pReg->numRects += 1; |
pNextRect++; |
} |
r1++; |
left = r1->left; |
} |
} |
|
/* |
* Add remaining minuend rectangles to region. |
*/ |
while (r1 != r1End) |
{ |
MEMCHECK(pReg, pNextRect, pReg->rects); |
pNextRect->left = left; |
pNextRect->top = top; |
pNextRect->right = r1->right; |
pNextRect->bottom = bottom; |
pReg->numRects += 1; |
pNextRect++; |
r1++; |
if (r1 != r1End) |
{ |
left = r1->left; |
} |
} |
} |
|
/*********************************************************************** |
* GdSubtractRegion |
* |
* Subtract regS from regM and leave the result in regD. |
* S stands for subtrahend, M for minuend and D for difference. |
* |
* Results: |
* TRUE. |
* |
* Side Effects: |
* regD is overwritten. |
* |
*/ |
void |
GdSubtractRegion(MWCLIPREGION *regD, MWCLIPREGION *regM, MWCLIPREGION *regS ) |
{ |
/* check for trivial reject */ |
if ( (!(regM->numRects)) || (!(regS->numRects)) || |
(!EXTENTCHECK(®M->extents, ®S->extents)) ) |
{ |
GdCopyRegion(regD, regM); |
return; |
} |
|
REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO, |
(voidProcp) REGION_SubtractNonO1, (voidProcp) NULL); |
|
/* |
* Can't alter newReg's extents before we call miRegionOp because |
* it might be one of the source regions and miRegionOp depends |
* on the extents of those regions being the unaltered. Besides, this |
* way there's no checking against rectangles that will be nuked |
* due to coalescing, so we have to examine fewer rectangles. |
*/ |
REGION_SetExtents (regD); |
regD->type = (regD->numRects) ? MWREGION_COMPLEX : MWREGION_NULL ; |
} |
|
/*********************************************************************** |
* GdXorRegion |
*/ |
void |
GdXorRegion(MWCLIPREGION *dr, MWCLIPREGION *sra, MWCLIPREGION *srb) |
{ |
MWCLIPREGION *tra, *trb; |
|
if ((! (tra = GdAllocRegion())) || (! (trb = GdAllocRegion()))) |
return; |
GdSubtractRegion(tra,sra,srb); |
GdSubtractRegion(trb,srb,sra); |
GdUnionRegion(dr,tra,trb); |
GdDestroyRegion(tra); |
GdDestroyRegion(trb); |
} |
|
#if 0 |
/*********************************************************************** |
* DumpRegion |
* Outputs the contents of a MWCLIPREGION |
*/ |
void |
DumpRegion(MWCLIPREGION *pReg) |
{ |
MWRECT *pRect, *pRectEnd = pReg->rects + pReg->numRects; |
|
DPRINTF("Region %p: %d,%d - %d,%d %d rects\n", pReg, |
pReg->extents.left, pReg->extents.top, |
pReg->extents.right, pReg->extents.bottom, pReg->numRects); |
for(pRect = pReg->rects; pRect < pRectEnd; pRect++) |
DPRINTF("\t%d,%d - %d,%d\n", pRect->left, pRect->top, |
pRect->right, pRect->bottom); |
} |
#endif |
/devclip.c
0,0 → 1,7
#include "device.h" |
|
#if DYNAMICREGIONS |
#include "devclip2.c" |
#else |
#include "devclip1.c" |
#endif |
/devmouse.c
0,0 → 1,452
/* |
* Copyright (c) 1999, 2000 Greg Haerr <greg@censoft.com> |
* Copyright (C) 1999 Bradley D. LaRonde (brad@ltc.com) |
* Copyright (c) 1991 David I. Bell |
* Permission is granted to use, distribute, or modify this source, |
* provided that this copyright notice remains intact. |
* |
* Device-independent top level mouse and cursor routines |
* |
* Reads data from mouse driver and tracks real position on the screen. |
* Intersection detection for cursor with auto removal |
* |
* Bradley D. LaRonde added absolute coordinates and z (pen pressure) Oct-1999 |
*/ |
#include <string.h> |
#include "device.h" |
|
/* |
* The following define specifies whether returned mouse |
* driver coordinates are adjusted when running in portrait |
* mode. If the mouse driver doesn't adjust returned values |
* when in portrait mode (as is the case for the iPAQ), then |
* this define should be set on. |
*/ |
#define FLIP_MOUSE_IN_PORTRAIT_MODE 1 |
|
static MWCOORD xpos; /* current x position of mouse */ |
static MWCOORD ypos; /* current y position of mouse */ |
static MWCOORD minx; /* minimum allowed x position */ |
static MWCOORD maxx; /* maximum allowed x position */ |
static MWCOORD miny; /* minimum allowed y position */ |
static MWCOORD maxy; /* maximum allowed y position */ |
static int scale; /* acceleration scale factor */ |
static int thresh; /* acceleration threshhold */ |
static int buttons; /* current state of buttons */ |
static MWBOOL changed; /* mouse state has changed */ |
|
static MWCOORD curminx; /* minimum x value of cursor */ |
static MWCOORD curminy; /* minimum y value of cursor */ |
static MWCOORD curmaxx; /* maximum x value of cursor */ |
static MWCOORD curmaxy; /* maximum y value of cursor */ |
static int curvisible; /* >0 if cursor is visible*/ |
static MWBOOL curneedsrestore;/* cursor needs restoration after drawing*/ |
static MWCOORD cursavx; /* saved cursor location*/ |
static MWCOORD cursavy; |
static MWCOORD cursavx2; |
static MWCOORD cursavy2; |
static MWPIXELVAL curfg; /* foreground color of cursor */ |
static MWPIXELVAL curbg; /* background color of cursor */ |
static MWPIXELVAL cursavbits[MWMAX_CURSOR_SIZE * MWMAX_CURSOR_SIZE]; |
static MWIMAGEBITS cursormask[MWMAX_CURSOR_SIZE]; |
static MWIMAGEBITS cursorcolor[MWMAX_CURSOR_SIZE]; |
|
extern int gr_mode; |
|
/* |
* Initialize the mouse. |
* This sets its position to (0, 0) with no boundaries and no buttons pressed. |
* Returns < 0 on error, or mouse fd on success |
*/ |
int |
GdOpenMouse(void) |
{ |
int fd; |
|
/* init mouse position info*/ |
buttons = 0; |
xpos = 0; |
ypos = 0; |
minx = MIN_MWCOORD; |
miny = MIN_MWCOORD; |
maxx = MAX_MWCOORD; |
maxy = MAX_MWCOORD; |
changed = TRUE; |
|
/* init cursor position and size info*/ |
curvisible = 0; |
curneedsrestore = FALSE; |
curminx = minx; |
curminy = miny; |
curmaxx = curminx + MWMAX_CURSOR_SIZE - 1; |
curmaxy = curminy + MWMAX_CURSOR_SIZE - 1; |
|
if ((fd = mousedev.Open(&mousedev)) == -1) |
return -1; |
|
/* get default acceleration settings*/ |
mousedev.GetDefaultAccel(&scale, &thresh); |
|
/* handle null mouse driver by hiding cursor*/ |
if(fd == -2) |
GdHideCursor(&scrdev); |
return fd; |
} |
|
/* |
* Terminate the use of the mouse. |
*/ |
void |
GdCloseMouse(void) |
{ |
mousedev.Close(); |
} |
|
void |
GdGetButtonInfo(int *buttons) |
{ |
*buttons = mousedev.GetButtonInfo(); |
} |
|
/* |
* Restrict the coordinates of the mouse to the specified coordinates. |
*/ |
void |
GdRestrictMouse(MWCOORD newminx, MWCOORD newminy, MWCOORD newmaxx, |
MWCOORD newmaxy) |
{ |
minx = newminx; |
miny = newminy; |
maxx = newmaxx; |
maxy = newmaxy; |
GdMoveMouse(xpos, ypos); |
} |
|
/* |
* Set the acceleration threshhold and scale factors. |
* Acceleration makes the cursor move further for faster movements. |
* Basically, at mouse speeds above the threshold, the excess distance |
* moved is multiplied by the scale factor. For example, with a threshhold |
* of 5 and a scale of 3, the following gives examples of the original and |
* modified mouse movements: |
* input: 0 4 5 6 9 20 |
* output: 0 4 5 8 17 50 |
*/ |
void |
GdSetAccelMouse(int newthresh, int newscale) |
{ |
if (newthresh < 0) |
newthresh = 0; |
if (newscale < 0) |
newscale = 0; |
thresh = newthresh; |
scale = newscale; |
} |
|
/* |
* Move the mouse to the specified coordinates. |
* The location is limited by the current mouse coordinate restrictions. |
*/ |
void |
GdMoveMouse(MWCOORD newx, MWCOORD newy) |
{ |
if (newx < minx) |
newx = minx; |
if (newx > maxx) |
newx = maxx; |
if (newy < miny) |
newy = miny; |
if (newy > maxy) |
newy = maxy; |
if (newx == xpos && newy == ypos) |
return; |
|
changed = TRUE; |
xpos = newx; |
ypos = newy; |
} |
|
/* |
* Read the current location and button states of the mouse. |
* Returns -1 on read error. |
* Returns 0 if no new data is available from the mouse driver, |
* or if the new data shows no change in button state or position. |
* Returns 1 if the mouse driver tells us a changed button state |
* or position. Button state and position are always both returned, |
* even if only one or the other changes. |
* Do not block. |
*/ |
int |
GdReadMouse(MWCOORD *px, MWCOORD *py, int *pb) |
{ |
MWCOORD x, y, z; |
int newbuttons; /* new button state */ |
int sign; /* sign of change */ |
int status; /* status of reading mouse */ |
|
*px = xpos; |
*py = ypos; |
*pb = buttons; |
|
if (changed) { |
changed = FALSE; |
return 1; |
} |
|
/* read the mouse position */ |
status = mousedev.Read(&x, &y, &z, &newbuttons); |
if (status < 0) |
return -1; |
|
/* no new info from the mouse driver? */ |
if (status == 0) |
return 0; |
|
/* has the button state changed? */ |
if (buttons != newbuttons) { |
changed = TRUE; |
buttons = newbuttons; |
} |
|
/* depending on the kind of data that we have */ |
switch(status) { |
case 1: /* relative position change reported, figure new position */ |
sign = 1; |
if (x < 0) { |
sign = -1; |
x = -x; |
} |
if (x > thresh) |
x = thresh + (x - thresh) * scale; |
x *= sign; |
|
sign = 1; |
if (y < 0) { |
sign = -1; |
y = -y; |
} |
if (y > thresh) |
y = thresh + (y - thresh) * scale; |
y *= sign; |
|
#if FLIP_MOUSE_IN_PORTRAIT_MODE |
if (scrdev.portrait == MWPORTRAIT_RIGHT) |
GdMoveMouse(xpos + y, ypos - x); /* right*/ |
else if (scrdev.portrait == MWPORTRAIT_LEFT) |
GdMoveMouse(xpos - y, ypos + x); /* left*/ |
else if (scrdev.portrait == MWPORTRAIT_DOWN) |
GdMoveMouse(xpos + x, ypos - y); /* down*/ |
else |
#endif |
GdMoveMouse(xpos + x, ypos + y); |
break; |
|
case 2: /* absolute position reported */ |
#if FLIP_MOUSE_IN_PORTRAIT_MODE |
if (scrdev.portrait == MWPORTRAIT_RIGHT) |
GdMoveMouse(y, scrdev.xres - x - 1); /* right*/ |
else if (scrdev.portrait == MWPORTRAIT_LEFT) |
GdMoveMouse(scrdev.yres - y - 1, x); /* left*/ |
else if (scrdev.portrait == MWPORTRAIT_DOWN) |
GdMoveMouse(x, scrdev.yres - y - 1); /* down?*/ |
else |
#endif |
GdMoveMouse(x, y); |
break; |
|
case 3: /* only button data is available */ |
break; |
} |
|
/* didn't anything change? */ |
if (!changed) |
return 0; |
|
/* report new mouse data */ |
changed = FALSE; |
*px = xpos; |
*py = ypos; |
*pb = buttons; |
return 1; |
} |
|
/* |
* Set the cursor position. |
*/ |
void |
GdMoveCursor(MWCOORD newx, MWCOORD newy) |
{ |
MWCOORD shiftx; |
MWCOORD shifty; |
|
shiftx = newx - curminx; |
shifty = newy - curminy; |
if(shiftx == 0 && shifty == 0) |
return; |
curminx += shiftx; |
curmaxx += shiftx; |
curminy += shifty; |
curmaxy += shifty; |
|
/* Restore the screen under the mouse pointer*/ |
GdHideCursor(&scrdev); |
|
/* Draw the new pointer*/ |
GdShowCursor(&scrdev); |
} |
|
/* return current mouse position in x, y*/ |
MWBOOL |
GdGetCursorPos(MWCOORD *px, MWCOORD *py) |
{ |
*px = xpos; |
*py = ypos; |
return curvisible > 0; /* return TRUE if visible*/ |
} |
|
/* |
* Set the cursor size and bitmaps. |
*/ |
void |
GdSetCursor(PMWCURSOR pcursor) |
{ |
int bytes; |
|
GdHideCursor(&scrdev); |
curmaxx = curminx + pcursor->width - 1; |
curmaxy = curminy + pcursor->height - 1; |
|
curfg = GdFindColor(pcursor->fgcolor); |
curbg = GdFindColor(pcursor->bgcolor); |
bytes = MWIMAGE_WORDS(pcursor->width) * pcursor->height |
* sizeof(MWIMAGEBITS); |
memcpy(cursorcolor, pcursor->image, bytes); |
memcpy(cursormask, pcursor->mask, bytes); |
|
GdShowCursor(&scrdev); |
} |
|
|
/* |
* Draw the mouse pointer. Save the screen contents underneath |
* before drawing. Returns previous cursor state. |
*/ |
int |
GdShowCursor(PSD psd) |
{ |
MWCOORD x; |
MWCOORD y; |
MWPIXELVAL * saveptr; |
MWIMAGEBITS * cursorptr; |
MWIMAGEBITS * maskptr; |
MWIMAGEBITS curbit, cbits, mbits; |
MWPIXELVAL oldcolor; |
MWPIXELVAL newcolor; |
int oldmode; |
int prevcursor = curvisible; |
|
if(++curvisible != 1) |
return prevcursor; |
oldmode = gr_mode; |
gr_mode = MWMODE_COPY; |
|
saveptr = cursavbits; |
cursavx = curminx; |
cursavy = curminy; |
cursavx2 = curmaxx; |
cursavy2 = curmaxy; |
cursorptr = cursorcolor; |
maskptr = cursormask; |
|
for (y = curminy; y <= curmaxy; y++) { |
cbits = *cursorptr++; |
mbits = *maskptr++; |
curbit = MWIMAGE_FIRSTBIT; |
for (x = curminx; x <= curmaxx; x++) { |
if(x >= 0 && x < psd->xvirtres && |
y >= 0 && y < psd->yvirtres) { |
oldcolor = psd->ReadPixel(psd, x, y); |
if (curbit & mbits) { |
newcolor = (curbit&cbits)? curbg: curfg; |
if (oldcolor != newcolor) |
psd->DrawPixel(psd, x, y, newcolor); |
} |
*saveptr++ = oldcolor; |
} |
curbit = MWIMAGE_NEXTBIT(curbit); |
} |
} |
|
gr_mode = oldmode; |
return prevcursor; |
} |
|
/* |
* Restore the screen overwritten by the cursor. |
*/ |
int |
GdHideCursor(PSD psd) |
{ |
MWPIXELVAL * saveptr; |
MWCOORD x, y; |
int oldmode; |
int prevcursor = curvisible; |
|
if(curvisible-- <= 0) |
return prevcursor; |
oldmode = gr_mode; |
gr_mode = MWMODE_COPY; |
|
saveptr = cursavbits; |
for (y = cursavy; y <= cursavy2; y++) { |
for (x = cursavx; x <= cursavx2; x++) { |
if(x >= 0 && x < psd->xvirtres && |
y >= 0 && y < psd->yvirtres) { |
psd->DrawPixel(psd, x, y, *saveptr++); |
} |
} |
} |
gr_mode = oldmode; |
return prevcursor; |
} |
|
/* Check to see if the mouse pointer is about to be overwritten. |
* If so, then remove the cursor so that the graphics operation |
* works correctly. If the cursor is removed, then this fact will |
* be remembered and a later call to GdFixCursor will restore it. |
*/ |
void |
GdCheckCursor(PSD psd,MWCOORD x1,MWCOORD y1,MWCOORD x2,MWCOORD y2) |
{ |
MWCOORD temp; |
|
if (curvisible <= 0 || (psd->flags & PSF_SCREEN) == 0) |
return; |
|
if (x1 > x2) { |
temp = x1; |
x1 = x2; |
x2 = temp; |
} |
if (y1 > y2) { |
temp = y1; |
y1 = y2; |
y2 = temp; |
} |
if (x1 > curmaxx || x2 < curminx || y1 > curmaxy || y2 < curminy) |
return; |
|
GdHideCursor(psd); |
curneedsrestore = TRUE; |
} |
|
|
/* Redisplay the cursor if it was removed because of a graphics operation. */ |
void |
GdFixCursor(PSD psd) |
{ |
if (curneedsrestore && (psd->flags & PSF_SCREEN)) { |
GdShowCursor(psd); |
curneedsrestore = FALSE; |
} |
} |
/error.c
0,0 → 1,32
/* |
* Reconfigurable error handler |
*/ |
|
#include <stdio.h> |
#include <stdarg.h> |
#include <string.h> |
#if (UNIX | DOS_DJGPP) |
#include <unistd.h> |
#endif |
#include "device.h" |
|
/* output error message and return -1*/ |
int |
GdError(const char *format, ...) |
{ |
va_list args; |
char buf[512]; |
|
va_start(args, format); |
vsprintf(buf, format, args); |
va_end(args); |
write(2, buf, strlen(buf)); |
return -1; |
} |
|
/* null routine to consume messages */ |
int |
GdErrorNull(const char *format, ...) |
{ |
return -1; |
} |
/Makefile
0,0 → 1,53
############################################################################## |
# Microwindows template Makefile |
# Copyright (c) 2000, 2002 Martin Jolicoeur, Greg Haerr |
############################################################################## |
|
include $(CONFIG) |
|
VPATH := $(TOP)/engine |
|
######################## Additional Flags section ############################ |
|
# Directories list for header files |
INCLUDEDIRS += |
# Defines for preprocessor |
DEFINES += |
|
# Compilation flags for C files OTHER than include directories |
CFLAGS += |
# Preprocessor flags OTHER than defines |
CPPFLAGS += |
# Linking flags |
LDFLAGS += |
|
############################# targets section ################################ |
|
# If you want to create a library with the objects files, define the name here |
LIBNAME = libmwengine.a |
LIBNAMESO = libmwengine.so |
|
# List of objects to compile |
OBJS = devopen.o devdraw.o devfont.o devmouse.o devkbd.o devclip.o devrgn.o \ |
devpal1.o devpal2.o devimage.o devlist.o selfont.o error.o \ |
devrgn2.o devarc.o devpoly.o |
|
#ifeq ($(UNIX), 1) |
OBJS += devtimer.o |
#endif |
|
ifneq ($(ARCH), ELKS) |
OBJS += devpal8.o |
endif |
|
ifeq ($(GRAYPALETTE), Y) |
OBJS += devpalgray4.o |
else |
OBJS += devpal4.o |
endif |
|
######################### Makefile.rules section ############################# |
|
include $(TOP)/Makefile.rules |
|
######################## Tools targets section ############################### |
/devpal1.c
0,0 → 1,14
/* |
* Copyright (c) 1999 Greg Haerr <greg@censoft.com> |
* |
* 1bpp (2 color) standard palette definition |
*/ |
#include "device.h" |
|
/* |
* Standard palette for 2 color (monochrome) systems. |
*/ |
MWPALENTRY mwstdpal1[2] = { |
RGBDEF( 0 , 0 , 0 ), /* black*/ |
RGBDEF( 255, 255, 255 ) /* white*/ |
}; |
/devdraw.c
0,0 → 1,1482
/* |
* Copyright (c) 1999, 2000, 2001 Greg Haerr <greg@censoft.com> |
* Portions Copyright (c) 1991 David I. Bell |
* Permission is granted to use, distribute, or modify this source, |
* provided that this copyright notice remains intact. |
* |
* Device-independent mid level drawing and color routines. |
* |
* These routines do the necessary range checking, clipping, and cursor |
* overwriting checks, and then call the lower level device dependent |
* routines to actually do the drawing. The lower level routines are |
* only called when it is known that all the pixels to be drawn are |
* within the device area and are visible. |
*/ |
/*#define NDEBUG*/ |
#include <stdio.h> |
#include <stdlib.h> |
#include <assert.h> |
#include "device.h" |
|
extern MWPIXELVAL gr_foreground; /* current foreground color */ |
extern MWPIXELVAL gr_background; /* current background color */ |
extern MWBOOL gr_usebg; /* TRUE if background drawn in pixmaps */ |
extern int gr_mode; /* drawing mode */ |
extern MWPALENTRY gr_palette[256]; /* current palette*/ |
extern int gr_firstuserpalentry;/* first user-changable palette entry*/ |
extern int gr_nextpalentry; /* next available palette entry*/ |
|
/*static*/ void drawpoint(PSD psd,MWCOORD x, MWCOORD y); |
/*static*/ void drawrow(PSD psd,MWCOORD x1,MWCOORD x2,MWCOORD y); |
static void drawcol(PSD psd,MWCOORD x,MWCOORD y1,MWCOORD y2); |
|
/* |
* Set the drawing mode for future calls. |
*/ |
int |
GdSetMode(int mode) |
{ |
int oldmode = gr_mode; |
|
gr_mode = mode; |
return oldmode; |
} |
|
/* |
* Set whether or not the background is used for drawing pixmaps and text. |
*/ |
MWBOOL |
GdSetUseBackground(MWBOOL flag) |
{ |
MWBOOL oldusebg = gr_usebg; |
|
gr_usebg = flag; |
return oldusebg; |
} |
|
/* |
* Set the foreground color for drawing. |
*/ |
MWPIXELVAL |
GdSetForeground(MWPIXELVAL fg) |
{ |
MWPIXELVAL oldfg = gr_foreground; |
|
gr_foreground = fg; |
return oldfg; |
} |
|
/* |
* Set the background color for bitmap and text backgrounds. |
*/ |
MWPIXELVAL |
GdSetBackground(MWPIXELVAL bg) |
{ |
MWPIXELVAL oldbg = gr_background; |
|
gr_background = bg; |
return oldbg; |
} |
|
/* |
* Draw a point using the current clipping region and foreground color. |
*/ |
void |
GdPoint(PSD psd, MWCOORD x, MWCOORD y) |
{ |
if (GdClipPoint(psd, x, y)) { |
psd->DrawPixel(psd, x, y, gr_foreground); |
GdFixCursor(psd); |
} |
} |
|
/* |
* Draw an arbitrary line using the current clipping region and foreground color |
* If bDrawLastPoint is FALSE, draw up to but not including point x2, y2. |
* |
* This routine is the only routine that adjusts coordinates for supporting |
* two different types of upper levels, those that draw the last point |
* in a line, and those that draw up to the last point. All other local |
* routines draw the last point. This gives this routine a bit more overhead, |
* but keeps overall complexity down. |
*/ |
void |
GdLine(PSD psd, MWCOORD x1, MWCOORD y1, MWCOORD x2, MWCOORD y2, |
MWBOOL bDrawLastPoint) |
{ |
int xdelta; /* width of rectangle around line */ |
int ydelta; /* height of rectangle around line */ |
int xinc; /* increment for moving x coordinate */ |
int yinc; /* increment for moving y coordinate */ |
int rem; /* current remainder */ |
MWCOORD temp; |
|
/* See if the line is horizontal or vertical. If so, then call |
* special routines. |
*/ |
if (y1 == y2) { |
/* |
* Adjust coordinates if not drawing last point. Tricky. |
*/ |
if(!bDrawLastPoint) { |
if (x1 > x2) { |
temp = x1; |
x1 = x2 + 1; |
x2 = temp; |
} else |
--x2; |
} |
|
/* call faster line drawing routine*/ |
drawrow(psd, x1, x2, y1); |
GdFixCursor(psd); |
return; |
} |
if (x1 == x2) { |
/* |
* Adjust coordinates if not drawing last point. Tricky. |
*/ |
if(!bDrawLastPoint) { |
if (y1 > y2) { |
temp = y1; |
y1 = y2 + 1; |
y2 = temp; |
} else |
--y2; |
} |
|
/* call faster line drawing routine*/ |
drawcol(psd, x1, y1, y2); |
GdFixCursor(psd); |
return; |
} |
|
/* See if the line is either totally visible or totally invisible. If |
* so, then the line drawing is easy. |
*/ |
switch (GdClipArea(psd, x1, y1, x2, y2)) { |
case CLIP_VISIBLE: |
/* |
* For size considerations, there's no low-level bresenham |
* line draw, so we've got to draw all non-vertical |
* and non-horizontal lines with per-point |
* clipping for the time being |
psd->Line(psd, x1, y1, x2, y2, gr_foreground); |
GdFixCursor(psd); |
return; |
*/ |
break; |
case CLIP_INVISIBLE: |
return; |
} |
|
/* The line may be partially obscured. Do the draw line algorithm |
* checking each point against the clipping regions. |
*/ |
xdelta = x2 - x1; |
ydelta = y2 - y1; |
if (xdelta < 0) xdelta = -xdelta; |
if (ydelta < 0) ydelta = -ydelta; |
xinc = (x2 > x1) ? 1 : -1; |
yinc = (y2 > y1) ? 1 : -1; |
if (GdClipPoint(psd, x1, y1)) |
psd->DrawPixel(psd, x1, y1, gr_foreground); |
if (xdelta >= ydelta) { |
rem = xdelta / 2; |
for(;;) { |
if(!bDrawLastPoint && x1 == x2) |
break; |
x1 += xinc; |
rem += ydelta; |
if (rem >= xdelta) { |
rem -= xdelta; |
y1 += yinc; |
} |
if (GdClipPoint(psd, x1, y1)) |
psd->DrawPixel(psd, x1, y1, gr_foreground); |
if(bDrawLastPoint && x1 == x2) |
break; |
} |
} else { |
rem = ydelta / 2; |
for(;;) { |
if(!bDrawLastPoint && y1 == y2) |
break; |
y1 += yinc; |
rem += xdelta; |
if (rem >= ydelta) { |
rem -= ydelta; |
x1 += xinc; |
} |
if (GdClipPoint(psd, x1, y1)) |
psd->DrawPixel(psd, x1, y1, gr_foreground); |
if(bDrawLastPoint && y1 == y2) |
break; |
} |
} |
GdFixCursor(psd); |
} |
|
/* Draw a point in the foreground color, applying clipping if necessary*/ |
/*static*/ void |
drawpoint(PSD psd, MWCOORD x, MWCOORD y) |
{ |
if (GdClipPoint(psd, x, y)) |
psd->DrawPixel(psd, x, y, gr_foreground); |
} |
|
/* Draw a horizontal line from x1 to and including x2 in the |
* foreground color, applying clipping if necessary. |
*/ |
/*static*/ void |
drawrow(PSD psd, MWCOORD x1, MWCOORD x2, MWCOORD y) |
{ |
MWCOORD temp; |
|
/* reverse endpoints if necessary*/ |
if (x1 > x2) { |
temp = x1; |
x1 = x2; |
x2 = temp; |
} |
|
/* clip to physical device*/ |
if (x1 < 0) |
x1 = 0; |
if (x2 >= psd->xvirtres) |
x2 = psd->xvirtres - 1; |
|
/* check cursor intersect once for whole line*/ |
GdCheckCursor(psd, x1, y, x2, y); |
|
while (x1 <= x2) { |
if (GdClipPoint(psd, x1, y)) { |
temp = MWMIN(clipmaxx, x2); |
psd->DrawHorzLine(psd, x1, temp, y, gr_foreground); |
} else |
temp = MWMIN(clipmaxx, x2); |
x1 = temp + 1; |
} |
} |
|
/* Draw a vertical line from y1 to and including y2 in the |
* foreground color, applying clipping if necessary. |
*/ |
static void |
drawcol(PSD psd, MWCOORD x,MWCOORD y1,MWCOORD y2) |
{ |
MWCOORD temp; |
|
/* reverse endpoints if necessary*/ |
if (y1 > y2) { |
temp = y1; |
y1 = y2; |
y2 = temp; |
} |
|
/* clip to physical device*/ |
if (y1 < 0) |
y1 = 0; |
if (y2 >= psd->yvirtres) |
y2 = psd->yvirtres - 1; |
|
/* check cursor intersect once for whole line*/ |
GdCheckCursor(psd, x, y1, x, y2); |
|
while (y1 <= y2) { |
if (GdClipPoint(psd, x, y1)) { |
temp = MWMIN(clipmaxy, y2); |
psd->DrawVertLine(psd, x, y1, temp, gr_foreground); |
} else |
temp = MWMIN(clipmaxy, y2); |
y1 = temp + 1; |
} |
} |
|
/* Draw a rectangle in the foreground color, applying clipping if necessary. |
* This is careful to not draw points multiple times in case the rectangle |
* is being drawn using XOR. |
*/ |
void |
GdRect(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, MWCOORD height) |
{ |
MWCOORD maxx; |
MWCOORD maxy; |
|
if (width <= 0 || height <= 0) |
return; |
maxx = x + width - 1; |
maxy = y + height - 1; |
drawrow(psd, x, maxx, y); |
if (height > 1) |
drawrow(psd, x, maxx, maxy); |
if (height < 3) |
return; |
y++; |
maxy--; |
drawcol(psd, x, y, maxy); |
if (width > 1) |
drawcol(psd, maxx, y, maxy); |
GdFixCursor(psd); |
} |
|
/* Draw a filled in rectangle in the foreground color, applying |
* clipping if necessary. |
*/ |
void |
GdFillRect(PSD psd, MWCOORD x1, MWCOORD y1, MWCOORD width, MWCOORD height) |
{ |
MWCOORD x2 = x1+width-1; |
MWCOORD y2 = y1+height-1; |
|
if (width <= 0 || height <= 0) |
return; |
|
/* See if the rectangle is either totally visible or totally |
* invisible. If so, then the rectangle drawing is easy. |
*/ |
switch (GdClipArea(psd, x1, y1, x2, y2)) { |
case CLIP_VISIBLE: |
psd->FillRect(psd, x1, y1, x2, y2, gr_foreground); |
GdFixCursor(psd); |
return; |
|
case CLIP_INVISIBLE: |
return; |
} |
|
/* The rectangle may be partially obstructed. So do it line by line. */ |
while (y1 <= y2) |
drawrow(psd, x1, x2, y1++); |
GdFixCursor(psd); |
} |
|
/* |
* Draw a rectangular area using the current clipping region and the |
* specified bit map. This differs from rectangle drawing in that the |
* rectangle is drawn using the foreground color and possibly the background |
* color as determined by the bit map. Each row of bits is aligned to the |
* next bitmap word boundary (so there is padding at the end of the row). |
* The background bit values are only written if the gr_usebg flag |
* is set. |
*/ |
void |
GdBitmap(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, MWCOORD height, |
MWIMAGEBITS *imagebits) |
{ |
MWCOORD minx; |
MWCOORD maxx; |
MWPIXELVAL savecolor; /* saved foreground color */ |
MWIMAGEBITS bitvalue = 0; /* bitmap word value */ |
int bitcount; /* number of bits left in bitmap word */ |
|
switch (GdClipArea(psd, x, y, x + width - 1, y + height - 1)) { |
case CLIP_VISIBLE: |
/* |
* For size considerations, there's no low-level bitmap |
* draw so we've got to draw everything with per-point |
* clipping for the time being. |
if (gr_usebg) |
psd->FillRect(psd, x, y, x + width - 1, y + height - 1, |
gr_background); |
psd->DrawBitmap(psd, x, y, width, height, imagebits, gr_foreground); |
return; |
*/ |
break; |
|
case CLIP_INVISIBLE: |
return; |
} |
|
/* The rectangle is partially visible, so must do clipping. First |
* fill a rectangle in the background color if necessary. |
*/ |
if (gr_usebg) { |
savecolor = gr_foreground; |
gr_foreground = gr_background; |
/* note: change to fillrect*/ |
GdFillRect(psd, x, y, width, height); |
gr_foreground = savecolor; |
} |
minx = x; |
maxx = x + width - 1; |
bitcount = 0; |
while (height > 0) { |
if (bitcount <= 0) { |
bitcount = MWIMAGE_BITSPERIMAGE; |
bitvalue = *imagebits++; |
} |
if (MWIMAGE_TESTBIT(bitvalue) && GdClipPoint(psd, x, y)) |
psd->DrawPixel(psd, x, y, gr_foreground); |
bitvalue = MWIMAGE_SHIFTBIT(bitvalue); |
bitcount--; |
if (x++ == maxx) { |
x = minx; |
y++; |
height--; |
bitcount = 0; |
} |
} |
GdFixCursor(psd); |
} |
|
/* |
* Return true if color is in palette |
*/ |
MWBOOL |
GdColorInPalette(MWCOLORVAL cr,MWPALENTRY *palette,int palsize) |
{ |
int i; |
|
for(i=0; i<palsize; ++i) |
if(GETPALENTRY(palette, i) == cr) |
return TRUE; |
return FALSE; |
} |
|
/* |
* Create a MWPIXELVAL conversion table between the passed palette |
* and the in-use palette. The system palette is loaded/merged according |
* to fLoadType. |
*/ |
void |
GdMakePaletteConversionTable(PSD psd,MWPALENTRY *palette,int palsize, |
MWPIXELVAL *convtable,int fLoadType) |
{ |
int i; |
MWCOLORVAL cr; |
int newsize, nextentry; |
MWPALENTRY newpal[256]; |
|
/* |
* Check for load palette completely, or add colors |
* from passed palette to system palette until full. |
*/ |
if(psd->pixtype == MWPF_PALETTE) { |
switch(fLoadType) { |
case LOADPALETTE: |
/* Load palette from beginning with image's palette. |
* First palette entries are Microwindows colors |
* and not changed. |
*/ |
GdSetPalette(psd, gr_firstuserpalentry, palsize, palette); |
break; |
|
case MERGEPALETTE: |
/* get system palette*/ |
for(i=0; i<(int)psd->ncolors; ++i) |
newpal[i] = gr_palette[i]; |
|
/* merge passed palette into system palette*/ |
newsize = 0; |
nextentry = gr_nextpalentry; |
|
/* if color missing and there's room, add it*/ |
for(i=0; i<palsize && nextentry < (int)psd->ncolors; ++i) { |
cr = GETPALENTRY(palette, i); |
if(!GdColorInPalette(cr, newpal, nextentry)) { |
newpal[nextentry++] = palette[i]; |
++newsize; |
} |
} |
|
/* set the new palette if any color was added*/ |
if(newsize) { |
GdSetPalette(psd, gr_nextpalentry, newsize, |
&newpal[gr_nextpalentry]); |
gr_nextpalentry += newsize; |
} |
break; |
} |
} |
|
/* |
* Build conversion table from inuse system palette and |
* passed palette. This will load RGB values directly |
* if running truecolor, otherwise it will find the |
* nearest color from the inuse palette. |
* FIXME: tag the conversion table to the bitmap image |
*/ |
for(i=0; i<palsize; ++i) { |
cr = GETPALENTRY(palette, i); |
convtable[i] = GdFindColor(cr); |
} |
} |
|
/* |
* Draw a color bitmap image in 1, 4, 8, 24 or 32 bits per pixel. The |
* Microwindows color image format is DWORD padded bytes, with |
* the upper bits corresponding to the left side (identical to |
* the MS Windows format). This format is currently different |
* than the MWIMAGEBITS format, which uses word-padded bits |
* for monochrome display only, where the upper bits in the word |
* correspond with the left side. |
*/ |
void |
GdDrawImage(PSD psd, MWCOORD x, MWCOORD y, PMWIMAGEHDR pimage) |
{ |
MWCOORD minx; |
MWCOORD maxx; |
MWUCHAR bitvalue = 0; |
int bitcount; |
MWUCHAR *imagebits; |
MWCOORD height, width; |
MWPIXELVAL pixel; |
int clip; |
int extra, linesize; |
int rgborder; |
MWCOLORVAL cr; |
MWCOORD yoff; |
unsigned long transcolor; |
MWPIXELVAL convtable[256]; |
|
height = pimage->height; |
width = pimage->width; |
|
/* determine if entire image is clipped out, save clipresult for later*/ |
clip = GdClipArea(psd, x, y, x + width - 1, y + height - 1); |
if(clip == CLIP_INVISIBLE) |
return; |
|
transcolor = pimage->transcolor; |
|
/* |
* Merge the images's palette and build a palette index conversion table. |
*/ |
if (pimage->bpp <= 8) { |
if(!pimage->palette) { |
/* for jpeg's without a palette*/ |
for(yoff=0; yoff<pimage->palsize; ++yoff) |
convtable[yoff] = yoff; |
} else GdMakePaletteConversionTable(psd, pimage->palette, |
pimage->palsize, convtable, MERGEPALETTE); |
|
/* The following is no longer used. One reason is that it required */ |
/* the transparent color to be unique, which was unnessecary */ |
|
/* convert transcolor to converted palette index for speed*/ |
/* if (transcolor != -1L) |
transcolor = (unsigned long) convtable[transcolor]; */ |
} |
|
minx = x; |
maxx = x + width - 1; |
imagebits = pimage->imagebits; |
|
/* check for bottom-up image*/ |
if(pimage->compression & MWIMAGE_UPSIDEDOWN) { |
y += height - 1; |
yoff = -1; |
} else |
yoff = 1; |
|
#define PIX2BYTES(n) (((n)+7)/8) |
/* imagebits are dword aligned*/ |
switch(pimage->bpp) { |
default: |
case 8: |
linesize = width; |
break; |
case 32: |
linesize = width*4; |
break; |
case 24: |
linesize = width*3; |
break; |
case 4: |
linesize = PIX2BYTES(width<<2); |
break; |
case 1: |
linesize = PIX2BYTES(width); |
break; |
} |
extra = pimage->pitch - linesize; |
|
/* 24bpp RGB rather than BGR byte order?*/ |
rgborder = pimage->compression & MWIMAGE_RGB; |
|
bitcount = 0; |
while(height > 0) { |
unsigned long trans = 0; |
|
if (bitcount <= 0) { |
bitcount = sizeof(MWUCHAR) * 8; |
bitvalue = *imagebits++; |
} |
switch(pimage->bpp) { |
case 24: |
case 32: |
cr = rgborder? MWRGB(bitvalue, imagebits[0], imagebits[1]): |
MWRGB(imagebits[1], imagebits[0], bitvalue); |
|
/* Include the upper bits for transcolor stuff */ |
if (imagebits[2]) /* FIXME: 24bpp error*/ |
trans = cr | 0x01000000L; |
|
if (pimage->bpp == 32) |
imagebits += 3; |
else imagebits += 2; |
bitcount = 0; |
|
/* handle transparent color*/ |
if (transcolor == trans) |
goto next; |
|
switch(psd->pixtype) { |
case MWPF_PALETTE: |
default: |
pixel = GdFindColor(cr); |
break; |
case MWPF_TRUECOLOR0888: |
case MWPF_TRUECOLOR888: |
pixel = COLOR2PIXEL888(cr); |
break; |
case MWPF_TRUECOLOR565: |
pixel = COLOR2PIXEL565(cr); |
break; |
case MWPF_TRUECOLOR555: |
pixel = COLOR2PIXEL555(cr); |
break; |
case MWPF_TRUECOLOR332: |
pixel = COLOR2PIXEL332(cr); |
break; |
} |
break; |
default: |
case 8: |
bitcount = 0; |
if (bitvalue == transcolor) |
goto next; |
|
pixel = convtable[bitvalue]; |
break; |
case 4: |
if (((bitvalue & 0xf0) >> 4) == transcolor) { |
bitvalue <<= 4; |
bitcount -= 4; |
goto next; |
} |
|
pixel = convtable[(bitvalue & 0xf0) >> 4]; |
bitvalue <<= 4; |
bitcount -= 4; |
break; |
case 1: |
--bitcount; |
if (((bitvalue & 0x80) ? 1 : 0) == transcolor) { |
bitvalue <<= 1; |
goto next; |
} |
|
pixel = convtable[(bitvalue & 0x80)? 1: 0]; |
bitvalue <<= 1; |
break; |
} |
|
/* if((unsigned long)pixel != transcolor &&*/ |
if (clip == CLIP_VISIBLE || GdClipPoint(psd, x, y)) |
psd->DrawPixel(psd, x, y, pixel); |
#if 0 |
/* fix: use clipmaxx to clip quicker*/ |
else if(clip != CLIP_VISIBLE && !clipresult && x > clipmaxx) { |
x = maxx; |
} |
#endif |
next: |
if(x++ == maxx) { |
x = minx; |
y += yoff; |
height--; |
bitcount = 0; |
imagebits += extra; |
} |
} |
GdFixCursor(psd); |
} |
|
/* |
* Read a rectangular area of the screen. |
* The color table is indexed row by row. |
*/ |
void |
GdReadArea(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, MWCOORD height, |
MWPIXELVAL *pixels) |
{ |
MWCOORD row; |
MWCOORD col; |
|
if (width <= 0 || height <= 0) |
return; |
|
GdCheckCursor(psd, x, y, x+width-1, y+height-1); |
for (row = y; row < height+y; row++) |
for (col = x; col < width+x; col++) |
if (row < 0 || row >= psd->yvirtres || |
col < 0 || col >= psd->xvirtres) |
*pixels++ = 0; |
else *pixels++ = psd->ReadPixel(psd, col, row); |
|
GdFixCursor(psd); |
} |
|
/* Draw a rectangle of color values, clipping if necessary. |
* If a color matches the background color, |
* then that pixel is only drawn if the gr_usebg flag is set. |
* |
* The pixels are packed according to pixtype: |
* |
* pixtype array of |
* MWPF_RGB MWCOLORVAL (unsigned long) |
* MWPF_PIXELVAL MWPIXELVAL (compile-time dependent) |
* MWPF_PALETTE unsigned char |
* MWPF_TRUECOLOR0888 unsigned long |
* MWPF_TRUECOLOR888 packed struct {char r,char g,char b} (24 bits) |
* MWPF_TRUECOLOR565 unsigned short |
* MWPF_TRUECOLOR555 unsigned short |
* MWPF_TRUECOLOR332 unsigned char |
* |
* NOTE: Currently, no translation is performed if the pixtype |
* is not MWPF_RGB. Pixtype is only then used to determine the |
* packed size of the pixel data, and is then stored unmodified |
* in a MWPIXELVAL and passed to the screen driver. Virtually, |
* this means there's only three reasonable options for client |
* programs: (1) pass all data as RGB MWCOLORVALs, (2) pass |
* data as unpacked 32-bit MWPIXELVALs in the format the current |
* screen driver is running, or (3) pass data as packed values |
* in the format the screen driver is running. Options 2 and 3 |
* are identical except for the packing structure. |
*/ |
void |
GdArea(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, MWCOORD height, void *pixels, |
int pixtype) |
{ |
unsigned char *PIXELS = pixels; /* for ANSI compilers, can't use void*/ |
long cellstodo; /* remaining number of cells */ |
long count; /* number of cells of same color */ |
long cc; /* current cell count */ |
long rows; /* number of complete rows */ |
MWCOORD minx; /* minimum x value */ |
MWCOORD maxx; /* maximum x value */ |
MWPIXELVAL savecolor; /* saved foreground color */ |
MWBOOL dodraw; /* TRUE if draw these points */ |
MWCOLORVAL rgbcolor = 0L; |
int pixsize; |
unsigned char r, g, b; |
|
minx = x; |
maxx = x + width - 1; |
|
/* Set up area clipping, and just return if nothing is visible */ |
if ( GdClipArea(psd, minx, y, maxx, y + height - 1) == CLIP_INVISIBLE ) |
return; |
|
/* psd->DrawArea driver call temp removed, doesn't work with new blit routines*/ |
#if 0000 |
{ |
driver_gc_t hwgc; |
int px1, px2, py1, py2, pw, ph, rx1, rx2, ry1, ry2; |
#if DYNAMICREGIONS |
MWRECT *prc; |
extern MWCLIPREGION *clipregion; |
#else |
MWCLIPRECT *prc; |
extern MWCLIPRECT cliprects[]; |
extern int clipcount; |
#endif |
|
#if HAVE_T1LIB_SUPPORT | HAVE_FREETYPE_SUPPORT |
/* can't use drawarea driver in 16 bpp mode yet with font routines*/ |
goto fallback; |
#endif |
if ( !(psd->flags & PSF_HAVEOP_COPY) ) |
goto fallback; |
|
#if DYNAMICREGIONS |
prc = clipregion->rects; |
count = clipregion->numRects; |
#else |
prc = cliprects; |
count = clipcount; |
#endif |
|
hwgc.pixels = PIXELS; |
hwgc.src_linelen = width; |
hwgc.gr_usebg = gr_usebg; |
hwgc.bg_color = gr_background; |
|
while ( count-- > 0 ) { |
#if DYNAMICREGIONS |
rx1 = prc->left; |
ry1 = prc->top; |
rx2 = prc->right; |
ry2 = prc->bottom; |
#else |
/* New clip-code by Morten */ |
rx1 = prc->x; |
ry1 = prc->y; |
rx2 = prc->x + prc->width; |
ry2 = prc->y + prc->height; |
#endif |
|
/* Check if this rect intersects with the one we draw */ |
px1 = x; |
py1 = y; |
px2 = x + width; |
py2 = y + height; |
if ( px1 < rx1 ) px1 = rx1; |
if ( py1 < ry1 ) py1 = ry1; |
if ( px2 > rx2 ) px2 = rx2; |
if ( py2 > ry2 ) py2 = ry2; |
|
pw = px2 - px1; |
ph = py2 - py1; |
|
if ( pw > 0 && ph > 0 ) { |
hwgc.dstx = px1; |
hwgc.dsty = py1; |
hwgc.dstw = pw; |
hwgc.dsth = ph; |
hwgc.srcx = px1 - x; |
hwgc.srcy = py1 - y; |
GdCheckCursor(psd,px1,py1,px1+pw-1,py1+ph-1); |
psd->DrawArea(psd,&hwgc,PSDOP_COPY); |
} |
prc++; |
} |
GdFixCursor(psd); |
return; |
fallback: |
} |
#endif /* if 0000 temp removed*/ |
|
/* Calculate size of packed pixels*/ |
switch(pixtype) { |
case MWPF_RGB: |
pixsize = sizeof(MWCOLORVAL); |
break; |
case MWPF_PIXELVAL: |
pixsize = sizeof(MWPIXELVAL); |
break; |
case MWPF_PALETTE: |
case MWPF_TRUECOLOR332: |
pixsize = sizeof(unsigned char); |
break; |
case MWPF_TRUECOLOR0888: |
pixsize = sizeof(unsigned long); |
break; |
case MWPF_TRUECOLOR888: |
pixsize = 3; |
break; |
case MWPF_TRUECOLOR565: |
case MWPF_TRUECOLOR555: |
pixsize = sizeof(unsigned short); |
break; |
default: |
return; |
} |
|
savecolor = gr_foreground; |
cellstodo = (long)width * height; |
while (cellstodo > 0) { |
/* read the pixel value from the pixtype*/ |
switch(pixtype) { |
case MWPF_RGB: |
rgbcolor = *(MWCOLORVAL *)PIXELS; |
PIXELS += sizeof(MWCOLORVAL); |
gr_foreground = GdFindColor(rgbcolor); |
break; |
case MWPF_PIXELVAL: |
gr_foreground = *(MWPIXELVAL *)PIXELS; |
PIXELS += sizeof(MWPIXELVAL); |
break; |
case MWPF_PALETTE: |
case MWPF_TRUECOLOR332: |
gr_foreground = *PIXELS++; |
break; |
case MWPF_TRUECOLOR0888: |
gr_foreground = *(unsigned long *)PIXELS; |
PIXELS += sizeof(unsigned long); |
break; |
case MWPF_TRUECOLOR888: |
r = *PIXELS++; |
g = *PIXELS++; |
b = *PIXELS++; |
gr_foreground = (MWPIXELVAL)MWRGB(r, g, b); |
break; |
case MWPF_TRUECOLOR565: |
case MWPF_TRUECOLOR555: |
gr_foreground = *(unsigned short *)PIXELS; |
PIXELS += sizeof(unsigned short); |
break; |
} |
dodraw = (gr_usebg || (gr_foreground != gr_background)); |
count = 1; |
--cellstodo; |
|
/* See how many of the adjacent remaining points have the |
* same color as the next point. |
* |
* NOTE: Yes, with the addition of the pixel unpacking, |
* it's almost slower to look ahead than to just draw |
* the pixel... FIXME |
*/ |
while (cellstodo > 0) { |
switch(pixtype) { |
case MWPF_RGB: |
if(rgbcolor != *(MWCOLORVAL *)PIXELS) |
goto breakwhile; |
PIXELS += sizeof(MWCOLORVAL); |
break; |
case MWPF_PIXELVAL: |
if(gr_foreground != *(MWPIXELVAL *)PIXELS) |
goto breakwhile; |
PIXELS += sizeof(MWPIXELVAL); |
break; |
case MWPF_PALETTE: |
case MWPF_TRUECOLOR332: |
if(gr_foreground != *(unsigned char *)PIXELS) |
goto breakwhile; |
++PIXELS; |
break; |
case MWPF_TRUECOLOR0888: |
if(gr_foreground != *(unsigned long *)PIXELS) |
goto breakwhile; |
PIXELS += sizeof(unsigned long); |
break; |
case MWPF_TRUECOLOR888: |
r = *(unsigned char *)PIXELS; |
g = *(unsigned char *)(PIXELS + 1); |
b = *(unsigned char *)(PIXELS + 2); |
if(gr_foreground != (MWPIXELVAL)MWRGB(r, g, b)) |
goto breakwhile; |
PIXELS += 3; |
break; |
case MWPF_TRUECOLOR565: |
case MWPF_TRUECOLOR555: |
if(gr_foreground != *(unsigned short *)PIXELS) |
goto breakwhile; |
PIXELS += sizeof(unsigned short); |
break; |
} |
++count; |
--cellstodo; |
} |
breakwhile: |
|
/* If there is only one point with this color, then draw it |
* by itself. |
*/ |
if (count == 1) { |
if (dodraw) |
drawpoint(psd, x, y); |
if (++x > maxx) { |
x = minx; |
y++; |
} |
continue; |
} |
|
/* There are multiple points with the same color. If we are |
* not at the start of a row of the rectangle, then draw this |
* first row specially. |
*/ |
if (x != minx) { |
cc = count; |
if (x + cc - 1 > maxx) |
cc = maxx - x + 1; |
if (dodraw) |
drawrow(psd, x, x + cc - 1, y); |
count -= cc; |
x += cc; |
if (x > maxx) { |
x = minx; |
y++; |
} |
} |
|
/* Now the x value is at the beginning of a row if there are |
* any points left to be drawn. Draw all the complete rows |
* with one call. |
*/ |
rows = count / width; |
if (rows > 0) { |
if (dodraw) { |
/* note: change to fillrect, (parm types changed)*/ |
/*GdFillRect(psd, x, y, maxx, y + rows - 1);*/ |
GdFillRect(psd, x, y, maxx - x + 1, rows); |
} |
count %= width; |
y += rows; |
} |
|
/* If there is a final partial row of pixels left to be |
* drawn, then do that. |
*/ |
if (count > 0) { |
if (dodraw) |
drawrow(psd, x, x + count - 1, y); |
x += count; |
} |
} |
gr_foreground = savecolor; |
GdFixCursor(psd); |
} |
|
#if NOTYET |
/* Copy a rectangular area from one screen area to another. |
* This bypasses clipping. |
*/ |
void |
GdCopyArea(PSD psd, MWCOORD srcx, MWCOORD srcy, MWCOORD width, MWCOORD height, |
MWCOORD destx, MWCOORD desty) |
{ |
if (width <= 0 || height <= 0) |
return; |
|
if (srcx == destx && srcy == desty) |
return; |
GdCheckCursor(psd, srcx, srcy, srcx + width - 1, srcy + height - 1); |
GdCheckCursor(psd, destx, desty, destx + width - 1, desty + height - 1); |
psd->CopyArea(psd, srcx, srcy, width, height, destx, desty); |
GdFixCursor(psd); |
} |
#endif |
|
/* Copy source rectangle of pixels to destination rectangle quickly*/ |
void |
GdBlit(PSD dstpsd, MWCOORD dstx, MWCOORD dsty, MWCOORD width, MWCOORD height, |
PSD srcpsd, MWCOORD srcx, MWCOORD srcy, long rop) |
{ |
int rx1, rx2, ry1, ry2; |
int px1, px2, py1, py2; |
int pw, ph; |
int count; |
#if DYNAMICREGIONS |
MWRECT * prc; |
extern MWCLIPREGION *clipregion; |
#else |
MWCLIPRECT * prc; |
extern MWCLIPRECT cliprects[]; |
extern int clipcount; |
#endif |
|
/*FIXME: compare bpp's and convert if necessary*/ |
assert(dstpsd->planes == srcpsd->planes); |
assert(dstpsd->bpp == srcpsd->bpp); |
|
/* temporary assert() until rotation blits completed*/ |
assert(dstpsd->portrait == srcpsd->portrait); |
|
/* clip blit rectangle to source screen/bitmap size*/ |
/* we must do this because there isn't any source clipping setup*/ |
if(srcx < 0) { |
width += srcx; |
dstx -= srcx; |
srcx = 0; |
} |
if(srcy < 0) { |
height += srcy; |
dsty -= srcy; |
srcy = 0; |
} |
if(srcx+width > srcpsd->xvirtres) |
width = srcpsd->xvirtres - srcx; |
if(srcy+height > srcpsd->yvirtres) |
height = srcpsd->yvirtres - srcy; |
|
switch(GdClipArea(dstpsd, dstx, dsty, dstx+width-1, dsty+height-1)) { |
case CLIP_VISIBLE: |
/* check cursor in src region*/ |
GdCheckCursor(dstpsd, srcx, srcy, srcx+width-1, srcy+height-1); |
dstpsd->Blit(dstpsd, dstx, dsty, width, height, |
srcpsd, srcx, srcy, rop); |
GdFixCursor(dstpsd); |
return; |
|
case CLIP_INVISIBLE: |
return; |
} |
|
/* Partly clipped, we'll blit using destination clip |
* rectangles, and offset the blit accordingly. |
* Since the destination is already clipped, we |
* only need to clip the source here. |
*/ |
#if DYNAMICREGIONS |
prc = clipregion->rects; |
count = clipregion->numRects; |
#else |
prc = cliprects; |
count = clipcount; |
#endif |
while(--count >= 0) { |
#if DYNAMICREGIONS |
rx1 = prc->left; |
ry1 = prc->top; |
rx2 = prc->right; |
ry2 = prc->bottom; |
#else |
rx1 = prc->x; |
ry1 = prc->y; |
rx2 = prc->x + prc->width; |
ry2 = prc->y + prc->height; |
#endif |
/* Check: does this rect intersect the one we want to draw? */ |
px1 = dstx; |
py1 = dsty; |
px2 = dstx + width; |
py2 = dsty + height; |
if (px1 < rx1) px1 = rx1; |
if (py1 < ry1) py1 = ry1; |
if (px2 > rx2) px2 = rx2; |
if (py2 > ry2) py2 = ry2; |
|
pw = px2 - px1; |
ph = py2 - py1; |
if(pw > 0 && ph > 0) { |
/* check cursor in dest and src regions*/ |
GdCheckCursor(dstpsd, px1, py1, px2-1, py2-1); |
GdCheckCursor(dstpsd, srcx, srcy, |
srcx+width, srcy+height); |
dstpsd->Blit(dstpsd, px1, py1, pw, ph, srcpsd, |
srcx + (px1-dstx), srcy + (py1-dsty), rop); |
} |
++prc; |
} |
GdFixCursor(dstpsd); |
} |
|
/* experimental globals for ratio bug when src != 0*/ |
int g_row_inc, g_col_inc; |
/* Stretch source rectangle of pixels to destination rectangle quickly*/ |
void |
GdStretchBlit(PSD dstpsd, MWCOORD dstx, MWCOORD dsty, MWCOORD dstw, |
MWCOORD dsth, PSD srcpsd, MWCOORD srcx, MWCOORD srcy, MWCOORD srcw, |
MWCOORD srch, long rop) |
{ |
int count; |
#if DYNAMICREGIONS |
MWRECT * prc; |
extern MWCLIPREGION *clipregion; |
#else |
MWCLIPRECT * prc; |
extern MWCLIPRECT cliprects[]; |
extern int clipcount; |
#endif |
|
g_row_inc = g_col_inc = 0; |
|
/* check for driver stretch blit implementation*/ |
if (!dstpsd->StretchBlit) |
return; |
|
/*FIXME: compare bpp's and convert if necessary*/ |
assert(dstpsd->planes == srcpsd->planes); |
assert(dstpsd->bpp == srcpsd->bpp); |
|
/* clip blit rectangle to source screen/bitmap size*/ |
/* we must do this because there isn't any source clipping setup*/ |
if(srcx < 0) { |
srcw += srcx; |
/*dstx -= srcx;*/ |
srcx = 0; |
} |
if(srcy < 0) { |
srch += srcy; |
/*dsty -= srcy;*/ |
srcy = 0; |
} |
if(srcx+srcw > srcpsd->xvirtres) |
srcw = srcpsd->xvirtres - srcx; |
if(srcy+srch > srcpsd->yvirtres) |
srch = srcpsd->yvirtres - srcy; |
|
/* temp dest clipping for partially visible case*/ |
if(dstx+dstw > dstpsd->xvirtres) |
dstw = dstpsd->xvirtres - dstx; |
if(dsty+dsth > dstpsd->yvirtres) |
dsth = dstpsd->yvirtres - dsty; |
|
switch(GdClipArea(dstpsd, dstx, dsty, dstx+dstw-1, dsty+dsth-1)) { |
case CLIP_VISIBLE: |
/* check cursor in src region*/ |
GdCheckCursor(dstpsd, srcx, srcy, srcx+srcw-1, srcy+srch-1); |
dstpsd->StretchBlit(dstpsd, dstx, dsty, dstw, dsth, |
srcpsd, srcx, srcy, srcw, srch, rop); |
GdFixCursor(dstpsd); |
return; |
|
case CLIP_INVISIBLE: |
return; |
} |
|
/* Partly clipped, we'll blit using destination clip |
* rectangles, and offset the blit accordingly. |
* Since the destination is already clipped, we |
* only need to clip the source here. |
*/ |
#if DYNAMICREGIONS |
prc = clipregion->rects; |
count = clipregion->numRects; |
#else |
prc = cliprects; |
count = clipcount; |
#endif |
while(--count >= 0) { |
int rx1, rx2, ry1, ry2; |
int px1, px2, py1, py2; |
int pw, ph; |
int sx, sy, sw, sh; |
#if DYNAMICREGIONS |
rx1 = prc->left; |
ry1 = prc->top; |
rx2 = prc->right; |
ry2 = prc->bottom; |
#else |
rx1 = prc->x; |
ry1 = prc->y; |
rx2 = prc->x + prc->width; |
ry2 = prc->y + prc->height; |
#endif |
/* Check: does this rect intersect the one we want to draw? */ |
px1 = dstx; |
py1 = dsty; |
px2 = dstx + dstw; |
py2 = dsty + dsth; |
if (px1 < rx1) px1 = rx1; |
if (py1 < ry1) py1 = ry1; |
if (px2 > rx2) px2 = rx2; |
if (py2 > ry2) py2 = ry2; |
|
pw = px2 - px1; |
ph = py2 - py1; |
if(pw > 0 && ph > 0) { |
/* calc proper src/dst offset for stretch rect*/ |
g_row_inc = (srch << 16) / dsth; |
g_col_inc = (srcw << 16) / dstw; |
sw = pw * srcw / dstw; |
sh = ph * srch / dsth; |
|
if (sw > 0 && sh > 0) { |
sx = srcx + (px1-dstx) * srcw / dstw; |
sy = srcy + (py1-dsty) * srch / dsth; |
/*printf("P %d,%d,%d,%d %d,%d\n", sx, sy, sw, sh, g_row_inc, g_col_inc);*/ |
|
/* check cursor in dest and src regions*/ |
GdCheckCursor(dstpsd, px1, py1, px2-1, py2-1); |
GdCheckCursor(dstpsd, srcx, srcy, srcx+srcw, srcy+srch); |
dstpsd->StretchBlit(dstpsd, px1, py1, pw, ph, srcpsd, |
sx, sy, sw, sh, rop); |
} |
} |
++prc; |
} |
GdFixCursor(dstpsd); |
} |
|
/* |
* Calculate size and linelen of memory gc. |
* If bpp or planes is 0, use passed psd's bpp/planes. |
* Note: linelen is calculated to be DWORD aligned for speed |
* for bpp <= 8. Linelen is converted to bytelen for bpp > 8. |
*/ |
int |
GdCalcMemGCAlloc(PSD psd, unsigned int width, unsigned int height, int planes, |
int bpp, int *psize, int *plinelen) |
{ |
int bytelen, linelen, tmp; |
|
if(!planes) |
planes = psd->planes; |
if(!bpp) |
bpp = psd->bpp; |
/* |
* swap width and height in left/right portrait modes, |
* so imagesize is calculated properly |
*/ |
if(psd->portrait & (MWPORTRAIT_LEFT|MWPORTRAIT_RIGHT)) { |
tmp = width; |
width = height; |
height = tmp; |
} |
|
/* |
* use bpp and planes to create size and linelen. |
* linelen is in bytes for bpp 1, 2, 4, 8, and pixels for bpp 16,24,32. |
*/ |
if(planes == 1) { |
switch(bpp) { |
case 1: |
linelen = (width+7)/8; |
bytelen = linelen = (linelen+3) & ~3; |
break; |
case 2: |
linelen = (width+3)/4; |
bytelen = linelen = (linelen+3) & ~3; |
break; |
case 4: |
linelen = (width+1)/2; |
bytelen = linelen = (linelen+3) & ~3; |
break; |
case 8: |
bytelen = linelen = (width+3) & ~3; |
break; |
case 16: |
linelen = width; |
bytelen = width * 2; |
break; |
case 24: |
linelen = width; |
bytelen = width * 3; |
break; |
case 32: |
linelen = width; |
bytelen = width * 4; |
break; |
default: |
return 0; |
} |
} else if(planes == 4) { |
/* FIXME assumes VGA 4 planes 4bpp*/ |
/* we use 4bpp linear for memdc format*/ |
linelen = (width+1)/2; |
linelen = (linelen+3) & ~3; |
bytelen = linelen; |
} else { |
*psize = *plinelen = 0; |
return 0; |
} |
|
*plinelen = linelen; |
*psize = bytelen * height; |
return 1; |
} |
|
/* Translate a rectangle of color values |
* |
* The pixels are packed according to inpixtype/outpixtype: |
* |
* pixtype array of |
* MWPF_RGB MWCOLORVAL (unsigned long) |
* MWPF_PIXELVAL MWPIXELVAL (compile-time dependent) |
* MWPF_PALETTE unsigned char |
* MWPF_TRUECOLOR0888 unsigned long |
* MWPF_TRUECOLOR888 packed struct {char r,char g,char b} (24 bits) |
* MWPF_TRUECOLOR565 unsigned short |
* MWPF_TRUECOLOR555 unsigned short |
* MWPF_TRUECOLOR332 unsigned char |
*/ |
void |
GdTranslateArea(MWCOORD width, MWCOORD height, void *in, int inpixtype, |
MWCOORD inpitch, void *out, int outpixtype, int outpitch) |
{ |
unsigned char * inbuf = in; |
unsigned char * outbuf = out; |
unsigned long pixelval; |
MWCOLORVAL colorval; |
MWCOORD x, y; |
unsigned char r, g, b; |
extern MWPALENTRY gr_palette[256]; |
int gr_palsize = 256; /* FIXME*/ |
|
for(y=0; y<height; ++y) { |
for(x=0; x<width; ++x) { |
/* read pixel value and convert to BGR colorval (0x00BBGGRR)*/ |
switch (inpixtype) { |
case MWPF_RGB: |
colorval = *(MWCOLORVAL *)inbuf; |
inbuf += sizeof(MWCOLORVAL); |
break; |
case MWPF_PIXELVAL: |
pixelval = *(MWPIXELVAL *)inbuf; |
inbuf += sizeof(MWPIXELVAL); |
/* convert based on compile-time MWPIXEL_FORMAT*/ |
#if MWPIXEL_FORMAT == MWPF_PALETTE |
colorval = GETPALENTRY(gr_palette, pixelval); |
#else |
colorval = PIXELVALTOCOLORVAL(pixelval); |
#endif |
break; |
case MWPF_PALETTE: |
pixelval = *inbuf++; |
colorval = GETPALENTRY(gr_palette, pixelval); |
break; |
case MWPF_TRUECOLOR332: |
pixelval = *inbuf++; |
colorval = PIXEL332TOCOLORVAL(pixelval); |
break; |
case MWPF_TRUECOLOR0888: |
pixelval = *(unsigned long *)inbuf; |
colorval = PIXEL888TOCOLORVAL(pixelval); |
inbuf += sizeof(unsigned long); |
break; |
case MWPF_TRUECOLOR888: |
r = *inbuf++; |
g = *inbuf++; |
b = *inbuf++; |
colorval = (MWPIXELVAL)MWRGB(r, g, b); |
break; |
case MWPF_TRUECOLOR565: |
pixelval = *(unsigned short *)inbuf; |
colorval = PIXEL565TOCOLORVAL(pixelval); |
inbuf += sizeof(unsigned short); |
break; |
case MWPF_TRUECOLOR555: |
pixelval = *(unsigned short *)inbuf; |
colorval = PIXEL555TOCOLORVAL(pixelval); |
inbuf += sizeof(unsigned short); |
break; |
default: |
return; |
} |
|
/* convert from BGR colorval to desired output pixel format*/ |
switch (outpixtype) { |
case MWPF_RGB: |
*(MWCOLORVAL *)outbuf = colorval; |
outbuf += sizeof(MWCOLORVAL); |
break; |
case MWPF_PIXELVAL: |
/* convert based on compile-time MWPIXEL_FORMAT*/ |
#if MWPIXEL_FORMAT == MWPF_PALETTE |
*(MWPIXELVAL *)outbuf = GdFindNearestColor(gr_palette, |
gr_palsize, colorval); |
#else |
*(MWPIXELVAL *)outbuf = COLORVALTOPIXELVAL(colorval); |
#endif |
outbuf += sizeof(MWPIXELVAL); |
break; |
case MWPF_PALETTE: |
*outbuf++ = GdFindNearestColor(gr_palette, gr_palsize, |
colorval); |
break; |
case MWPF_TRUECOLOR332: |
*outbuf++ = COLOR2PIXEL332(colorval); |
break; |
case MWPF_TRUECOLOR0888: |
*(unsigned long *)outbuf = COLOR2PIXEL888(colorval); |
outbuf += sizeof(unsigned long); |
break; |
case MWPF_TRUECOLOR888: |
*outbuf++ = REDVALUE(colorval); |
*outbuf++ = GREENVALUE(colorval); |
*outbuf++ = BLUEVALUE(colorval); |
break; |
case MWPF_TRUECOLOR565: |
*(unsigned short *)outbuf = COLOR2PIXEL565(colorval); |
outbuf += sizeof(unsigned short); |
break; |
case MWPF_TRUECOLOR555: |
*(unsigned short *)outbuf = COLOR2PIXEL555(colorval); |
outbuf += sizeof(unsigned short); |
break; |
} |
} |
|
/* adjust line widths, if necessary*/ |
if(inpitch > width) |
inbuf += inpitch - width; |
if(outpitch > width) |
outbuf += outpitch - width; |
} |
} |
/devpal2.c
0,0 → 1,18
/* |
* Copyright (c) 1999 Greg Haerr <greg@censoft.com> |
* |
* 2pbb (4 color) standard palette definition |
*/ |
#include "device.h" |
|
/* |
* Standard palette for Everex Freestyle Palm PC |
* This palette is in reverse order from some 2bpp systems. |
* That is, white is pixel value 0, while black is 3. |
*/ |
MWPALENTRY mwstdpal2[4] = { |
RGBDEF( 255, 255, 255 ), /* white*/ |
RGBDEF( 192, 192, 192 ), /* ltgray*/ |
RGBDEF( 128, 128, 128 ), /* gray*/ |
RGBDEF( 0 , 0 , 0 ) /* black*/ |
}; |