diff options
author | Pixel <pixel@nobis-crew.org> | 2010-06-15 00:18:43 -0700 |
---|---|---|
committer | Pixel <pixel@nobis-crew.org> | 2010-06-15 00:18:43 -0700 |
commit | 25e85e1b809ec58ecac0f2e8fe48f74836f8e131 (patch) | |
tree | a53caef2257fefdd6610a17427fd14ee656bbc92 /cd/src/gdk/cdgdk.c | |
parent | 7c0c85a86aa73c0c495523f994f8412e377a8195 (diff) |
Upgrading to CD 5.3
Diffstat (limited to 'cd/src/gdk/cdgdk.c')
-rw-r--r-- | cd/src/gdk/cdgdk.c | 1713 |
1 files changed, 1713 insertions, 0 deletions
diff --git a/cd/src/gdk/cdgdk.c b/cd/src/gdk/cdgdk.c new file mode 100644 index 0000000..b0c8ea8 --- /dev/null +++ b/cd/src/gdk/cdgdk.c @@ -0,0 +1,1713 @@ +/** \file + * \brief Gdk Base Driver + * + * See Copyright Notice in cd.h + */ + +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <math.h> + +#include "cdgdk.h" + +#include <gdk/gdk.h> + +#define NUM_HATCHES 6 +#define HATCH_WIDTH 8 +#define HATCH_HEIGHT 8 + +/* +** 6 predefined patterns to be accessed through cdHatch( + CD_HORIZONTAL | CD_VERTICAL | CD_FDIAGONAL | CD_BDIAGONAL | + CD_CROSS | CD_DIAGCROSS) + +*/ +static char hatches[NUM_HATCHES][8] = { + {0x00,0x00,0xFF,0x00,0x00,0x00,0xFF,0x00}, /* HORIZONTAL */ + {0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22}, /* VERTICAL */ + {0x08,0x10,0x20,0x40,0x80,0x01,0x02,0x04}, /* FDIAGONAL */ + {0x10,0x08,0x04,0x02,0x01,0x80,0x40,0x20}, /* BDIAGONAL */ + {0x22,0x22,0xFF,0x22,0x22,0x22,0xFF,0x22}, /* CROSS */ + {0x18,0x18,0x24,0x42,0x81,0x81,0x42,0x24} /* DIAGCROSS */ +}; + +/******************************************************/ + +static int gdkStrIsAscii(const char* str) +{ + while(*str) + { + int c = *str; + if (c < 0) + return 0; + str++; + } + return 1; +} + +static char* gdkStrToUTF8(const char *str, const char* charset, int length) +{ + return g_convert(str, length, "UTF-8", charset, NULL, NULL, NULL); +} + +char* cdgdkStrConvertToUTF8(cdCtxCanvas *ctxcanvas, const char* str, int length) /* From CD to GTK/GDK */ +{ + const char *charset = NULL; + + if (!str || *str == 0) + return (char*)str; + + if (g_get_charset(&charset) == TRUE) /* current locale is already UTF-8 */ + { + if (g_utf8_validate(str, -1, NULL)) + { + return (char*)str; + } + else + { + ctxcanvas->gdkLastConvertUTF8 = gdkStrToUTF8(str, "ISO8859-1", length); /* if string is not UTF-8, assume ISO8859-1 */ + + if (!ctxcanvas->gdkLastConvertUTF8) + return (char*)str; + + return ctxcanvas->gdkLastConvertUTF8; + } + } + else + { + if (gdkStrIsAscii(str) || !charset) + { + return (char*)str; + } + else if (charset) + { + ctxcanvas->gdkLastConvertUTF8 = gdkStrToUTF8(str, charset, length); + + if (!ctxcanvas->gdkLastConvertUTF8) + return (char*)str; + + return ctxcanvas->gdkLastConvertUTF8; + } + } + return (char*)str; +} + +static GdkColor cdColorToGdk(unsigned long rgb) +{ + GdkColor clrRGB; + + clrRGB.red = cdCOLOR8TO16(cdRed(rgb)); + clrRGB.green = cdCOLOR8TO16(cdGreen(rgb)); + clrRGB.blue = cdCOLOR8TO16(cdBlue(rgb)); + + return clrRGB; +} + +/******************************************************/ + +void cdgdkKillCanvas(cdCtxCanvas *ctxcanvas) +{ + if (ctxcanvas->canvas->bpp <= 8) + { + if (ctxcanvas->colormap != gdk_gc_get_colormap(ctxcanvas->gc)) + g_object_unref(ctxcanvas->colormap); + } + + if (ctxcanvas->last_hatch) g_object_unref(ctxcanvas->last_hatch); + + if (ctxcanvas->fontdesc) pango_font_description_free(ctxcanvas->fontdesc); + if (ctxcanvas->fontlayout) g_object_unref(ctxcanvas->fontlayout); + if (ctxcanvas->fontcontext) g_object_unref(ctxcanvas->fontcontext); + + if (ctxcanvas->new_rgn) gdk_region_destroy(ctxcanvas->new_rgn); + if (ctxcanvas->clip_rgn) gdk_region_destroy(ctxcanvas->clip_rgn); + + if (ctxcanvas->last_pattern) + { + g_object_unref(ctxcanvas->last_pattern_gc); + g_object_unref(ctxcanvas->last_pattern); + } + + if (ctxcanvas->last_stipple) + { + g_object_unref(ctxcanvas->last_stipple_gc); + g_object_unref(ctxcanvas->last_stipple); + } + + g_object_unref(ctxcanvas->gc); + + free(ctxcanvas); +} + +/******************************************************/ + +static void cdflush(cdCtxCanvas *ctxcanvas) +{ + (void)ctxcanvas; + gdk_error_trap_push(); + gdk_flush(); + gdk_error_trap_pop(); +} + +/******************************************************/ + +static int cdclip(cdCtxCanvas *ctxcanvas, int clip_mode) +{ + switch (clip_mode) + { + case CD_CLIPOFF: + gdk_gc_set_clip_region(ctxcanvas->gc, NULL); + break; + case CD_CLIPAREA: + { + GdkRectangle rect; + rect.x = ctxcanvas->canvas->clip_rect.xmin; + rect.y = ctxcanvas->canvas->clip_rect.ymin; + rect.width = ctxcanvas->canvas->clip_rect.xmax - ctxcanvas->canvas->clip_rect.xmin; + rect.height = ctxcanvas->canvas->clip_rect.ymax - ctxcanvas->canvas->clip_rect.ymin; + gdk_gc_set_clip_rectangle(ctxcanvas->gc, &rect); + break; + } + case CD_CLIPPOLYGON: + if (ctxcanvas->clip_rgn) + gdk_gc_set_clip_region(ctxcanvas->gc, ctxcanvas->clip_rgn); + break; + case CD_CLIPREGION: + if (ctxcanvas->new_rgn) + gdk_gc_set_clip_region(ctxcanvas->gc, ctxcanvas->new_rgn); + break; + } + + return clip_mode; +} + +static void cdcliparea(cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax) +{ + if (ctxcanvas->canvas->clip_mode == CD_CLIPAREA) + { + ctxcanvas->canvas->clip_rect.xmin = xmin; + ctxcanvas->canvas->clip_rect.ymin = ymin; + ctxcanvas->canvas->clip_rect.xmax = xmax; + ctxcanvas->canvas->clip_rect.ymax = ymax; + cdclip(ctxcanvas, CD_CLIPAREA); + } +} + +static void cdnewregion(cdCtxCanvas *ctxcanvas) +{ + if (ctxcanvas->new_rgn) + gdk_region_destroy(ctxcanvas->new_rgn); + + ctxcanvas->new_rgn = gdk_region_new(); +} + +static int cdispointinregion(cdCtxCanvas *ctxcanvas, int x, int y) +{ + if (!ctxcanvas->new_rgn) + return 0; + + if (gdk_region_point_in(ctxcanvas->new_rgn, x, y)) + return 1; + + return 0; +} + +static void cdoffsetregion(cdCtxCanvas *ctxcanvas, int x, int y) +{ + if (!ctxcanvas->new_rgn) + return; + + gdk_region_offset(ctxcanvas->new_rgn, x, y); +} + +static void cdgetregionbox(cdCtxCanvas *ctxcanvas, int *xmin, int *xmax, int *ymin, int *ymax) +{ + GdkRectangle rect; + + if (!ctxcanvas->new_rgn) + return; + + gdk_region_get_clipbox(ctxcanvas->new_rgn, &rect); + + *xmin = rect.x; + *xmax = rect.x + rect.width; + *ymin = rect.y; + *ymax = rect.y + rect.height; +} + +static void sCombineRegion(cdCtxCanvas *ctxcanvas, GdkRegion* rgn) +{ + switch(ctxcanvas->canvas->combine_mode) + { + case CD_UNION: + gdk_region_union(ctxcanvas->new_rgn, rgn); + break; + case CD_INTERSECT: + gdk_region_intersect(ctxcanvas->new_rgn, rgn); + break; + case CD_DIFFERENCE: + gdk_region_subtract(ctxcanvas->new_rgn, rgn); + break; + case CD_NOTINTERSECT: + gdk_region_xor(ctxcanvas->new_rgn, rgn); + break; + } + + gdk_region_destroy(rgn); +} + +/******************************************************/ + +static int cdwritemode(cdCtxCanvas *ctxcanvas, int write_mode) +{ + switch (write_mode) + { + case CD_REPLACE: + gdk_gc_set_function(ctxcanvas->gc, GDK_COPY); + break; + case CD_XOR: + gdk_gc_set_function(ctxcanvas->gc, GDK_XOR); + break; + case CD_NOT_XOR: + gdk_gc_set_function(ctxcanvas->gc, GDK_EQUIV); + break; + } + + return write_mode; +} + +static int cdinteriorstyle(cdCtxCanvas *ctxcanvas, int style) +{ + GdkFill sty = GDK_SOLID; + + switch (style) + { + case CD_SOLID: + sty = GDK_SOLID; + break; + + case CD_HATCH : + if (!ctxcanvas->last_hatch) + return ctxcanvas->canvas->interior_style; + + gdk_gc_set_stipple(ctxcanvas->gc, ctxcanvas->last_hatch); + + if (ctxcanvas->canvas->back_opacity == CD_OPAQUE) + sty = GDK_OPAQUE_STIPPLED; + else + sty = GDK_STIPPLED; + break; + + case CD_STIPPLE: + gdk_gc_set_stipple(ctxcanvas->gc, ctxcanvas->last_stipple); + + if (ctxcanvas->canvas->back_opacity == CD_OPAQUE) + sty = GDK_OPAQUE_STIPPLED; + else + sty = GDK_STIPPLED; + break; + + case CD_PATTERN: + gdk_gc_set_tile(ctxcanvas->gc, ctxcanvas->last_pattern); + sty = GDK_TILED; + break; + } + + gdk_gc_set_fill(ctxcanvas->gc, sty); + + return style; +} + +static int cdhatch(cdCtxCanvas *ctxcanvas, int hatch_style) +{ + GdkColor fg, bg; + + if (ctxcanvas->last_hatch) + g_object_unref(ctxcanvas->last_hatch); + + fg.pixel = 1; + bg.pixel = 0; + + ctxcanvas->last_hatch = gdk_pixmap_create_from_data(ctxcanvas->wnd, hatches[hatch_style], + HATCH_WIDTH, HATCH_HEIGHT, 1, &fg, &bg); + + cdinteriorstyle(ctxcanvas, CD_HATCH); + + return hatch_style; +} + +static void cdstipple(cdCtxCanvas *ctxcanvas, int w, int h, const unsigned char *data) +{ + int x, y; + + if (ctxcanvas->last_stipple == 0 || (ctxcanvas->last_stipple_w != w || ctxcanvas->last_stipple_h != h)) + { + if (ctxcanvas->last_stipple != 0) + { + g_object_unref(ctxcanvas->last_stipple); + g_object_unref(ctxcanvas->last_stipple_gc); + } + + ctxcanvas->last_stipple = gdk_pixmap_new(ctxcanvas->wnd, w, h, 1); + if (!ctxcanvas->last_stipple) + return; + + ctxcanvas->last_stipple_gc = gdk_gc_new((GdkDrawable*)ctxcanvas->last_stipple); + ctxcanvas->last_stipple_w = w; + ctxcanvas->last_stipple_h = h; + } + + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + GdkColor clr; + + if(data[y*w+x]) + clr.pixel = 1; + else + clr.pixel = 0; + + gdk_gc_set_foreground(ctxcanvas->last_stipple_gc, &clr); + gdk_draw_point(ctxcanvas->last_stipple, ctxcanvas->last_stipple_gc, x, h-y-1); + } + } + + cdinteriorstyle(ctxcanvas, CD_STIPPLE); +} + +static void cdpattern(cdCtxCanvas *ctxcanvas, int w, int h, const long int *colors) +{ + int x, y; + GdkColor color; + + if (ctxcanvas->last_pattern == 0 || (ctxcanvas->last_pattern_w != w || ctxcanvas->last_pattern_h != h)) + { + if (ctxcanvas->last_pattern != 0) + { + g_object_unref(ctxcanvas->last_pattern); + g_object_unref(ctxcanvas->last_pattern_gc); + } + + ctxcanvas->last_pattern = gdk_pixmap_new(ctxcanvas->wnd, w, h, ctxcanvas->depth); + if (!ctxcanvas->last_pattern) + return; + + ctxcanvas->last_pattern_gc = gdk_gc_new((GdkDrawable*)ctxcanvas->last_pattern); + ctxcanvas->last_pattern_w = w; + ctxcanvas->last_pattern_h = h; + } + + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + color = cdColorToGdk(colors[y*w+x]); + gdk_gc_set_rgb_fg_color(ctxcanvas->last_pattern_gc, &color); + gdk_draw_point(ctxcanvas->last_pattern, ctxcanvas->last_pattern_gc, x, h-y-1); + } + } + + cdinteriorstyle(ctxcanvas, CD_PATTERN); +} + +static int cdlinestyle(cdCtxCanvas *ctxcanvas, int style) +{ + switch (style) + { + case CD_CONTINUOUS: + ctxcanvas->gcval.line_style = GDK_LINE_SOLID; + break; + case CD_DASHED: + case CD_DOTTED: + case CD_DASH_DOT: + case CD_DASH_DOT_DOT: + { + static struct { + int size; + signed char list[6]; + } dashes[4] = { + { 2, { 6, 2 } }, + { 2, { 2, 2 } }, + { 4, { 6, 2, 2, 2 } }, + { 6, { 6, 2, 2, 2, 2, 2 } } + }; + + if (ctxcanvas->canvas->back_opacity == CD_OPAQUE) + ctxcanvas->gcval.line_style = GDK_LINE_DOUBLE_DASH; + else + ctxcanvas->gcval.line_style = GDK_LINE_ON_OFF_DASH; + + gdk_gc_set_dashes(ctxcanvas->gc, 0, dashes[style-CD_DASHED].list, dashes[style-CD_DASHED].size); + break; + } + case CD_CUSTOM: + { + int i; + signed char* dash_style = (signed char*)malloc(ctxcanvas->canvas->line_dashes_count); + for (i = 0; i < ctxcanvas->canvas->line_dashes_count; i++) + dash_style[i] = (char)ctxcanvas->canvas->line_dashes[i]; + + if (ctxcanvas->canvas->back_opacity == CD_OPAQUE) + ctxcanvas->gcval.line_style = GDK_LINE_DOUBLE_DASH; + else + ctxcanvas->gcval.line_style = GDK_LINE_ON_OFF_DASH; + + gdk_gc_set_dashes(ctxcanvas->gc, 0, dash_style, ctxcanvas->canvas->line_dashes_count); + free(dash_style); + break; + } + } + + gdk_gc_set_values(ctxcanvas->gc, &ctxcanvas->gcval, GDK_GC_LINE_STYLE); + + return style; +} + +static int cdlinewidth(cdCtxCanvas *ctxcanvas, int width) +{ + if (width == 1) + ctxcanvas->gcval.line_width = 0; + else + ctxcanvas->gcval.line_width = width; + + gdk_gc_set_values(ctxcanvas->gc, &ctxcanvas->gcval, GDK_GC_LINE_WIDTH); + + return width; +} + +static int cdlinecap(cdCtxCanvas *ctxcanvas, int cap) +{ + int cd2x_cap[] = {GDK_CAP_BUTT, GDK_CAP_PROJECTING, GDK_CAP_ROUND}; + + ctxcanvas->gcval.cap_style = cd2x_cap[cap]; + gdk_gc_set_values(ctxcanvas->gc, &ctxcanvas->gcval, GDK_GC_CAP_STYLE); + + return cap; +} + +static int cdlinejoin(cdCtxCanvas *ctxcanvas, int join) +{ + int cd2x_join[] = {GDK_JOIN_MITER, GDK_JOIN_BEVEL, GDK_JOIN_ROUND}; + + ctxcanvas->gcval.join_style = cd2x_join[join]; + gdk_gc_set_values(ctxcanvas->gc, &ctxcanvas->gcval, GDK_GC_JOIN_STYLE); + + return join; +} + +static int cdbackopacity(cdCtxCanvas *ctxcanvas, int opaque) +{ + ctxcanvas->canvas->back_opacity = opaque; + cdinteriorstyle(ctxcanvas, ctxcanvas->canvas->interior_style); + cdlinestyle(ctxcanvas, ctxcanvas->canvas->line_style); + return opaque; +} + +static int cdfont(cdCtxCanvas *ctxcanvas, const char *typeface, int style, int size) +{ + int is_italic = 0, is_bold = 0; /* default is CD_PLAIN */ + int is_strikeout = 0, is_underline = 0; + char font[256]; + PangoAttrList *attrs; + + if (cdStrEqualNoCase(typeface, "Courier") || cdStrEqualNoCase(typeface, "Courier New")) + typeface = "Monospace"; + else if (cdStrEqualNoCase(typeface, "Times") || cdStrEqualNoCase(typeface, "Times New Roman")) + typeface = "Serif"; + else if (cdStrEqualNoCase(typeface, "Helvetica") || cdStrEqualNoCase(typeface, "Arial")) + typeface = "Sans"; + + if (style & CD_BOLD) + is_bold = 1; + + if (style & CD_ITALIC) + is_italic = 1; + + if (style & CD_UNDERLINE) + is_underline = 1; + + if (style & CD_STRIKEOUT) + is_strikeout = 1; + + if (size < 0) + { + double res = ((double)gdk_screen_get_width(gdk_screen_get_default()) / + (double)gdk_screen_get_width_mm(gdk_screen_get_default())); /* pixels/mm */ + + /* 1 point = 1/72 inch 1 inch = 25.4 mm */ + /* pixel = ((point/72)*25.4)*pixel/mm */ + size = (int)((-size/res)*2.83464567 + 0.5); /* from pixels to points */ + } + + sprintf(font, "%s, %s%s%d", typeface, is_bold?"Bold ":"", is_italic?"Italic ":"", size); + + ctxcanvas->fontdesc = pango_font_description_from_string(font); + + if (!ctxcanvas->fontdesc) + return 0; + + ctxcanvas->fontlayout = pango_layout_new(ctxcanvas->fontcontext); + pango_layout_set_font_description(ctxcanvas->fontlayout, ctxcanvas->fontdesc); + + attrs = pango_attr_list_new(); + pango_attr_list_insert(attrs, pango_attribute_copy(pango_attr_strikethrough_new(is_strikeout ? TRUE : FALSE))); + pango_attr_list_insert(attrs, pango_attribute_copy(pango_attr_underline_new(is_underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE))); + pango_layout_set_attributes(ctxcanvas->fontlayout, attrs); + + pango_attr_list_unref(attrs); + + return 1; +} + +static int cdnativefont(cdCtxCanvas *ctxcanvas, const char* nativefont) +{ + int size = 12, style = CD_PLAIN; + char typeface[1024]; + + /* parse the old Windows format first */ + if (!cdParseIupWinFont(nativefont, typeface, &style, &size)) + if (!cdParseXWinFont(nativefont, typeface, &style, &size)) + if (!cdParsePangoFont(nativefont, typeface, &style, &size)) + return 0; + + if (!cdfont(ctxcanvas, typeface, style, size)) + return 0; + + /* update cdfont parameters */ + ctxcanvas->canvas->font_style = style; + ctxcanvas->canvas->font_size = size; + strcpy(ctxcanvas->canvas->font_type_face, typeface); + + return 1; +} + +static void cdgetfontdim(cdCtxCanvas *ctxcanvas, int *max_width, int *height, int *ascent, int *descent) +{ + PangoFontMetrics* metrics; + int charwidth, charheight, charascent, chardescent; + + if(!ctxcanvas->fontdesc) + return; + + metrics = pango_context_get_metrics(ctxcanvas->fontcontext, ctxcanvas->fontdesc, pango_context_get_language(ctxcanvas->fontcontext)); + charascent = pango_font_metrics_get_ascent(metrics); + chardescent = pango_font_metrics_get_descent(metrics); + charheight = charascent + chardescent; + charwidth = pango_font_metrics_get_approximate_char_width(metrics); + + if (max_width) *max_width = (((charwidth) + PANGO_SCALE/2) / PANGO_SCALE); + if (height) *height = (((charheight) + PANGO_SCALE/2) / PANGO_SCALE); + if (ascent) *ascent = (((charascent) + PANGO_SCALE/2) / PANGO_SCALE); + if (descent) *descent = (((chardescent) + PANGO_SCALE/2) / PANGO_SCALE); + + pango_font_metrics_unref(metrics); +} + +static long int cdbackground(cdCtxCanvas *ctxcanvas, long int color) +{ + ctxcanvas->bg = cdColorToGdk(color); + gdk_gc_set_rgb_bg_color(ctxcanvas->gc, &ctxcanvas->bg); + return color; +} + +static long int cdforeground(cdCtxCanvas *ctxcanvas, long int color) +{ + ctxcanvas->fg = cdColorToGdk(color); + gdk_gc_set_rgb_fg_color(ctxcanvas->gc, &ctxcanvas->fg); + return color; +} + +static void cdpalette(cdCtxCanvas *ctxcanvas, int n, const long int *palette, int mode) +{ + int i; + GdkColor clr; + + if (mode == CD_FORCE) + { + /* if was POLITE then allocates own palette */ + if (ctxcanvas->colormap == gdk_gc_get_colormap(ctxcanvas->gc)) + ctxcanvas->colormap = gdk_colormap_new(ctxcanvas->vis, FALSE); + + /* allocate all the palette colors to the CD */ + for (i = 0; i < n; i++) + { + clr = cdColorToGdk(palette[i]); + gdk_colormap_alloc_color(ctxcanvas->colormap, &clr, FALSE, FALSE); + } + + /* set directly on the drawable */ + gdk_drawable_set_colormap(ctxcanvas->wnd, ctxcanvas->colormap); + } + else + { + /* if was FORCE, remove the own palette */ + if (ctxcanvas->colormap != gdk_gc_get_colormap(ctxcanvas->gc)) + { + g_object_unref(ctxcanvas->colormap); + ctxcanvas->colormap = gdk_gc_get_colormap(ctxcanvas->gc); + } + + /* if POLITE then just try to allocate all the colors of the palette */ + for (i = 0; i < n; i++) + { + clr = cdColorToGdk(palette[i]); + gdk_colormap_alloc_color(ctxcanvas->colormap, &clr, FALSE, TRUE); + } + } +} + +/******************************************************/ + +static void cdgdkCheckSolidStyle(cdCtxCanvas *ctxcanvas, int set) +{ + if (ctxcanvas->canvas->interior_style == CD_SOLID) + return; + + if (set) + gdk_gc_set_fill(ctxcanvas->gc, GDK_SOLID); + else + cdinteriorstyle(ctxcanvas, ctxcanvas->canvas->interior_style); +} + +static void cdclear(cdCtxCanvas* ctxcanvas) +{ + GdkColor clr; + + cdgdkCheckSolidStyle(ctxcanvas, 1); + + if (ctxcanvas->canvas->clip_mode != CD_CLIPOFF) + gdk_gc_set_clip_region(ctxcanvas->gc, NULL); + + clr = cdColorToGdk(ctxcanvas->canvas->background); + gdk_gc_set_rgb_fg_color(ctxcanvas->gc, &clr); + + gdk_draw_rectangle(ctxcanvas->wnd, ctxcanvas->gc, TRUE, 0, 0, ctxcanvas->canvas->w, ctxcanvas->canvas->h); + + clr = cdColorToGdk(ctxcanvas->canvas->foreground); + gdk_gc_set_rgb_fg_color(ctxcanvas->gc, &clr); + + if (ctxcanvas->canvas->clip_mode != CD_CLIPOFF) + cdclip(ctxcanvas, ctxcanvas->canvas->clip_mode); + + cdgdkCheckSolidStyle(ctxcanvas, 0); +} + +static void cdline(cdCtxCanvas *ctxcanvas, int x1, int y1, int x2, int y2) +{ + if (ctxcanvas->canvas->use_matrix) + { + cdMatrixTransformPoint(ctxcanvas->xmatrix, x1, y1, &x1, &y1); + cdMatrixTransformPoint(ctxcanvas->xmatrix, x2, y2, &x2, &y2); + } + + cdgdkCheckSolidStyle(ctxcanvas, 1); + gdk_draw_line(ctxcanvas->wnd, ctxcanvas->gc, x1, y1, x2, y2); + cdgdkCheckSolidStyle(ctxcanvas, 0); +} + +static void cdarc(cdCtxCanvas *ctxcanvas, int xc, int yc, int w, int h, double a1, double a2) +{ + if (ctxcanvas->canvas->use_matrix) + { + cdarcSIM(ctxcanvas, xc, yc, w, h, a1, a2); + return; + } + + cdgdkCheckSolidStyle(ctxcanvas, 1); + gdk_draw_arc(ctxcanvas->wnd, ctxcanvas->gc, FALSE, xc-w/2, yc-h/2, w, h, cdRound(a1*64), cdRound((a2 - a1)*64)); + cdgdkCheckSolidStyle(ctxcanvas, 0); +} + +static void cdsector(cdCtxCanvas *ctxcanvas, int xc, int yc, int w, int h, double a1, double a2) +{ + if (ctxcanvas->canvas->use_matrix) + { + cdsectorSIM(ctxcanvas, xc, yc, w, h, a1, a2); + return; + } + + if (ctxcanvas->canvas->new_region) + { + cdsectorSIM(ctxcanvas, xc, yc, w, h, a1, a2); + } + else + { + /* "filled parameter = TRUE" produces an 'pie slice' */ + gdk_draw_arc(ctxcanvas->wnd, ctxcanvas->gc, TRUE, xc-w/2, yc-h/2, w, h, cdRound(a1*64), cdRound((a2 - a1)*64)); + } +} + +static void cdrect(cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax) +{ + if (ctxcanvas->canvas->use_matrix) + { + cdrectSIM(ctxcanvas, xmin, xmax, ymin, ymax); + return; + } + + cdgdkCheckSolidStyle(ctxcanvas, 1); + gdk_draw_rectangle(ctxcanvas->wnd, ctxcanvas->gc, FALSE, xmin, ymin, xmax-xmin, ymax-ymin); + cdgdkCheckSolidStyle(ctxcanvas, 0); +} + +static void cdbox(cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax) +{ + if (ctxcanvas->canvas->use_matrix) + { + cdboxSIM(ctxcanvas, xmin, xmax, ymin, ymax); + return; + } + + if (ctxcanvas->canvas->new_region) + { + GdkRegion *rgn; + GdkRectangle rect; + + rect.x = xmin; rect.width = xmax-xmin; + rect.y = ymin; rect.height = ymax-ymin; + rgn = gdk_region_rectangle(&rect); + + sCombineRegion(ctxcanvas, rgn); + } + else + gdk_draw_rectangle(ctxcanvas->wnd, ctxcanvas->gc, TRUE, xmin, ymin, xmax-xmin+1, ymax-ymin+1); +} + +static void cdtext(cdCtxCanvas *ctxcanvas, int x, int y, const char *s, int len) +{ + PangoFontMetrics* metrics; + int w, h, desc, dir = -1; + int ox = x, oy = y; + + ctxcanvas->gdkLastConvertUTF8 = cdgdkStrConvertToUTF8(ctxcanvas, s, len); + pango_layout_set_text(ctxcanvas->fontlayout, ctxcanvas->gdkLastConvertUTF8, -1); + + pango_layout_get_pixel_size(ctxcanvas->fontlayout, &w, &h); + metrics = pango_context_get_metrics(ctxcanvas->fontcontext, ctxcanvas->fontdesc, pango_context_get_language(ctxcanvas->fontcontext)); + desc = (((pango_font_metrics_get_descent(metrics)) + PANGO_SCALE/2) / PANGO_SCALE); + + switch (ctxcanvas->canvas->text_alignment) + { + case CD_BASE_RIGHT: + case CD_NORTH_EAST: + case CD_EAST: + case CD_SOUTH_EAST: + x = x - w; + break; + case CD_BASE_CENTER: + case CD_CENTER: + case CD_NORTH: + case CD_SOUTH: + x = x - w/2; + break; + case CD_BASE_LEFT: + case CD_NORTH_WEST: + case CD_WEST: + case CD_SOUTH_WEST: + x = x; + break; + } + + if (ctxcanvas->canvas->invert_yaxis) + dir = 1; + + switch (ctxcanvas->canvas->text_alignment) + { + case CD_BASE_LEFT: + case CD_BASE_CENTER: + case CD_BASE_RIGHT: + y = y - (dir*h - desc); + break; + case CD_SOUTH_EAST: + case CD_SOUTH_WEST: + case CD_SOUTH: + y = y - (dir*h); + break; + case CD_NORTH_EAST: + case CD_NORTH: + case CD_NORTH_WEST: + y = y; + break; + case CD_CENTER: + case CD_EAST: + case CD_WEST: + y = y - (dir*(h/2)); + break; + } + + if(!ctxcanvas->canvas->use_matrix) + { + ctxcanvas->fontmatrix.xx = 1; ctxcanvas->fontmatrix.xy = 0; + ctxcanvas->fontmatrix.yx = 0; ctxcanvas->fontmatrix.yy = 1; + ctxcanvas->fontmatrix.x0 = 0; ctxcanvas->fontmatrix.y0 = 0; + } + + if (ctxcanvas->canvas->text_orientation != 0) + pango_matrix_rotate(&ctxcanvas->fontmatrix, (double)ctxcanvas->canvas->text_orientation); + + if (ctxcanvas->canvas->use_matrix || ctxcanvas->canvas->text_orientation != 0) + { + PangoRectangle rect; + double angle = CD_DEG2RAD*ctxcanvas->canvas->text_orientation; + double cos_angle = cos(angle); + double sin_angle = sin(angle); + + pango_context_set_matrix (ctxcanvas->fontcontext, &ctxcanvas->fontmatrix); + pango_layout_context_changed (ctxcanvas->fontlayout); + + pango_layout_get_pixel_extents(ctxcanvas->fontlayout, NULL, &rect); + pango_matrix_transform_pixel_rectangle(&ctxcanvas->fontmatrix, &rect); + + if (ctxcanvas->canvas->text_orientation) + cdRotatePoint(ctxcanvas->canvas, x, y, ox, oy, &x, &y, sin_angle, cos_angle); + + if (ctxcanvas->canvas->use_matrix) + cdMatrixTransformPoint(ctxcanvas->xmatrix, x, y, &x, &y); + + /* Defines the new position (X,Y), considering the Pango rectangle transformed */ + x += (int)rect.x; + y += (int)rect.y; + } + + cdgdkCheckSolidStyle(ctxcanvas, 1); + + if (ctxcanvas->canvas->new_region) + { + GdkRegion *rgn; + gint *idx; + gint range; + + pango_layout_line_get_x_ranges(pango_layout_get_line(ctxcanvas->fontlayout, 0), 0, len, &idx, &range); + + /* TODO: this is only the bounding box of the layout, not the text itself, + must transform the text into a polygon. */ + rgn = gdk_pango_layout_get_clip_region(ctxcanvas->fontlayout, x, y, idx, range); + + sCombineRegion(ctxcanvas, rgn); + } + else + gdk_draw_layout(ctxcanvas->wnd, ctxcanvas->gc, x, y, ctxcanvas->fontlayout); + + pango_context_set_matrix(ctxcanvas->fontcontext, NULL); + + cdgdkCheckSolidStyle(ctxcanvas, 0); + + pango_font_metrics_unref(metrics); +} + +static void cdgettextsize(cdCtxCanvas *ctxcanvas, const char *s, int len, int *width, int *height) +{ + if (!ctxcanvas->fontlayout) + return; + + ctxcanvas->gdkLastConvertUTF8 = cdgdkStrConvertToUTF8(ctxcanvas, s, len); + pango_layout_set_text(ctxcanvas->fontlayout, ctxcanvas->gdkLastConvertUTF8, -1); + pango_layout_get_pixel_size(ctxcanvas->fontlayout, width, height); +} + +static void cdpoly(cdCtxCanvas *ctxcanvas, int mode, cdPoint* poly, int n) +{ + int i; + + if (mode != CD_BEZIER) + { + for (i = 0; i < n; i++) + { + if (ctxcanvas->canvas->use_matrix) + cdMatrixTransformPoint(ctxcanvas->xmatrix, poly[i].x, poly[i].y, &(poly[i].x), &(poly[i].y)); + } + } + + switch( mode ) + { + case CD_FILL: + if (ctxcanvas->canvas->new_region) + { + GdkRegion* rgn = gdk_region_polygon((GdkPoint*)poly, n, ctxcanvas->canvas->fill_mode == CD_EVENODD ? GDK_EVEN_ODD_RULE : GDK_WINDING_RULE); + sCombineRegion(ctxcanvas, rgn); + } + else + gdk_draw_polygon(ctxcanvas->wnd, ctxcanvas->gc, TRUE, (GdkPoint*)poly, n); + break; + + case CD_CLOSED_LINES: + cdgdkCheckSolidStyle(ctxcanvas, 1); + gdk_draw_polygon(ctxcanvas->wnd, ctxcanvas->gc, FALSE, (GdkPoint*)poly, n); + cdgdkCheckSolidStyle(ctxcanvas, 0); + break; + + case CD_OPEN_LINES: + cdgdkCheckSolidStyle(ctxcanvas, 1); + gdk_draw_lines(ctxcanvas->wnd, ctxcanvas->gc, (GdkPoint*)poly, n); + cdgdkCheckSolidStyle(ctxcanvas, 0); + break; + + case CD_CLIP: + ctxcanvas->clip_rgn = gdk_region_polygon((GdkPoint*)poly, n, ctxcanvas->canvas->fill_mode == CD_EVENODD ? GDK_EVEN_ODD_RULE : GDK_WINDING_RULE); + if (ctxcanvas->canvas->clip_mode == CD_CLIPPOLYGON) + cdclip(ctxcanvas, CD_CLIPPOLYGON); + break; + + case CD_BEZIER: + cdSimPolyBezier(ctxcanvas->canvas, poly, n); + break; + } +} + +/******************************************************/ + +static void cdgdkGetPixbufData(GdkPixbuf* pixbuf, unsigned char *r, unsigned char *g, unsigned char *b) +{ + int w, h, y, x, bpp; + guchar *pixdata, *pixline_data; + int rowstride, channels; + + w = gdk_pixbuf_get_width(pixbuf); + h = gdk_pixbuf_get_height(pixbuf); + bpp = gdk_pixbuf_get_bits_per_sample(pixbuf)*gdk_pixbuf_get_n_channels(pixbuf); + + if (bpp!=24 && bpp!=32) + return; + + pixdata = gdk_pixbuf_get_pixels(pixbuf); + rowstride = gdk_pixbuf_get_rowstride(pixbuf); + channels = gdk_pixbuf_get_n_channels(pixbuf); + + /* planes are separated in imgdata */ + for (y=0; y<h; y++) + { + int lineoffset = (h-1 - y)*w; /* imgdata is bottom up */ + pixline_data = pixdata + y * rowstride; + for(x=0;x<w;x++) + { + int pos = x*channels; + r[lineoffset+x] = pixline_data[pos]; + g[lineoffset+x] = pixline_data[pos+1]; + b[lineoffset+x] = pixline_data[pos+2]; + } + } +} + +static GdkPixbuf* cdgdkCreatePixbufRGBA(int width, int height, const unsigned char *r, const unsigned char *g, const unsigned char *b, const unsigned char *a, int ix, int iy, int image_width) +{ + GdkPixbuf* pixbuf; + guchar *pixdata, *pixline_data; + int rowstride, channels; + int x, y; + + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, a?TRUE:FALSE, 8, width, height); + if (!pixbuf) + return NULL; + + pixdata = gdk_pixbuf_get_pixels(pixbuf); + rowstride = gdk_pixbuf_get_rowstride(pixbuf); + channels = gdk_pixbuf_get_n_channels(pixbuf); + + /* GdkPixbuf is top-bottom */ + /* imgdata is bottom up */ + + /* planes are separated in imgdata */ + for (y=0; y<height; y++) + { + int lineoffset = (height-1 - y + iy)*image_width; /* imgdata is bottom up */ + pixline_data = pixdata + y * rowstride; + for(x=0;x<width;x++) + { + int pos = x*channels; + pixline_data[pos] = r[lineoffset+x+ix]; + pixline_data[pos+1] = g[lineoffset+x+ix]; + pixline_data[pos+2] = b[lineoffset+x+ix]; + + if (a) + pixline_data[pos+3] = a[lineoffset+x+ix]; + } + } + + return pixbuf; +} + +static GdkPixbuf* cdgdkCreatePixbufMap(int width, int height, const long* colors, const unsigned char *map, int ix, int iy, int image_width) +{ + GdkPixbuf* pixbuf; + guchar *pixdata, *pixline_data; + int rowstride, channels; + const unsigned char *line_data; + int x, y; + + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); + if (!pixbuf) + return NULL; + + pixdata = gdk_pixbuf_get_pixels(pixbuf); + rowstride = gdk_pixbuf_get_rowstride(pixbuf); + channels = gdk_pixbuf_get_n_channels(pixbuf); + + /* GdkPixbuf is top-bottom */ + /* map is bottom up */ + + for (y=0; y<height; y++) + { + pixline_data = pixdata + y * rowstride; + line_data = map + (height-1 - y + iy) * image_width; /* map is bottom up */ + + for (x=0; x<width; x++) + { + unsigned char index = line_data[x+ix]; + long c = colors[index]; + guchar *r = &pixline_data[channels*x], + *g = r+1, + *b = g+1; + + *r = cdRed(c); + *g = cdGreen(c); + *b = cdBlue(c); + } + } + + return pixbuf; +} + +static void cdgetimagergb(cdCtxCanvas *ctxcanvas, unsigned char *r, unsigned char *g, unsigned char *b, int x, int y, int w, int h) +{ + GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(NULL, ctxcanvas->wnd, ctxcanvas->colormap, + x, y-h+1, + 0, 0, w, h); + if (!pixbuf) + { + fprintf(stderr, "CanvasDraw: error getting image\n"); + return; + } + + cdgdkGetPixbufData(pixbuf, r, g, b); +} + +static void cdputimagerectrgba_matrix(cdCtxCanvas* ctxcanvas, int iw, int ih, const unsigned char *r, const unsigned char *g, const unsigned char *b, const unsigned char *a, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax) +{ + int t_xmin, t_xmax, t_ymin, t_ymax, ew, eh, + t_x, t_y, dst_offset, size, nc, doff, rect[8]; + float i_x, i_y, xfactor, yfactor; + unsigned char *dst_r, *dst_g, *dst_b, *dst_a = NULL; + double inv_matrix[6]; + + /* calculate the destination limits */ + cdImageRGBCalcDstLimits(ctxcanvas->canvas, x, y, w, h, &t_xmin, &t_xmax, &t_ymin, &t_ymax, rect); + + /* Setup inverse transform (use the original transform here, NOT ctxcanvas->xmatrix) */ + cdImageRGBInitInverseTransform(w, h, xmin, xmax, ymin, ymax, &xfactor, &yfactor, ctxcanvas->canvas->matrix, inv_matrix); + + /* create an image for the destination area */ + ew = (t_xmax-t_xmin+1); + eh = (t_ymax-t_ymin+1); + size = ew*eh; + nc = 3; + if (a) nc = 4; + dst_r = malloc(nc*size); + if (!dst_r) + { + fprintf(stderr, "CanvasDraw: no enough memory\n"); + return; + } + dst_g = dst_r + size; + dst_b = dst_g + size; + if (a) dst_a = dst_b + size; + memset(dst_r, 0, nc*size); + + /* for all pixels in the destiny area */ + for(t_y = t_ymin; t_y <= t_ymax; t_y++) + { + dst_offset = (t_y-t_ymin) * ew; + + for(t_x = t_xmin; t_x <= t_xmax; t_x++) + { + cdImageRGBInverseTransform(t_x, t_y, &i_x, &i_y, xfactor, yfactor, xmin, ymin, x, y, inv_matrix); + + if (i_x > xmin && i_y > ymin && i_x < xmax+1 && i_y < ymax+1) + { + doff = (t_x-t_xmin) + dst_offset; + *(dst_r+doff) = cdBilinearInterpolation(iw, ih, r, i_x, i_y); + *(dst_g+doff) = cdBilinearInterpolation(iw, ih, g, i_x, i_y); + *(dst_b+doff) = cdBilinearInterpolation(iw, ih, b, i_x, i_y); + if (a) *(dst_a+doff) = cdBilinearInterpolation(iw, ih, a, i_x, i_y); + } + } + } + + { + int ex = t_xmin, + ey = t_ymin + eh-1; /* GdkPixbuf origin is at top-left */ + GdkPixbuf *pixbuf; + GdkRegion *clip_polygon; + GdkPoint* pnt = g_malloc(64); + + /* Since the transformation used was the original transformation, */ + /* must invert the Y axis here. */ + ey = _cdInvertYAxis(ctxcanvas->canvas, ey); + + /* use clipping to select only the transformed rectangle */ + pnt[0].x = (short)rect[0]; pnt[0].y = (short)_cdInvertYAxis(ctxcanvas->canvas, rect[1]); + pnt[1].x = (short)rect[2]; pnt[1].y = (short)_cdInvertYAxis(ctxcanvas->canvas, rect[3]); + pnt[2].x = (short)rect[4]; pnt[2].y = (short)_cdInvertYAxis(ctxcanvas->canvas, rect[5]); + pnt[3].x = (short)rect[6]; pnt[3].y = (short)_cdInvertYAxis(ctxcanvas->canvas, rect[7]); + clip_polygon = gdk_region_polygon(pnt, 4, ctxcanvas->canvas->fill_mode == CD_EVENODD ? GDK_EVEN_ODD_RULE : GDK_WINDING_RULE); + + /* combine with the existing clipping */ + gdk_gc_set_function(ctxcanvas->gc, GDK_AND); + gdk_gc_set_clip_region(ctxcanvas->gc, clip_polygon); + + cdwritemode(ctxcanvas, ctxcanvas->canvas->write_mode); /* reset gdk_gc_set_function */ + + pixbuf = cdgdkCreatePixbufRGBA(ew, eh, dst_r, dst_g, dst_b, dst_a, 0, 0, ew); + if (!pixbuf) + return; + + gdk_draw_pixbuf(ctxcanvas->wnd, ctxcanvas->gc, pixbuf, 0, 0, ex, ey, -1, -1, ctxcanvas->img_dither, 0, 0); + + /* reset clipping */ + gdk_region_destroy(clip_polygon); + cdclip(ctxcanvas, ctxcanvas->canvas->clip_mode); + + g_object_unref(pixbuf); + g_free(pnt); + } + + free(dst_r); +} + +static void cdputimagerectmap_matrix(cdCtxCanvas* ctxcanvas, int iw, int ih, const unsigned char *index, const long int *colors, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax) +{ + int t_xmin, t_xmax, t_ymin, t_ymax, ew, eh, + t_x, t_y, dst_offset, size, doff, rect[8]; + float i_x, i_y, xfactor, yfactor; + unsigned char *dst_index; + double inv_matrix[6]; + + /* calculate the destination limits */ + cdImageRGBCalcDstLimits(ctxcanvas->canvas, x, y, w, h, &t_xmin, &t_xmax, &t_ymin, &t_ymax, rect); + + /* Setup inverse transform (use the original transform here, NOT ctxcanvas->xmatrix) */ + cdImageRGBInitInverseTransform(w, h, xmin, xmax, ymin, ymax, &xfactor, &yfactor, ctxcanvas->canvas->matrix, inv_matrix); + + /* create an image for the destination area */ + ew = (t_xmax-t_xmin+1); + eh = (t_ymax-t_ymin+1); + size = ew*eh; + dst_index = malloc(size); + if (!dst_index) + { + fprintf(stderr, "CanvasDraw: no enough memory\n"); + return; + } + memset(dst_index, 0, size); + + /* for all pixels in the destiny area */ + for(t_y = t_ymin; t_y <= t_ymax; t_y++) + { + dst_offset = (t_y-t_ymin) * ew; + + for(t_x = t_xmin; t_x <= t_xmax; t_x++) + { + cdImageRGBInverseTransform(t_x, t_y, &i_x, &i_y, xfactor, yfactor, xmin, ymin, x, y, inv_matrix); + + if (i_x > xmin && i_y > ymin && i_x < xmax+1 && i_y < ymax+1) + { + doff = (t_x-t_xmin) + dst_offset; + *(dst_index+doff) = cdZeroOrderInterpolation(iw, ih, index, i_x, i_y); + } + } + } + + { + int ex = t_xmin, + ey = t_ymin + eh-1; /* GdkPixbuf* origin is at top-left */ + + GdkPixbuf *pixbuf; + GdkRegion *clip_polygon; + GdkPoint pnt[4]; + + /* Since the transformation used was the original transformation, */ + /* must invert the Y axis here. */ + ey = _cdInvertYAxis(ctxcanvas->canvas, ey); + + /* use clipping to select only the transformed rectangle */ + pnt[0].x = (short)rect[0]; pnt[0].y = (short)_cdInvertYAxis(ctxcanvas->canvas, rect[1]); + pnt[1].x = (short)rect[2]; pnt[1].y = (short)_cdInvertYAxis(ctxcanvas->canvas, rect[3]); + pnt[2].x = (short)rect[4]; pnt[2].y = (short)_cdInvertYAxis(ctxcanvas->canvas, rect[5]); + pnt[3].x = (short)rect[6]; pnt[3].y = (short)_cdInvertYAxis(ctxcanvas->canvas, rect[7]); + clip_polygon = gdk_region_polygon(pnt, 4, ctxcanvas->canvas->fill_mode == CD_EVENODD ? GDK_EVEN_ODD_RULE : GDK_WINDING_RULE); + + /* combine with the existing clipping */ + gdk_gc_set_function(ctxcanvas->gc, GDK_AND); + gdk_gc_set_clip_region(ctxcanvas->gc, clip_polygon); + + cdwritemode(ctxcanvas, ctxcanvas->canvas->write_mode); /* reset gdk_gc_set_function */ + + pixbuf = cdgdkCreatePixbufMap(ew, eh, colors, dst_index, 0, 0, ew); + if (!pixbuf) + return; + + gdk_draw_pixbuf(ctxcanvas->wnd, ctxcanvas->gc, pixbuf, 0, 0, ex, ey, -1, -1, ctxcanvas->img_dither, 0, 0); + + /* reset clipping */ + gdk_region_destroy(clip_polygon); + cdclip(ctxcanvas, ctxcanvas->canvas->clip_mode); + + g_object_unref(pixbuf); + } + + free(dst_index); +} + +static void cdputimagerectrgb(cdCtxCanvas *ctxcanvas, int iw, int ih, const unsigned char *r, const unsigned char *g, const unsigned char *b, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax) +{ + int ew = w, eh = h, ex = x, ey = y; + int bw = iw, bh = ih, bx = 0, by = 0; + int rw, rh; + GdkPixbuf* pixbuf; + + if (ctxcanvas->canvas->use_matrix) + { + cdputimagerectrgba_matrix(ctxcanvas, iw, ih, r, g, b, NULL, x, y, w, h, xmin, xmax, ymin, ymax); + return; + } + + rw = xmax-xmin+1; + rh = ymax-ymin+1; + y -= (h - 1); /* GdkPixbuf origin is at top-left */ + + if (!cdCalcZoom(ctxcanvas->canvas->w, x, w, &ex, &ew, xmin, rw, &bx, &bw, 1)) + return; + + if (!cdCalcZoom(ctxcanvas->canvas->h, y, h, &ey, &eh, ymin, rh, &by, &bh, 0)) + return; + + pixbuf = cdgdkCreatePixbufRGBA(bw, bh, r, g, b, NULL, bx, by, iw); + if (!pixbuf) + return; + + if (bw!=ew || bh!=eh) + { + GdkPixbuf *pixbuf_scaled = gdk_pixbuf_scale_simple(pixbuf, ew, eh, ctxcanvas->img_interp); + gdk_draw_pixbuf(ctxcanvas->wnd, ctxcanvas->gc, pixbuf_scaled, 0, 0, ex, ey, -1, -1, ctxcanvas->img_dither, 0, 0); + g_object_unref(pixbuf_scaled); + } + else + gdk_draw_pixbuf(ctxcanvas->wnd, ctxcanvas->gc, pixbuf, 0, 0, ex, ey, -1, -1, ctxcanvas->img_dither, 0, 0); + + g_object_unref(pixbuf); +} + +static void cdputimagerectrgba(cdCtxCanvas *ctxcanvas, int iw, int ih, const unsigned char *r, const unsigned char *g, const unsigned char *b, const unsigned char *a, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax) +{ + GdkPixbuf *pixbuf; + int ew = w, eh = h, ex = x, ey = y; + int bw = iw, bh = ih, bx = 0, by = 0; + int rw, rh; + + if (ctxcanvas->canvas->use_matrix) + { + cdputimagerectrgba_matrix(ctxcanvas, iw, ih, r, g, b, a, x, y, w, h, xmin, xmax, ymin, ymax); + return; + } + + rw = xmax-xmin+1; + rh = ymax-ymin+1; + y -= (h - 1); /* GdkPixbuf origin is at top-left */ + + if (!cdCalcZoom(ctxcanvas->canvas->w, x, w, &ex, &ew, xmin, rw, &bx, &bw, 1)) + return; + + if (!cdCalcZoom(ctxcanvas->canvas->h, y, h, &ey, &eh, ymin, rh, &by, &bh, 0)) + return; + + pixbuf = cdgdkCreatePixbufRGBA(bw, bh, r, g, b, a, bx, by, iw); + if (!pixbuf) + return; + + if (bw!=ew || bh!=eh) + { + GdkPixbuf *pixbuf_scaled = gdk_pixbuf_scale_simple(pixbuf, ew, eh, ctxcanvas->img_interp); + gdk_draw_pixbuf(ctxcanvas->wnd, ctxcanvas->gc, pixbuf_scaled, 0, 0, ex, ey, -1, -1, ctxcanvas->img_dither, 0, 0); + g_object_unref(pixbuf_scaled); + } + else + gdk_draw_pixbuf(ctxcanvas->wnd, ctxcanvas->gc, pixbuf, 0, 0, ex, ey, -1, -1, ctxcanvas->img_dither, 0, 0); + + g_object_unref(pixbuf); +} + +static void cdputimagerectmap(cdCtxCanvas *ctxcanvas, int iw, int ih, const unsigned char *index, const long int *colors, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax) +{ + int ew = w, eh = h, ex = x, ey = y; + int bw = iw, bh = ih, bx = 0, by = 0; + int rw, rh; + GdkPixbuf* pixbuf; + + if (ctxcanvas->canvas->use_matrix) + { + cdputimagerectmap_matrix(ctxcanvas, iw, ih, index, colors, x, y, w, h, xmin, xmax, ymin, ymax); + return; + } + + rw = xmax-xmin+1; + rh = ymax-ymin+1; + y -= (h - 1); /* GdkPixbuf origin is at top-left */ + + if (!cdCalcZoom(ctxcanvas->canvas->w, x, w, &ex, &ew, xmin, rw, &bx, &bw, 1)) + return; + + if (!cdCalcZoom(ctxcanvas->canvas->h, y, h, &ey, &eh, ymin, rh, &by, &bh, 0)) + return; + + pixbuf = cdgdkCreatePixbufMap(bw, bh, colors, index, bx, by, iw); + if (!pixbuf) + return; + + if (bw!=ew || bh!=eh) + { + GdkPixbuf *pixbuf_scaled = gdk_pixbuf_scale_simple(pixbuf, ew, eh, ctxcanvas->img_interp); + gdk_draw_pixbuf(ctxcanvas->wnd, ctxcanvas->gc, pixbuf_scaled, 0, 0, ex, ey, -1, -1, ctxcanvas->img_dither, 0, 0); + g_object_unref(pixbuf_scaled); + } + else + gdk_draw_pixbuf(ctxcanvas->wnd, ctxcanvas->gc, pixbuf, 0, 0, ex, ey, -1, -1, ctxcanvas->img_dither, 0, 0); + + g_object_unref(pixbuf); +} + +static void cdpixel(cdCtxCanvas *ctxcanvas, int x, int y, long int color) +{ + if (ctxcanvas->canvas->foreground != color) + { + GdkColor clr = cdColorToGdk(color); + gdk_gc_set_rgb_fg_color(ctxcanvas->gc, &clr); + } + + if (ctxcanvas->canvas->use_matrix) + cdMatrixTransformPoint(ctxcanvas->xmatrix, x, y, &x, &y); + + /* Draw pixel */ + gdk_draw_point(ctxcanvas->wnd, ctxcanvas->gc, x, y); + + if (ctxcanvas->canvas->foreground != color) + gdk_gc_set_rgb_fg_color(ctxcanvas->gc, &ctxcanvas->fg); +} + +static cdCtxImage *cdcreateimage (cdCtxCanvas *ctxcanvas, int w, int h) +{ + GdkGC* gc; + cdCtxImage *ctximage = (cdCtxImage *)malloc(sizeof(cdCtxImage)); + GdkColor clr; + + ctximage->w = w; + ctximage->h = h; + ctximage->depth = ctxcanvas->depth; + ctximage->scr = ctxcanvas->scr; + ctximage->vis = ctxcanvas->vis; + + ctximage->img = gdk_pixmap_new(ctxcanvas->wnd, w, h, ctxcanvas->depth); + + if (!ctximage->img) + { + free(ctximage); + return (void *)0; + } + + gc = gdk_gc_new(ctximage->img); + + clr = cdColorToGdk(CD_WHITE); + + gdk_gc_set_rgb_fg_color(gc, &clr); + gdk_draw_rectangle(ctximage->img, gc, TRUE, 0, 0, ctximage->w, ctxcanvas->canvas->h); + + g_object_unref(gc); + + return (void*)ctximage; +} + +static void cdgetimage (cdCtxCanvas *ctxcanvas, cdCtxImage *ctximage, int x, int y) +{ + gdk_draw_drawable(ctximage->img, ctxcanvas->gc, + ctxcanvas->wnd, x, y - ctximage->h+1, 0, 0, + ctximage->w, ctximage->h); +} + +static void cdputimagerect (cdCtxCanvas *ctxcanvas, cdCtxImage *ctximage, int x, int y, int xmin, int xmax, int ymin, int ymax) +{ + gdk_draw_drawable(ctxcanvas->wnd, ctxcanvas->gc, + ctximage->img, xmin, ctximage->h-ymax-1, x, y-(ymax-ymin+1)+1, + xmax-xmin+1, ymax-ymin+1); +} + +static void cdkillimage (cdCtxImage *ctximage) +{ + g_object_unref(ctximage->img); + free(ctximage); +} + +static void cdscrollarea (cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax, int dx, int dy) +{ + gdk_draw_drawable(ctxcanvas->wnd, ctxcanvas->gc, + ctxcanvas->wnd, xmin, ymin, xmin+dx, ymin+dy, + xmax-xmin+1, ymax-ymin+1); +} + +static void cdtransform(cdCtxCanvas *ctxcanvas, const double* matrix) +{ + if (matrix) + { + PangoMatrix tmpMtx = PANGO_MATRIX_INIT; + + /* configure a bottom-up coordinate system */ + ctxcanvas->xmatrix[0] = 1; + ctxcanvas->xmatrix[1] = 0; + ctxcanvas->xmatrix[2] = 0; + ctxcanvas->xmatrix[3] = -1; + ctxcanvas->xmatrix[4] = 0; + ctxcanvas->xmatrix[5] = (ctxcanvas->canvas->h-1); + cdMatrixMultiply(matrix, ctxcanvas->xmatrix); + + /* Pango Matrix Transform */ + ctxcanvas->fontmatrix.xx = matrix[0] * tmpMtx.xx + matrix[1] * tmpMtx.xy; + ctxcanvas->fontmatrix.xy = matrix[0] * tmpMtx.yx + matrix[1] * tmpMtx.yy; + ctxcanvas->fontmatrix.yx = matrix[2] * tmpMtx.xx + matrix[3] * tmpMtx.xy; + ctxcanvas->fontmatrix.yy = matrix[2] * tmpMtx.yx + matrix[3] * tmpMtx.yy; + ctxcanvas->fontmatrix.x0 = 0; + ctxcanvas->fontmatrix.y0 = 0; + + ctxcanvas->canvas->invert_yaxis = 0; + } + else + { + ctxcanvas->canvas->invert_yaxis = 1; + } +} + +/******************************************************************/ + +static void set_rotate_attrib(cdCtxCanvas* ctxcanvas, char* data) +{ + if (data) + { + sscanf(data, "%g %d %d", &ctxcanvas->rotate_angle, + &ctxcanvas->rotate_center_x, + &ctxcanvas->rotate_center_y); + + cdCanvasTransformTranslate(ctxcanvas->canvas, ctxcanvas->rotate_center_x, ctxcanvas->rotate_center_y); + cdCanvasTransformRotate(ctxcanvas->canvas, ctxcanvas->rotate_angle); + cdCanvasTransformTranslate(ctxcanvas->canvas, -ctxcanvas->rotate_center_x, -ctxcanvas->rotate_center_y); + } + else + { + ctxcanvas->rotate_angle = 0; + ctxcanvas->rotate_center_x = 0; + ctxcanvas->rotate_center_y = 0; + + cdCanvasTransform(ctxcanvas->canvas, NULL); + } +} + +static char* get_rotate_attrib(cdCtxCanvas* ctxcanvas) +{ + static char data[100]; + + if (!ctxcanvas->rotate_angle) + return NULL; + + sprintf(data, "%g %d %d", (double)ctxcanvas->rotate_angle, + ctxcanvas->rotate_center_x, + ctxcanvas->rotate_center_y); + + return data; +} + +static cdAttribute rotate_attrib = +{ + "ROTATE", + set_rotate_attrib, + get_rotate_attrib +}; + +static void set_imgdither_attrib(cdCtxCanvas* ctxcanvas, char* data) +{ + if (data && cdStrEqualNoCase(data, "NORMAL")) + ctxcanvas->img_dither = GDK_RGB_DITHER_NORMAL; + else + ctxcanvas->img_dither = GDK_RGB_DITHER_NONE; +} + +static char* get_imgdither_attrib(cdCtxCanvas* ctxcanvas) +{ + if (ctxcanvas->img_dither) + return "NORMAL"; + else + return "NONE"; +} + +static cdAttribute imgdither_attrib = +{ + "IMGDITHER", + set_imgdither_attrib, + get_imgdither_attrib +}; + +static void set_interp_attrib(cdCtxCanvas* ctxcanvas, char* data) +{ + if (data && cdStrEqualNoCase(data, "BILINEAR")) + ctxcanvas->img_interp = GDK_INTERP_BILINEAR; + else + ctxcanvas->img_interp = GDK_INTERP_NEAREST; +} + +static char* get_interp_attrib(cdCtxCanvas* ctxcanvas) +{ + if (ctxcanvas->img_interp) + return "BILINEAR"; + else + return "NEAREST"; +} + +static cdAttribute interp_attrib = +{ + "IMGINTERP", + set_interp_attrib, + get_interp_attrib +}; + +static char* get_gc_attrib(cdCtxCanvas *ctxcanvas) +{ + return (char*)ctxcanvas->gc; +} + +static cdAttribute gc_attrib = +{ + "GC", + NULL, + get_gc_attrib +}; + +static char* get_pangoversion_attrib(cdCtxCanvas* ctxcanvas) +{ + (void)ctxcanvas; + return (char*)pango_version_string(); +} + +static cdAttribute pangoversion_attrib = +{ + "PANGOVERSION", + NULL, + get_pangoversion_attrib +}; + +cdCtxCanvas *cdgdkCreateCanvas(cdCanvas* canvas, GdkDrawable* wnd, GdkScreen* scr, GdkVisual* vis) +{ + cdCtxCanvas *ctxcanvas = (cdCtxCanvas *)malloc(sizeof(cdCtxCanvas)); + memset(ctxcanvas, 0, sizeof(cdCtxCanvas)); + + ctxcanvas->scr = scr; + ctxcanvas->vis = vis; + ctxcanvas->wnd = wnd; + + ctxcanvas->gc = gdk_gc_new(wnd); + + if (!ctxcanvas->gc) + { + free(canvas); + return NULL; + } + + ctxcanvas->fontcontext = gdk_pango_context_get(); + pango_context_set_language(ctxcanvas->fontcontext, pango_language_get_default()); + ctxcanvas->gdkLastConvertUTF8 = NULL; + + ctxcanvas->canvas = canvas; + canvas->ctxcanvas = ctxcanvas; + + gdk_drawable_get_size(wnd, &ctxcanvas->canvas->w, &ctxcanvas->canvas->h); + ctxcanvas->depth = gdk_drawable_get_depth(wnd); + + canvas->bpp = ctxcanvas->depth; + canvas->xres = ((double)gdk_screen_get_width(scr) / (double)gdk_screen_get_width_mm(scr)); + canvas->yres = ((double)gdk_screen_get_height(scr) / (double)gdk_screen_get_height_mm(scr)); + canvas->w_mm = ((double)canvas->w) / canvas->xres; + canvas->h_mm = ((double)canvas->h) / canvas->yres; + canvas->invert_yaxis = 1; + + if (canvas->bpp <= 8) + { + ctxcanvas->colormap = gdk_gc_get_colormap(ctxcanvas->gc); + if (!ctxcanvas->colormap) + { + ctxcanvas->colormap = gdk_colormap_get_system(); + gdk_gc_set_colormap(ctxcanvas->gc, ctxcanvas->colormap); + } + ctxcanvas->num_colors = ctxcanvas->colormap->size; + } + + cdRegisterAttribute(canvas, &gc_attrib); + cdRegisterAttribute(canvas, &rotate_attrib); + cdRegisterAttribute(canvas, &pangoversion_attrib); + cdRegisterAttribute(canvas, &imgdither_attrib); + cdRegisterAttribute(canvas, &interp_attrib); + + return ctxcanvas; +} + +void cdgdkInitTable(cdCanvas* canvas) +{ + canvas->cxFlush = cdflush; + canvas->cxClear = cdclear; + + canvas->cxPixel = cdpixel; + canvas->cxLine = cdline; + canvas->cxPoly = cdpoly; + canvas->cxRect = cdrect; + canvas->cxBox = cdbox; + canvas->cxArc = cdarc; + canvas->cxSector = cdsector; + canvas->cxChord = cdchordSIM; + canvas->cxText = cdtext; + + canvas->cxNewRegion = cdnewregion; + canvas->cxIsPointInRegion = cdispointinregion; + canvas->cxOffsetRegion = cdoffsetregion; + canvas->cxGetRegionBox = cdgetregionbox; + canvas->cxClip = cdclip; + canvas->cxClipArea = cdcliparea; + canvas->cxWriteMode = cdwritemode; + canvas->cxLineStyle = cdlinestyle; + canvas->cxLineWidth = cdlinewidth; + canvas->cxLineCap = cdlinecap; + canvas->cxLineJoin = cdlinejoin; + canvas->cxBackOpacity = cdbackopacity; + canvas->cxInteriorStyle = cdinteriorstyle; + canvas->cxHatch = cdhatch; + canvas->cxStipple = cdstipple; + canvas->cxPattern = cdpattern; + canvas->cxFont = cdfont; + canvas->cxNativeFont = cdnativefont; + canvas->cxGetFontDim = cdgetfontdim; + canvas->cxGetTextSize = cdgettextsize; + canvas->cxPalette = cdpalette; + canvas->cxBackground = cdbackground; + canvas->cxForeground = cdforeground; + canvas->cxTransform = cdtransform; + + canvas->cxScrollArea = cdscrollarea; + canvas->cxCreateImage = cdcreateimage; + canvas->cxGetImage = cdgetimage; + canvas->cxPutImageRect = cdputimagerect; + canvas->cxKillImage = cdkillimage; + + canvas->cxGetImageRGB = cdgetimagergb; + canvas->cxPutImageRectRGB = cdputimagerectrgb; + canvas->cxPutImageRectMap = cdputimagerectmap; + canvas->cxPutImageRectRGBA = cdputimagerectrgba; +} + +int cdBaseDriver(void) +{ + return CD_BASE_GDK; +} |