"Fossies" - the Fresh Open Source Software Archive

Member "gambas-3.16.3/gb.gtk/src/cpaint_impl.cpp" (7 Sep 2021, 40220 Bytes) of package /linux/misc/gambas-3.16.3.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "cpaint_impl.cpp": 3.16.2_vs_3.16.3.

    1 /***************************************************************************
    2 
    3   cpaint_impl.cpp
    4 
    5   (c) 2000-2017 BenoƮt Minisini <g4mba5@gmail.com>
    6 
    7   This program is free software; you can redistribute it and/or modify
    8   it under the terms of the GNU General Public License as published by
    9   the Free Software Foundation; either version 2, or (at your option)
   10   any later version.
   11 
   12   This program is distributed in the hope that it will be useful,
   13   but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15   GNU General Public License for more details.
   16 
   17   You should have received a copy of the GNU General Public License
   18   along with this program; if not, write to the Free Software
   19   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   20   MA 02110-1301, USA.
   21 
   22 ***************************************************************************/
   23 
   24 #define __CPAINT_IMPL_CPP
   25 
   26 #include <cairo.h>
   27 #ifndef GTK3
   28 #include <cairo-xlib.h>
   29 #endif
   30 
   31 #include "gambas.h"
   32 #include "gb_common.h"
   33 #include "widgets.h"
   34 #include "gdesktop.h"
   35 
   36 #include "CWindow.h"
   37 #include "CDrawingArea.h"
   38 #include "CContainer.h"
   39 #include "CPicture.h"
   40 #include "CImage.h"
   41 #include "cprinter.h"
   42 #include "csvgimage.h"
   43 #include "CFont.h"
   44 #include "CDraw.h"
   45 #include "cpaint_impl.h"
   46 
   47 /**** Cairo image management *********************************************/
   48 
   49 static void free_image(GB_IMG *img, void *image)
   50 {
   51     cairo_surface_destroy((cairo_surface_t *)image);
   52 }
   53 
   54 static void *temp_image(GB_IMG *img)
   55 {
   56     cairo_surface_t *image;
   57 
   58     if (!img->data)
   59         image = NULL; // TODO: use a static small image surface
   60     else
   61         image = cairo_image_surface_create_for_data(img->data, CAIRO_FORMAT_ARGB32, img->width, img->height,
   62                                                     cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, img->width));
   63     return image;
   64 }
   65 
   66 static GB_IMG_OWNER _image_owner = {
   67     "gb.gtk.cairo",
   68     GB_IMAGE_BGRP,
   69     free_image,
   70     free_image,
   71     temp_image
   72     };
   73 
   74 static cairo_surface_t *check_image(void *img)
   75 {
   76     // TODO: format is endian-dependent
   77     return (cairo_surface_t *)IMAGE.Check((GB_IMG *)img, &_image_owner);
   78 }
   79 
   80 static GB_COLOR get_color(GB_PAINT *d, GB_COLOR col)
   81 {
   82     if (col == GB_COLOR_DEFAULT)
   83     {
   84         if (GB.Is(d->device, CLASS_Control))
   85             col = (((CWIDGET *)d->device)->widget)->realBackground(true);
   86         else
   87             col = 0xFFFFFF;
   88     }
   89 
   90     return col;
   91 }
   92 
   93 
   94 /**** Paint implementation ***********************************************/
   95 
   96 typedef
   97     struct {
   98         cairo_t *context;
   99         GtkPrintContext *print_context;
  100         gFont *font;
  101         gFont **font_stack;
  102         PangoLayout *layout;
  103         float ascent;
  104         cairo_matrix_t init;
  105         double dx;
  106         double dy;
  107         double bx;
  108         double by;
  109         bool invert;
  110         }
  111     GB_PAINT_EXTRA;
  112 
  113 #define EXTRA(d) ((GB_PAINT_EXTRA *)d->extra)
  114 #define CONTEXT(d) EXTRA(d)->context
  115 //#define DX(d) EXTRA(d)->dx
  116 //#define DY(d) EXTRA(d)->dy
  117 #define DX(d) 0
  118 #define DY(d) 0
  119     
  120 static bool _internal_paint = false;
  121 
  122 static gFont *get_default_font(GB_PAINT *d)
  123 {
  124     if (GB.Is(d->device, CLASS_DrawingArea) || GB.Is(d->device, CLASS_UserControl))
  125     {
  126         gControl *wid = (gControl *)((CWIDGET *)d->device)->widget;
  127         return wid->font()->copy();
  128     }
  129     else
  130     {
  131         return new gFont();
  132     }
  133 }
  134 
  135 //static void _Font(GB_PAINT *d, int set, GB_FONT *font);
  136 
  137 static void update_layout(GB_PAINT *d)
  138 {
  139     GB_PAINT_EXTRA *dx = EXTRA(d);
  140 
  141     if (dx->layout)
  142     {
  143         gt_add_layout_from_font(dx->layout, dx->font, d->resolutionY);
  144         dx->ascent = dx->font->ascentF();
  145 
  146         pango_cairo_context_set_font_options(pango_layout_get_context(dx->layout), gdk_screen_get_font_options (gdk_screen_get_default()));
  147 
  148         /*cairo_font_options_t *options = cairo_font_options_create(); //cairo_font_options_copy(pango_cairo_context_get_font_options(pango_layout_get_context(dx->layout)));
  149         cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
  150         cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_MEDIUM);
  151         cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_ON);
  152         cairo_font_options_set_subpixel_order(options, CAIRO_SUBPIXEL_ORDER_RGB);
  153         pango_cairo_context_set_font_options(pango_layout_get_context(dx->layout), options);
  154         cairo_font_options_destroy(options);*/
  155 
  156         pango_layout_context_changed(dx->layout);
  157     }
  158 }
  159 
  160 
  161 static bool init_painting(GB_PAINT *d, cairo_surface_t *target, double w, double h, int rx, int ry)
  162 {
  163     GB_PAINT_EXTRA *dx = EXTRA(d);
  164     gColor col;
  165     int r, g, b, a;
  166 
  167     d->area.width = w;
  168     d->area.height = h;
  169     d->resolutionX = rx; //device->physicalDpiX();
  170     d->resolutionY = ry; //device->physicalDpiY();
  171 
  172     /*if (device->paintingActive())
  173     {
  174         GB.Error("Device already being painted");
  175         return TRUE;
  176     }*/
  177 
  178     if (target)
  179     {
  180         dx->context = cairo_create(target);
  181         cairo_surface_destroy(target);
  182     }
  183 
  184     if (GB.Is(d->device, CLASS_Control))
  185         col = (((CWIDGET *)d->device)->widget)->realForeground(true);
  186     else
  187         col = 0;
  188     
  189     GB_COLOR_SPLIT(col, r, g, b, a);
  190     cairo_set_source_rgba(CONTEXT(d), r / 255.0, g / 255.0, b / 255.0, a / 255.0);
  191 
  192     cairo_set_line_width(CONTEXT(d), 1.0);
  193     /*cairo_set_line_join(CONTEXT(d), CAIRO_LINE_JOIN_MITER);
  194     cairo_set_miter_limit(CONTEXT(d), 10.0);
  195     cairo_set_line_cap(CONTEXT(d), CAIRO_LINE_CAP_BUTT);*/
  196 
  197     dx->font = get_default_font(d);
  198     dx->font_stack = NULL;
  199 
  200     cairo_get_matrix(CONTEXT(d), &EXTRA(d)->init);
  201 
  202     return FALSE;
  203 }
  204 
  205 #if 0
  206 static void _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context, cairo_t *cr)
  207 {
  208   cairo_matrix_t matrix;
  209   gdouble width, height;
  210     GtkPageSetup *page = gtk_print_context_get_page_setup(context);
  211 
  212   /*width = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH);
  213   width = width * context->surface_dpi_x / context->pixels_per_unit_x;
  214   height = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH);
  215   height = height * context->surface_dpi_y / context->pixels_per_unit_y;*/
  216 
  217     width = gtk_print_context_get_width(context);
  218     height = gtk_print_context_get_height(context);
  219 
  220   switch (gtk_page_setup_get_orientation (page))
  221     {
  222     default:
  223     case GTK_PAGE_ORIENTATION_PORTRAIT:
  224       break;
  225     case GTK_PAGE_ORIENTATION_LANDSCAPE:
  226             fprintf(stderr, "rotate landscape\n");
  227       cairo_translate (cr, 0, height);
  228       cairo_matrix_init (&matrix,
  229              0, -1,
  230              1,  0,
  231              0,  0);
  232       cairo_transform (cr, &matrix);
  233       break;
  234     case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
  235       cairo_translate (cr, width, height);
  236       cairo_matrix_init (&matrix,
  237              -1,  0,
  238               0, -1,
  239               0,  0);
  240       cairo_transform (cr, &matrix);
  241       break;
  242     case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
  243       cairo_translate (cr, width, 0);
  244       cairo_matrix_init (&matrix,
  245               0,  1,
  246              -1,  0,
  247               0,  0);
  248       cairo_transform (cr, &matrix);
  249       break;
  250     }
  251 }
  252 #endif
  253 
  254 static int Begin(GB_PAINT *d)
  255 {
  256     void *device = d->device;
  257     cairo_surface_t *target = NULL;
  258     double w, h;
  259     int rx = 96, ry = 96;
  260 
  261     EXTRA(d)->print_context = NULL;
  262     EXTRA(d)->dx = EXTRA(d)->dy = 0;
  263 
  264     if (GB.Is(device, CLASS_Picture))
  265     {
  266         gPicture *picture = ((CPICTURE *)device)->picture;
  267 
  268         if (picture->isVoid())
  269         {
  270             GB.Error("Bad picture");
  271             return TRUE;
  272         }
  273 
  274         w = picture->width();
  275         h = picture->height();
  276 
  277 #ifdef GTK3
  278         target = picture->getSurface();
  279         cairo_surface_reference(target);
  280 #else
  281         GdkDrawable *pixmap = (GdkDrawable *)picture->getPixmap();
  282 
  283         target =
  284             cairo_xlib_surface_create(gdk_x11_drawable_get_xdisplay(pixmap), gdk_x11_drawable_get_xid(pixmap),
  285                 gdk_x11_visual_get_xvisual(gdk_drawable_get_visual(pixmap)), w, h);
  286 #endif
  287     }
  288     else if (GB.Is(device, CLASS_Image))
  289     {
  290         target = check_image(device);
  291         if (!target)
  292         {
  293             GB.Error("Bad image");
  294             return TRUE;
  295         }
  296 
  297         cairo_surface_reference(target);
  298         w = ((GB_IMG *)device)->width;
  299         h = ((GB_IMG *)device)->height;
  300     }
  301     else if (GB.Is(device, CLASS_DrawingArea))
  302     {
  303         gDrawingArea *wid = (gDrawingArea *)((CWIDGET *)device)->widget;
  304         double dx = 0, dy = 0;
  305 
  306         w = wid->width();
  307         h = wid->height();
  308 
  309 #ifdef GTK3
  310         if (wid->cached())
  311         {
  312             EXTRA(d)->context = cairo_create(wid->buffer);
  313         }
  314         else
  315         {
  316             if (!wid->inDrawEvent())
  317             {
  318                 GB.Error("Cannot paint outside of Draw event handler");
  319                 return TRUE;
  320             }
  321 
  322             EXTRA(d)->context = ((CDRAWINGAREA *)device)->context;
  323             cairo_reference(CONTEXT(d));
  324 
  325             /*GtkAllocation a;
  326             gtk_widget_get_allocation(wid->border, &a);
  327             dx = a.x;
  328             dy = a.y;*/
  329 
  330         }
  331 
  332         rx = gDesktop::resolution(); //device->physicalDpiX();
  333         ry = gDesktop::resolution(); //device->physicalDpiY();
  334 
  335 #else
  336         GdkDrawable *dr;
  337 
  338         if (wid->cached())
  339         {
  340             wid->resizeCache(); // Why is it needed?
  341             dr = wid->buffer;
  342         }
  343         else
  344         {
  345             if (!wid->inDrawEvent())
  346             {
  347                 GB.Error("Cannot paint outside of Draw event handler");
  348                 return TRUE;
  349             }
  350 
  351             GtkAllocation *a = &wid->widget->allocation;
  352             dx = a->x;
  353             dy = a->y;
  354             dr = gtk_widget_get_window(wid->widget);
  355         }
  356 
  357         rx = gDesktop::resolution(); //device->physicalDpiX();
  358         ry = gDesktop::resolution(); //device->physicalDpiY();
  359 
  360         EXTRA(d)->context = gdk_cairo_create(dr);
  361 #endif
  362 
  363         EXTRA(d)->dx = dx;
  364         EXTRA(d)->dy = dy;
  365 
  366         cairo_translate(CONTEXT(d), dx, dy);
  367     }
  368     else if (GB.Is(device, CLASS_UserControl))
  369     {
  370         gContainer *wid = (gDrawingArea *)((CWIDGET *)device)->widget;
  371         double dx = 0, dy = 0;
  372 
  373         w = wid->width();
  374         h = wid->height();
  375 
  376         if (!_internal_paint)
  377         {
  378             GB.Error("Cannot paint outside of Draw event handler");
  379             return TRUE;
  380         }
  381 
  382 #ifdef GTK3
  383         EXTRA(d)->context = ((CUSERCONTROL *)device)->context;
  384         cairo_reference(CONTEXT(d));
  385 #else
  386         GdkDrawable *dr;
  387 
  388         GtkAllocation *a = &wid->widget->allocation;
  389         dx = a->x;
  390         dy = a->y;
  391         dr = gtk_widget_get_window(wid->widget);
  392 
  393         EXTRA(d)->context = gdk_cairo_create(dr);
  394 #endif
  395 
  396         rx = gDesktop::resolution();
  397         ry = gDesktop::resolution();
  398 
  399         EXTRA(d)->dx = dx;
  400         EXTRA(d)->dy = dy;
  401 
  402         cairo_translate(CONTEXT(d), dx, dy);
  403     }
  404     else if (GB.Is(device, CLASS_Printer))
  405     {
  406         CPRINTER *printer = (CPRINTER *)device;
  407         GtkPrintContext *context = printer->context;
  408         double pw, ph;
  409 
  410         if (!context)
  411         {
  412             GB.Error("Printer is not printing");
  413             return TRUE;
  414         }
  415 
  416         EXTRA(d)->print_context = context;
  417         EXTRA(d)->context = gtk_print_context_get_cairo_context(context);
  418 
  419         cairo_reference(CONTEXT(d));
  420 
  421         cairo_surface_set_fallback_resolution(cairo_get_target(CONTEXT(d)), 1200, 1200);
  422 
  423         w = gtk_print_context_get_width(context);
  424         h = gtk_print_context_get_height(context);
  425 
  426         rx = (int)gtk_print_context_get_dpi_x(context);
  427         ry = (int)gtk_print_context_get_dpi_y(context);
  428 
  429         printer->printer->getPaperSize(&pw, &ph);
  430         d->fontScale = 25.4 * d->area.width / pw / printer->printer->resolution();
  431     }
  432     else if (GB.Is(device, CLASS_SvgImage))
  433     {
  434         CSVGIMAGE *svgimage = ((CSVGIMAGE *)device);
  435         target = SVGIMAGE_begin(svgimage);
  436         if (!target)
  437             return TRUE;
  438 
  439         cairo_surface_reference(target);
  440         w = svgimage->width;
  441         h = svgimage->height;
  442         rx = ry = 72;
  443     }
  444     else
  445         return TRUE;
  446 
  447     return init_painting(d, target, w, h, rx, ry);
  448 }
  449 
  450 static void End(GB_PAINT *d)
  451 {
  452     int i;
  453     void *device = d->device;
  454     GB_PAINT_EXTRA *dx = EXTRA(d);
  455 
  456     if (dx->layout)
  457         g_object_unref(dx->layout);
  458 
  459     if (dx->font_stack)
  460     {
  461         for (i = 0; i < GB.Count(dx->font_stack); i++)
  462             delete dx->font_stack[i];
  463 
  464         GB.FreeArray(POINTER(&dx->font_stack));
  465     }
  466 
  467     delete dx->font;
  468 
  469     if (GB.Is(device, CLASS_Picture))
  470     {
  471         gPicture *picture = ((CPICTURE *)device)->picture;
  472         picture->invalidate();
  473     }
  474     else if (GB.Is(device, CLASS_DrawingArea))
  475     {
  476         gDrawingArea *wid = (gDrawingArea *)((CWIDGET *)device)->widget;
  477         if (wid && wid->cached())
  478             wid->setCache();
  479     }
  480     else if (GB.Is(device, CLASS_SvgImage))
  481     {
  482         CSVGIMAGE *svgimage = ((CSVGIMAGE *)device);
  483         SVGIMAGE_end(svgimage);
  484     }
  485 
  486     cairo_destroy(dx->context);
  487 }
  488 
  489 static void Save(GB_PAINT *d)
  490 {
  491     GB_PAINT_EXTRA *dx = EXTRA(d);
  492     gFont **pfont;
  493 
  494     cairo_save(dx->context);
  495 
  496     if (!dx->font_stack)
  497         GB.NewArray(POINTER(&dx->font_stack), sizeof(void *), 0);
  498 
  499     pfont = (gFont **)GB.Add(POINTER(&dx->font_stack));
  500     *pfont = dx->font->copy();
  501 }
  502 
  503 static void Restore(GB_PAINT *d)
  504 {
  505     GB_PAINT_EXTRA *dx = EXTRA(d);
  506 
  507     cairo_restore(dx->context);
  508 
  509     if (dx->font_stack && GB.Count(dx->font_stack) > 0)
  510     {
  511         delete dx->font;
  512         dx->font = dx->font_stack[GB.Count(dx->font_stack) - 1];
  513         GB.Remove(POINTER(&dx->font_stack), GB.Count(dx->font_stack) - 1, 1);
  514         update_layout(d);
  515     }
  516 }
  517 
  518 static void Antialias(GB_PAINT *d, int set, int *antialias)
  519 {
  520     if (set)
  521         cairo_set_antialias(CONTEXT(d), *antialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
  522     else
  523         *antialias = (cairo_get_antialias(CONTEXT(d)) == CAIRO_ANTIALIAS_NONE) ? 0 : 1;
  524 }
  525 
  526 static void apply_font(gFont *font, void *object = 0)
  527 {
  528     double scale;
  529     GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent();
  530     GB_PAINT_EXTRA *dx = EXTRA(d);
  531 
  532     font = font->copy();
  533 
  534     scale = d->fontScale;
  535     if (dx->print_context)
  536         scale *= ((CPRINTER *)d->device)->printer->resolution() / 96.0;
  537 
  538     if (scale != 1)
  539         font->setSize(font->size() * scale);
  540 
  541     delete dx->font;
  542     dx->font = font;
  543 
  544     update_layout(d);
  545 }
  546 
  547 // Font is used by X11!
  548 static void _Font(GB_PAINT *d, int set, GB_FONT *font)
  549 {
  550     GB_PAINT_EXTRA *dx = EXTRA(d);
  551     gFont *f;
  552     double scale;
  553 
  554     scale = d->fontScale;
  555     if (dx->print_context)
  556         scale *= ((CPRINTER *)d->device)->printer->resolution() / 96.0;
  557 
  558     if (set)
  559     {
  560         delete dx->font;
  561         if (*font)
  562             f = ((CFONT *)(*font))->font->copy();
  563         else
  564             f = get_default_font(d);
  565 
  566         if (scale != 1)
  567             f->setSize(f->size() * scale);
  568 
  569         dx->font = f;
  570 
  571         update_layout(d);
  572     }
  573     else
  574     {
  575         f = dx->font->copy();
  576 
  577         if (scale != 1)
  578             f->setSize(f->size() / scale);
  579 
  580         *font = CFONT_create(f, apply_font);
  581     }
  582 }
  583 
  584 
  585 static void Background(GB_PAINT *d, int set, GB_COLOR *color)
  586 {
  587     if (set)
  588     {
  589         int r, g, b, a;
  590         int col = get_color(d, *color);
  591         GB_COLOR_SPLIT(col, r, g, b, a);
  592         cairo_set_source_rgba(CONTEXT(d), r / 255.0, g / 255.0, b / 255.0, a / 255.0);
  593     }
  594     else
  595     {
  596         double r, g, b, a;
  597         if (cairo_pattern_get_rgba(cairo_get_source(CONTEXT(d)), &r, &g, &b, &a) != CAIRO_STATUS_SUCCESS)
  598             *color = 0;
  599         else
  600             *color = GB_COLOR_MAKE((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0));
  601     }
  602 }
  603 
  604 
  605 static void Invert(GB_PAINT *d, int set, int *invert)
  606 {
  607     #if CAIRO_MAJOR >= 2 || (CAIRO_MAJOR == 1 && CAIRO_MINOR >= 10)
  608     if (set)
  609         cairo_set_operator(CONTEXT(d), *invert ? CAIRO_OPERATOR_DIFFERENCE : CAIRO_OPERATOR_OVER);
  610     else
  611         *invert = cairo_get_operator(CONTEXT(d)) == CAIRO_OPERATOR_DIFFERENCE;
  612     #else
  613     if (set)
  614         EXTRA(d)->invert = *invert;
  615     else
  616         *invert = EXTRA(d)->invert;
  617     #endif
  618 }
  619 
  620 
  621 static void Clip(GB_PAINT *d, int preserve)
  622 {
  623     if (preserve)
  624         cairo_clip_preserve(CONTEXT(d));
  625     else
  626         cairo_clip(CONTEXT(d));
  627 }
  628 
  629 static void ResetClip(GB_PAINT *d)
  630 {
  631     cairo_reset_clip(CONTEXT(d));
  632 }
  633 
  634 static void ClipExtents(GB_PAINT *d, GB_EXTENTS *ext)
  635 {
  636     double x1, y1, x2, y2;
  637     cairo_clip_extents(CONTEXT(d), &x1, &y1, &x2, &y2);
  638 
  639     ext->x1 = (float)x1 - DX(d);
  640     ext->y1 = (float)y1 - DY(d);
  641     ext->x2 = (float)x2;
  642     ext->y2 = (float)y2;
  643 }
  644 
  645 static void Fill(GB_PAINT *d, int preserve)
  646 {
  647     if (preserve)
  648         cairo_fill_preserve(CONTEXT(d));
  649     else
  650         cairo_fill(CONTEXT(d));
  651 }
  652 
  653 static void Stroke(GB_PAINT *d, int preserve)
  654 {
  655     if (preserve)
  656         cairo_stroke_preserve(CONTEXT(d));
  657     else
  658         cairo_stroke(CONTEXT(d));
  659 }
  660 
  661 static void PathExtents(GB_PAINT *d, GB_EXTENTS *ext)
  662 {
  663     double x1, y1, x2, y2;
  664     cairo_path_extents(CONTEXT(d), &x1, &y1, &x2, &y2);
  665 
  666     ext->x1 = (float)x1 - DX(d);
  667     ext->y1 = (float)y1 - DY(d);
  668     ext->x2 = (float)x2;
  669     ext->y2 = (float)y2;
  670 }
  671 
  672 static int PathContains(GB_PAINT *d, float x, float y)
  673 {
  674     return cairo_in_fill(CONTEXT(d), (double)x + DX(d), (double)y + DY(d));
  675 }
  676 
  677 static void PathOutline(GB_PAINT *d, GB_PAINT_OUTLINE_CB cb)
  678 {
  679     cairo_path_t *path;
  680     cairo_path_data_t *data;
  681     int i;
  682 
  683     path = cairo_copy_path_flat(CONTEXT(d));
  684 
  685     for (i = 0; i < path->num_data; i += path->data[i].header.length)
  686     {
  687     data = &path->data[i];
  688     switch (data->header.type)
  689         {
  690             case CAIRO_PATH_MOVE_TO:
  691                 (*cb)(GB_PAINT_PATH_MOVE, data[1].point.x, data[1].point.y);
  692                 break;
  693 
  694             case CAIRO_PATH_LINE_TO:
  695                 (*cb)(GB_PAINT_PATH_LINE, data[1].point.x, data[1].point.y);
  696                 break;
  697 
  698             case CAIRO_PATH_CURVE_TO:
  699                 fprintf(stderr, "gb.gtk: warning: CAIRO_PATH_CURVE_TO not supported\n");
  700                 break;
  701 
  702             case CAIRO_PATH_CLOSE_PATH:
  703                 fprintf(stderr, "gb.gtk: warning: CAIRO_PATH_CLOSE_PATH not supported\n");
  704                 break;
  705     }
  706     }
  707 
  708     cairo_path_destroy(path);
  709 }
  710 
  711 static void Dash(GB_PAINT *d, int set, float **dashes, int *count)
  712 {
  713     int i;
  714     double lw;
  715 
  716     lw = cairo_get_line_width(CONTEXT(d));
  717     if (lw == 0) lw = 1;
  718 
  719     if (set)
  720     {
  721         if (!*count)
  722             cairo_set_dash(CONTEXT(d), NULL, 0, 0.0);
  723         else
  724         {
  725             double dd[*count];
  726 
  727             for (i = 0; i < *count; i++)
  728                 dd[i] = (*dashes)[i] * lw;
  729 
  730             cairo_set_dash(CONTEXT(d), dd, *count, 0.0);
  731         }
  732     }
  733     else
  734     {
  735         *count = cairo_get_dash_count(CONTEXT(d));
  736 
  737         if (*count)
  738         {
  739             double dd[*count];
  740             cairo_get_dash(CONTEXT(d), dd, NULL);
  741 
  742             GB.Alloc(POINTER(dashes), sizeof(float) * *count);
  743             for (int i = 0; i < *count; i++)
  744                 (*dashes)[i] = (float)dd[i] / lw;
  745         }
  746         else
  747         {
  748             *dashes = NULL;
  749         }
  750     }
  751 }
  752 
  753 static void DashOffset(GB_PAINT *d, int set, float *offset)
  754 {
  755     double lw;
  756 
  757     lw = cairo_get_line_width(CONTEXT(d));
  758     if (lw == 0) lw = 1;
  759 
  760     if (set)
  761     {
  762         int count = cairo_get_dash_count(CONTEXT(d));
  763         double dashes[count];
  764         cairo_get_dash(CONTEXT(d), dashes, NULL);
  765         cairo_set_dash(CONTEXT(d), dashes, count, (double)*offset * lw);
  766     }
  767     else
  768     {
  769         double v;
  770         cairo_get_dash(CONTEXT(d), NULL, &v);
  771         *offset = (float)v / lw;
  772     }
  773 }
  774 
  775 
  776 static void FillRule(GB_PAINT *d, int set, int *value)
  777 {
  778     if (set)
  779     {
  780         cairo_fill_rule_t v;
  781 
  782         switch (*value)
  783         {
  784             case GB_PAINT_FILL_RULE_EVEN_ODD: v = CAIRO_FILL_RULE_EVEN_ODD; break;
  785             case GB_PAINT_FILL_RULE_WINDING: default: v = CAIRO_FILL_RULE_WINDING;
  786         }
  787 
  788         cairo_set_fill_rule(CONTEXT(d), v);
  789     }
  790     else
  791     {
  792         switch (cairo_get_fill_rule(CONTEXT(d)))
  793         {
  794             case CAIRO_FILL_RULE_EVEN_ODD: *value = GB_PAINT_FILL_RULE_EVEN_ODD; break;
  795             case CAIRO_FILL_RULE_WINDING: default: *value = GB_PAINT_FILL_RULE_WINDING;
  796         }
  797     }
  798 }
  799 
  800 
  801 static void FillStyle(GB_PAINT *d, int set, int *style)
  802 {
  803     /*if (set)
  804     {
  805         EXTRA(d)->fillRule = *value;
  806     }
  807     else
  808         *value = EXTRA(d)->fillRule;*/
  809 }
  810 
  811 
  812 static void LineCap(GB_PAINT *d, int set, int *value)
  813 {
  814     if (set)
  815     {
  816         cairo_line_cap_t v;
  817 
  818         switch (*value)
  819         {
  820             case GB_PAINT_LINE_CAP_ROUND: v = CAIRO_LINE_CAP_ROUND; break;
  821             case GB_PAINT_LINE_CAP_SQUARE: v = CAIRO_LINE_CAP_SQUARE; break;
  822             case GB_PAINT_LINE_CAP_BUTT: default: v = CAIRO_LINE_CAP_BUTT;
  823         }
  824 
  825         cairo_set_line_cap(CONTEXT(d), v);
  826     }
  827     else
  828     {
  829         switch (cairo_get_line_cap(CONTEXT(d)))
  830         {
  831             case CAIRO_LINE_CAP_ROUND: *value = GB_PAINT_LINE_CAP_ROUND; break;
  832             case CAIRO_LINE_CAP_SQUARE: *value = GB_PAINT_LINE_CAP_SQUARE; break;
  833             case CAIRO_LINE_CAP_BUTT: default: *value = GB_PAINT_LINE_CAP_BUTT;
  834         }
  835     }
  836 }
  837 
  838 static void LineJoin(GB_PAINT *d, int set, int *value)
  839 {
  840     if (set)
  841     {
  842         cairo_line_join_t v;
  843 
  844         switch (*value)
  845         {
  846             case GB_PAINT_LINE_JOIN_ROUND: v = CAIRO_LINE_JOIN_ROUND; break;
  847             case GB_PAINT_LINE_JOIN_BEVEL: v = CAIRO_LINE_JOIN_BEVEL; break;
  848             case GB_PAINT_LINE_JOIN_MITER: default: v = CAIRO_LINE_JOIN_MITER;
  849         }
  850 
  851         cairo_set_line_join(CONTEXT(d), v);
  852     }
  853     else
  854     {
  855         switch (cairo_get_line_join(CONTEXT(d)))
  856         {
  857             case CAIRO_LINE_JOIN_ROUND: *value = GB_PAINT_LINE_JOIN_ROUND; break;
  858             case CAIRO_LINE_JOIN_BEVEL: *value = GB_PAINT_LINE_JOIN_BEVEL; break;
  859             case CAIRO_LINE_JOIN_MITER: default: *value = GB_PAINT_LINE_JOIN_MITER;
  860         }
  861     }
  862 }
  863 
  864 static void LineWidth(GB_PAINT *d, int set, float *value)
  865 {
  866     if (set)
  867     {
  868         float *dashes;
  869         int count;
  870         float offset;
  871 
  872         Dash(d, FALSE, &dashes, &count);
  873         DashOffset(d, FALSE, &offset);
  874 
  875         cairo_set_line_width(CONTEXT(d), (double)*value);
  876 
  877         Dash(d, TRUE, &dashes, &count);
  878         DashOffset(d, TRUE, &offset);
  879         GB.Free(POINTER(&dashes));
  880     }
  881     else
  882         *value = (float)cairo_get_line_width(CONTEXT(d));
  883 }
  884 
  885 static void MiterLimit(GB_PAINT *d, int set, float *value)
  886 {
  887     if (set)
  888         cairo_set_miter_limit(CONTEXT(d), (double)*value);
  889     else
  890         *value = (float)cairo_get_miter_limit(CONTEXT(d));
  891 }
  892 
  893 
  894 static void Operator(GB_PAINT *d, int set, int *value)
  895 {
  896     if (set)
  897     {
  898         cairo_operator_t v;
  899 
  900         switch (*value)
  901         {
  902             case GB_PAINT_OPERATOR_CLEAR: v = CAIRO_OPERATOR_CLEAR; break;
  903             case GB_PAINT_OPERATOR_SOURCE: v = CAIRO_OPERATOR_SOURCE; break;
  904             case GB_PAINT_OPERATOR_IN: v = CAIRO_OPERATOR_IN; break;
  905             case GB_PAINT_OPERATOR_OUT: v = CAIRO_OPERATOR_OUT; break;
  906             case GB_PAINT_OPERATOR_ATOP: v = CAIRO_OPERATOR_ATOP; break;
  907             case GB_PAINT_OPERATOR_DEST: v = CAIRO_OPERATOR_DEST; break;
  908             case GB_PAINT_OPERATOR_DEST_OVER: v = CAIRO_OPERATOR_DEST_OVER; break;
  909             case GB_PAINT_OPERATOR_DEST_IN: v = CAIRO_OPERATOR_DEST_IN; break;
  910             case GB_PAINT_OPERATOR_DEST_OUT: v = CAIRO_OPERATOR_DEST_OUT; break;
  911             case GB_PAINT_OPERATOR_DEST_ATOP: v = CAIRO_OPERATOR_DEST_ATOP; break;
  912             case GB_PAINT_OPERATOR_XOR: v = CAIRO_OPERATOR_XOR; break;
  913             case GB_PAINT_OPERATOR_ADD: v = CAIRO_OPERATOR_ADD; break;
  914             case GB_PAINT_OPERATOR_SATURATE: v = CAIRO_OPERATOR_SATURATE; break;
  915             case GB_PAINT_OPERATOR_OVER: default: v = CAIRO_OPERATOR_OVER; break;
  916         }
  917 
  918         cairo_set_operator(CONTEXT(d), v);
  919     }
  920     else
  921     {
  922         switch (cairo_get_operator(CONTEXT(d)))
  923         {
  924             case CAIRO_OPERATOR_CLEAR: *value = GB_PAINT_OPERATOR_CLEAR; break;
  925             case CAIRO_OPERATOR_SOURCE: *value = GB_PAINT_OPERATOR_SOURCE; break;
  926             case CAIRO_OPERATOR_IN: *value = GB_PAINT_OPERATOR_IN; break;
  927             case CAIRO_OPERATOR_OUT: *value = GB_PAINT_OPERATOR_OUT; break;
  928             case CAIRO_OPERATOR_ATOP: *value = GB_PAINT_OPERATOR_ATOP; break;
  929             case CAIRO_OPERATOR_DEST: *value = GB_PAINT_OPERATOR_DEST; break;
  930             case CAIRO_OPERATOR_DEST_OVER: *value = GB_PAINT_OPERATOR_DEST_OVER; break;
  931             case CAIRO_OPERATOR_DEST_IN: *value = GB_PAINT_OPERATOR_DEST_IN; break;
  932             case CAIRO_OPERATOR_DEST_OUT: *value = GB_PAINT_OPERATOR_DEST_OUT; break;
  933             case CAIRO_OPERATOR_DEST_ATOP: *value = GB_PAINT_OPERATOR_DEST_ATOP; break;
  934             case CAIRO_OPERATOR_XOR: *value = GB_PAINT_OPERATOR_XOR; break;
  935             case CAIRO_OPERATOR_ADD: *value = GB_PAINT_OPERATOR_ADD; break;
  936             case CAIRO_OPERATOR_SATURATE: *value = GB_PAINT_OPERATOR_SATURATE; break;
  937             case CAIRO_OPERATOR_OVER: default: *value = GB_PAINT_OPERATOR_OVER;
  938         }
  939     }
  940 }
  941 
  942 
  943 static void NewPath(GB_PAINT *d)
  944 {
  945     cairo_new_path(CONTEXT(d));
  946 }
  947 
  948 static void ClosePath(GB_PAINT *d)
  949 {
  950     cairo_close_path(CONTEXT(d));
  951 }
  952 
  953 
  954 static void Arc(GB_PAINT *d, float xc, float yc, float radius, float angle, float length, bool pie)
  955 {
  956     xc += DX(d);
  957     yc += DY(d);
  958 
  959     cairo_new_sub_path(CONTEXT(d));
  960 
  961     if (pie)
  962         cairo_move_to(CONTEXT(d), 0, 0);
  963 
  964     if (length < 0.0)
  965         cairo_arc_negative(CONTEXT(d), xc, yc, radius, angle, angle + length);
  966     else
  967         cairo_arc(CONTEXT(d), xc, yc, radius, angle, angle + length);
  968 
  969     if (pie)
  970         cairo_close_path(CONTEXT(d));
  971 }
  972 
  973 static void Ellipse(GB_PAINT *d, float x, float y, float width, float height, float angle, float length, bool pie)
  974 {
  975     x += DX(d);
  976     y += DY(d);
  977 
  978     cairo_new_sub_path(CONTEXT(d));
  979 
  980     cairo_save(CONTEXT(d));
  981 
  982     cairo_translate(CONTEXT(d), x + width / 2, y + height / 2);
  983     cairo_scale(CONTEXT(d), width / 2, height / 2);
  984 
  985     if (pie)
  986         cairo_move_to(CONTEXT(d), 0, 0);
  987 
  988     if (length < 0.0)
  989         cairo_arc_negative(CONTEXT(d), 0, 0, 1, angle, angle + length);
  990     else
  991         cairo_arc(CONTEXT(d), 0, 0, 1, angle, angle + length);
  992 
  993     if (pie)
  994         cairo_close_path(CONTEXT(d));
  995 
  996     cairo_restore(CONTEXT(d));
  997 }
  998 
  999 static void Rectangle(GB_PAINT *d, float x, float y, float width, float height)
 1000 {
 1001     x += DX(d);
 1002     y += DY(d);
 1003     cairo_rectangle(CONTEXT(d), x, y, width, height);
 1004 }
 1005 
 1006 static void ClipRect(GB_PAINT *d, int x, int y, int w, int h)
 1007 {
 1008     ResetClip(d);
 1009     Rectangle(d, x, y, w, h);
 1010     Clip(d, FALSE);
 1011 }
 1012 
 1013 static void GetCurrentPoint(GB_PAINT *d, float *x, float *y)
 1014 {
 1015     double cx, cy;
 1016 
 1017     cairo_get_current_point(CONTEXT(d), &cx, &cy);
 1018     *x = (float)cx - DX(d);
 1019     *y = (float)cy - DY(d);
 1020 }
 1021 
 1022 static void MoveTo(GB_PAINT *d, float x, float y)
 1023 {
 1024     cairo_move_to(CONTEXT(d), x + DX(d), y + DY(d));
 1025 }
 1026 
 1027 static void LineTo(GB_PAINT *d, float x, float y)
 1028 {
 1029     cairo_line_to(CONTEXT(d), x + DX(d), y + DY(d));
 1030 }
 1031 
 1032 static void CurveTo(GB_PAINT *d, float x1, float y1, float x2, float y2, float x3, float y3)
 1033 {
 1034     cairo_curve_to(CONTEXT(d), x1 + DX(d), y1 + DY(d), x2 + DX(d), y2 + DY(d), x3 + DX(d), y3 + DY(d));
 1035 }
 1036 
 1037 static PangoLayout *create_pango_layout(GB_PAINT *d)
 1038 {
 1039     GB_PAINT_EXTRA *dx = EXTRA(d);
 1040 
 1041     if (!dx->layout)
 1042         dx->layout = pango_cairo_create_layout(dx->context);
 1043 
 1044     return dx->layout;
 1045 }
 1046 
 1047 static void draw_text(GB_PAINT *d, bool rich, const char *text, int len, float w, float h, int align, bool draw)
 1048 {
 1049     char *html = NULL;
 1050     PangoLayout *layout;
 1051     float tw, th, offx, offy;
 1052 
 1053     layout = create_pango_layout(d);
 1054 
 1055     if (rich)
 1056     {
 1057         pango_layout_set_text(layout, "", 0);
 1058         if (w > 0)
 1059         {
 1060             pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
 1061             pango_layout_set_width(layout, (int)ceilf(w * PANGO_SCALE));
 1062         }
 1063         html = gt_html_to_pango_string(text, len, false);
 1064         pango_layout_set_markup(layout, html, -1);
 1065     }
 1066     else
 1067     {
 1068         pango_layout_set_markup(layout, "", 0);
 1069         pango_layout_set_text(layout, text, len);
 1070         pango_layout_set_width(layout, -1);
 1071     }
 1072     
 1073     update_layout(d);
 1074 
 1075     if (align == GB_DRAW_ALIGN_DEFAULT)
 1076         align = ALIGN_TOP_NORMAL;
 1077 
 1078     if (w > 0 || h > 0)
 1079     {
 1080         gt_layout_alignment(layout, w, h, &tw, &th, align, &offx, &offy);
 1081         if (rich)
 1082             offx = 0;
 1083     }
 1084     else
 1085     {
 1086         offx = 0;
 1087         offy = -(EXTRA(d)->ascent);
 1088     }
 1089 
 1090     cairo_rel_move_to(CONTEXT(d), offx, offy);
 1091     if (draw)
 1092         pango_cairo_show_layout(CONTEXT(d), layout);
 1093     else
 1094         pango_cairo_layout_path(CONTEXT(d), layout);
 1095 
 1096     if (html) g_free(html);
 1097 }
 1098 
 1099 static void Text(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw)
 1100 {
 1101     draw_text(d, FALSE, text, len, w, h, align, draw);
 1102 }
 1103 
 1104 static void RichText(GB_PAINT *d, const char *text, int len, float w, float h, int align, bool draw)
 1105 {
 1106     draw_text(d, TRUE, text, len, w, h, align, draw);
 1107 }
 1108 
 1109 
 1110 static void get_text_extents(GB_PAINT *d, bool rich, const char *text, int len, GB_EXTENTS *ext, float width)
 1111 {
 1112     GB_PAINT_EXTRA *dx = EXTRA(d);
 1113     char *html = NULL;
 1114     PangoLayout *layout;
 1115     PangoRectangle rect;
 1116     float x, y;
 1117 
 1118     layout = create_pango_layout(d);
 1119 
 1120     if (rich)
 1121     {
 1122         pango_layout_set_text(layout, "", 0);
 1123         pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
 1124         html = gt_html_to_pango_string(text, len, false);
 1125         pango_layout_set_markup(layout, html, -1);
 1126     }
 1127     else
 1128     {
 1129         pango_layout_set_markup(layout, "", 0);
 1130         pango_layout_set_text(layout, text, len);
 1131     }
 1132 
 1133     if (width > 0)
 1134         pango_layout_set_width(layout, width * PANGO_SCALE);
 1135     else
 1136         pango_layout_set_width(layout, -1);
 1137 
 1138     update_layout(d);
 1139     //gt_add_layout_from_font(layout, dx->font, d->resolutionY);
 1140 
 1141     pango_layout_get_extents(layout, &rect, NULL);
 1142 
 1143     GetCurrentPoint(d, &x, &y);
 1144 
 1145     ext->x1 = (float)rect.x / PANGO_SCALE + x;
 1146     ext->y1 = (float)rect.y / PANGO_SCALE + y - dx->ascent;
 1147     ext->x2 = ext->x1 + (float)rect.width / PANGO_SCALE;
 1148     ext->y2 = ext->y1 + (float)rect.height / PANGO_SCALE;
 1149 
 1150     if (html) g_free(html);
 1151 }
 1152 
 1153 static void TextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext)
 1154 {
 1155     get_text_extents(d, FALSE, text, len, ext, -1);
 1156 }
 1157 
 1158 static void RichTextExtents(GB_PAINT *d, const char *text, int len, GB_EXTENTS *ext, float width)
 1159 {
 1160     get_text_extents(d, TRUE, text, len, ext, width);
 1161 }
 1162 
 1163 static void TextSize(GB_PAINT *d, const char *text, int len, float *w, float *h)
 1164 {
 1165     GB_PAINT_EXTRA *dx = EXTRA(d);
 1166     float scale;
 1167 
 1168     scale = (float)d->resolutionY / gDesktop::resolution();
 1169 
 1170     dx->font->textSize(text, len, w, h);
 1171 
 1172     if (w) *w *= scale;
 1173     if (h) *h *= scale;
 1174 }
 1175 
 1176 static void RichTextSize(GB_PAINT *d, const char *text, int len, float sw, float *w, float *h)
 1177 {
 1178     GB_PAINT_EXTRA *dx = EXTRA(d);
 1179     float scale;
 1180 
 1181     scale = (float)d->resolutionY / gDesktop::resolution();
 1182 
 1183     if (sw > 0)
 1184         sw /= scale;
 1185 
 1186     dx->font->richTextSize(text, len, sw, w, h);
 1187     *w *= scale;
 1188     *h *= scale;
 1189 }
 1190 
 1191 
 1192 static void Matrix(GB_PAINT *d, int set, GB_TRANSFORM matrix)
 1193 {
 1194     cairo_matrix_t *t = (cairo_matrix_t *)matrix;
 1195 
 1196     if (set)
 1197     {
 1198         if (t)
 1199             cairo_set_matrix(CONTEXT(d), t);
 1200         else
 1201         {
 1202             //if (EXTRA(d)->print_context)
 1203                 cairo_set_matrix(CONTEXT(d), &EXTRA(d)->init);
 1204             /*else
 1205             {
 1206                 cairo_
 1207                 cairo_identity_matrix(CONTEXT(d));
 1208                 cairo_translate(CONTEXT(d), EXTRA(d)->dx, EXTRA(d)->dy);
 1209             }*/
 1210         }
 1211     }
 1212     else
 1213         cairo_get_matrix(CONTEXT(d), t);
 1214 }
 1215 
 1216 
 1217 static void SetBrush(GB_PAINT *d, GB_BRUSH brush)
 1218 {
 1219     cairo_set_source(CONTEXT(d), (cairo_pattern_t *)brush);
 1220 }
 1221 
 1222 static void BrushOrigin(GB_PAINT *d, int set, float *x, float *y)
 1223 {
 1224     if (set)
 1225     {
 1226         cairo_pattern_t *brush;
 1227         cairo_matrix_t matrix;
 1228 
 1229         brush = cairo_get_source(CONTEXT(d));
 1230         cairo_pattern_get_matrix(brush, &matrix);
 1231         cairo_matrix_translate(&matrix, EXTRA(d)->bx, EXTRA(d)->by);
 1232         cairo_matrix_translate(&matrix, (- *x), (- *y));
 1233         cairo_pattern_set_matrix(brush, &matrix);
 1234 
 1235         EXTRA(d)->bx = *x;
 1236         EXTRA(d)->by = *y;
 1237     }
 1238     else
 1239     {
 1240         *x = EXTRA(d)->bx;
 1241         *y = EXTRA(d)->by;
 1242     }
 1243 }
 1244 
 1245 static void BrushFree(GB_BRUSH brush)
 1246 {
 1247     // Should I release the surface associated with an image brush? Apparently no.
 1248     cairo_pattern_destroy((cairo_pattern_t *)brush);
 1249 }
 1250 
 1251 static void BrushColor(GB_BRUSH *brush, GB_COLOR color)
 1252 {
 1253     int r, g, b, a;
 1254     GB_COLOR_SPLIT(color, r, g, b, a);
 1255     *brush = (GB_BRUSH)cairo_pattern_create_rgba(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
 1256 }
 1257 
 1258 static void BrushImage(GB_BRUSH *brush, GB_IMAGE image)
 1259 {
 1260     gPicture *picture = CIMAGE_get((CIMAGE *)image);
 1261     cairo_surface_t *surface;
 1262     cairo_pattern_t *pattern;
 1263 
 1264     surface = gt_cairo_create_surface_from_pixbuf(picture->getPixbuf());
 1265 
 1266     pattern = cairo_pattern_create_for_surface(surface);
 1267     cairo_surface_destroy(surface);
 1268 
 1269     cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
 1270 
 1271     *brush = (GB_BRUSH)pattern;
 1272 }
 1273 
 1274 static void handle_color_stop(cairo_pattern_t *pattern, int nstop, const double *positions, const GB_COLOR *colors)
 1275 {
 1276     int i, r, g, b, a;
 1277 
 1278     for (i = 0; i < nstop; i++)
 1279     {
 1280         GB_COLOR_SPLIT(colors[i], r, g, b, a);
 1281         cairo_pattern_add_color_stop_rgba(pattern, positions[i], r / 255.0, g / 255.0, b / 255.0, a / 255.0);
 1282     }
 1283 }
 1284 
 1285 static void set_pattern_extend(cairo_pattern_t *pattern, int extend)
 1286 {
 1287     cairo_extend_t cext;
 1288 
 1289     switch (extend)
 1290     {
 1291         case GB_PAINT_EXTEND_REPEAT: cext = CAIRO_EXTEND_REPEAT; break;
 1292         case GB_PAINT_EXTEND_REFLECT: cext = CAIRO_EXTEND_REFLECT; break;
 1293         case GB_PAINT_EXTEND_PAD: default: cext = CAIRO_EXTEND_PAD;
 1294     }
 1295 
 1296     cairo_pattern_set_extend(pattern, cext);
 1297 }
 1298 
 1299 static void BrushLinearGradient(GB_BRUSH *brush, float x0, float y0, float x1, float y1, int nstop, double *positions, GB_COLOR *colors, int extend)
 1300 {
 1301     cairo_pattern_t *pattern;
 1302 
 1303     pattern = cairo_pattern_create_linear(x0, y0, x1, y1);
 1304 
 1305     handle_color_stop(pattern, nstop, positions, colors);
 1306     set_pattern_extend(pattern, extend);
 1307 
 1308     *brush = (GB_BRUSH)pattern;
 1309 }
 1310 
 1311 static void BrushRadialGradient(GB_BRUSH *brush, float cx, float cy, float r, float fx, float fy, int nstop, double *positions, GB_COLOR *colors, int extend)
 1312 {
 1313     cairo_pattern_t *pattern;
 1314 
 1315     // I know that from librsvg sources
 1316     pattern = cairo_pattern_create_radial(fx, fy, 0.0, cx, cy, r);
 1317 
 1318     handle_color_stop(pattern, nstop, positions, colors);
 1319     set_pattern_extend(pattern, extend);
 1320 
 1321     *brush = (GB_BRUSH)pattern;
 1322 }
 1323 
 1324 // Matrix must be inverted, so that it behaves the same way as in Qt4
 1325 
 1326 static void BrushMatrix(GB_BRUSH brush, int set, GB_TRANSFORM matrix)
 1327 {
 1328     cairo_matrix_t *t = (cairo_matrix_t *)matrix;
 1329     cairo_pattern_t *pattern = (cairo_pattern_t *)brush;
 1330     cairo_matrix_t actual;
 1331 
 1332     if (set)
 1333     {
 1334         if (t)
 1335         {
 1336             actual = *t;
 1337             cairo_matrix_invert(&actual);
 1338         }
 1339         else
 1340             cairo_matrix_init_identity(&actual);
 1341 
 1342         cairo_pattern_set_matrix(pattern, &actual);
 1343     }
 1344     else
 1345     {
 1346         cairo_pattern_get_matrix(pattern, t);
 1347         cairo_matrix_invert(t);
 1348     }
 1349 }
 1350 
 1351 static void TransformCreate(GB_TRANSFORM *matrix)
 1352 {
 1353     GB.Alloc(POINTER(matrix), sizeof(cairo_matrix_t));
 1354     cairo_matrix_init_identity((cairo_matrix_t *)*matrix);
 1355 }
 1356 
 1357 static void TransformCopy(GB_TRANSFORM *matrix, GB_TRANSFORM copy)
 1358 {
 1359     GB.Alloc(POINTER(matrix), sizeof(cairo_matrix_t));
 1360     *(cairo_matrix_t *)*matrix = *(cairo_matrix_t *)copy;
 1361 }
 1362 
 1363 static void TransformDelete(GB_TRANSFORM *matrix)
 1364 {
 1365     GB.Free(POINTER(matrix));
 1366 }
 1367 
 1368 static void TransformInit(GB_TRANSFORM matrix, float xx, float yx, float xy, float yy, float x0, float y0)
 1369 {
 1370     cairo_matrix_init((cairo_matrix_t *)matrix, xx, yx, xy, yy, x0, y0);
 1371 }
 1372 
 1373 static void TransformTranslate(GB_TRANSFORM matrix, float tx, float ty)
 1374 {
 1375     cairo_matrix_translate((cairo_matrix_t *)matrix, tx, ty);
 1376 }
 1377 
 1378 static void TransformScale(GB_TRANSFORM matrix, float sx, float sy)
 1379 {
 1380     cairo_matrix_scale((cairo_matrix_t *)matrix, sx, sy);
 1381 }
 1382 
 1383 static void TransformRotate(GB_TRANSFORM matrix, float angle)
 1384 {
 1385     cairo_matrix_rotate((cairo_matrix_t *)matrix, -angle);
 1386 }
 1387 
 1388 static int TransformInvert(GB_TRANSFORM matrix)
 1389 {
 1390     cairo_status_t status = cairo_matrix_invert((cairo_matrix_t *)matrix);
 1391     return status != CAIRO_STATUS_SUCCESS;
 1392 }
 1393 
 1394 static void TransformMultiply(GB_TRANSFORM matrix, GB_TRANSFORM matrix2)
 1395 {
 1396     cairo_matrix_multiply((cairo_matrix_t *)matrix, (cairo_matrix_t *)matrix, (cairo_matrix_t *)matrix2);
 1397 }
 1398 
 1399 static void TransformMap(GB_TRANSFORM matrix, double *x, double *y)
 1400 {
 1401     cairo_matrix_transform_point((cairo_matrix_t *)matrix, x, y);
 1402 }
 1403 
 1404 static void my_cairo_fill(cairo_t *cr)
 1405 {
 1406     if (cairo_get_operator(cr) == CAIRO_OPERATOR_OVER)
 1407         cairo_fill(cr);
 1408     else
 1409     {
 1410         cairo_save(cr);
 1411         cairo_clip(cr);
 1412         cairo_paint(cr);
 1413         cairo_restore(cr);
 1414     }
 1415 }
 1416 
 1417 static void DrawImage(GB_PAINT *d, GB_IMAGE image, float x, float y, float w, float h, float opacity, GB_RECT *source)
 1418 {
 1419     cairo_t *cr = CONTEXT(d);
 1420     cairo_surface_t *surface;
 1421     cairo_pattern_t *pattern = NULL;
 1422     cairo_pattern_t *save;
 1423     cairo_matrix_t matrix;
 1424 
 1425     save = cairo_get_source(cr);
 1426     cairo_pattern_reference(save);
 1427     cairo_save(cr);
 1428 
 1429     surface = check_image(image); //picture->getSurface();
 1430 
 1431     pattern = cairo_pattern_create_for_surface(surface);
 1432 
 1433     cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
 1434 
 1435     if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0)
 1436         cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
 1437 
 1438     cairo_matrix_init_identity(&matrix);
 1439     cairo_matrix_translate(&matrix, x, y);
 1440     if (source)
 1441     {
 1442         cairo_matrix_scale(&matrix, w / source->w, h / source->h);
 1443         cairo_matrix_translate(&matrix, -source->x, -source->y);
 1444     }
 1445     else if (w > 0 && h > 0)
 1446         cairo_matrix_scale(&matrix, w / cairo_image_surface_get_width(surface), h / cairo_image_surface_get_height(surface));
 1447 
 1448     cairo_matrix_invert(&matrix);
 1449     cairo_pattern_set_matrix(pattern, &matrix);
 1450     cairo_set_source(cr, pattern);
 1451 
 1452     cairo_rectangle(cr, x, y, w, h);
 1453 
 1454     if (opacity == 1.0)
 1455     {
 1456         my_cairo_fill(cr);
 1457     }
 1458     else
 1459     {
 1460         cairo_clip(cr);
 1461         cairo_paint_with_alpha(cr, opacity);
 1462     }
 1463 
 1464     cairo_restore(cr);
 1465     cairo_set_source(cr, save);
 1466     cairo_pattern_destroy(save);
 1467 
 1468     cairo_pattern_destroy(pattern);
 1469 }
 1470 
 1471 #ifdef GTK3
 1472 static void DrawPicture(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source)
 1473 {
 1474     cairo_t *cr = CONTEXT(d);
 1475     gPicture *pic = ((CPICTURE *)picture)->picture;
 1476     cairo_surface_t *surface;
 1477     cairo_pattern_t *pattern = NULL;
 1478     cairo_pattern_t *save;
 1479     cairo_matrix_t matrix;
 1480 
 1481     cairo_save(cr);
 1482 
 1483     save = cairo_get_source(cr);
 1484     cairo_pattern_reference(save);
 1485 
 1486     surface = pic->getSurface();
 1487 
 1488     pattern = cairo_pattern_create_for_surface(surface);
 1489 
 1490     cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
 1491 
 1492     if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0)
 1493         cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
 1494 
 1495     cairo_matrix_init_identity(&matrix);
 1496     cairo_matrix_translate(&matrix, x, y);
 1497     if (source)
 1498     {
 1499         cairo_matrix_scale(&matrix, w / source->w, h / source->h);
 1500         cairo_matrix_translate(&matrix, -source->x, -source->y);
 1501     }
 1502     else if (w > 0 && h > 0)
 1503         cairo_matrix_scale(&matrix, w / cairo_image_surface_get_width(surface), h / cairo_image_surface_get_height(surface));
 1504 
 1505     cairo_matrix_invert(&matrix);
 1506     cairo_pattern_set_matrix(pattern, &matrix);
 1507     cairo_set_source(cr, pattern);
 1508 
 1509     cairo_rectangle(cr, x, y, w, h);
 1510     my_cairo_fill(cr);
 1511 
 1512     cairo_set_source(cr, save);
 1513     cairo_pattern_destroy(save);
 1514 
 1515     cairo_restore(cr);
 1516 
 1517     cairo_pattern_destroy(pattern);
 1518 }
 1519 #else
 1520 static void DrawPicture(GB_PAINT *d, GB_PICTURE picture, float x, float y, float w, float h, GB_RECT *source)
 1521 {
 1522     cairo_pattern_t *pattern, *save;
 1523     cairo_matrix_t matrix;
 1524     gPicture *pic = ((CPICTURE *)picture)->picture;
 1525 
 1526     if (pic->type() != gPicture::PIXMAP || source)
 1527     {
 1528         gt_cairo_draw_pixbuf(CONTEXT(d), pic->getPixbuf(), x, y, w, h, 1.0, source);
 1529         return;
 1530     }
 1531 
 1532     x += DX(d);
 1533     y += DY(d);
 1534 
 1535     cairo_save(CONTEXT(d));
 1536     save = cairo_get_source(CONTEXT(d));
 1537     cairo_pattern_reference(save);
 1538 
 1539     gdk_cairo_set_source_pixmap(CONTEXT(d), pic->getPixmap(), 0, 0);
 1540 
 1541     pattern = cairo_get_source(CONTEXT(d));
 1542     cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
 1543 
 1544     /*if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0)
 1545         cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);*/
 1546 
 1547     //gdk_cairo_set_source_pixbuf(CONTEXT(d), picture->getPixbuf(), x, y);
 1548 
 1549     cairo_matrix_init_identity(&matrix);
 1550     cairo_matrix_translate(&matrix, x, y);
 1551     cairo_matrix_scale(&matrix, w / pic->width(), h / pic->height());
 1552     cairo_matrix_invert(&matrix);
 1553     cairo_pattern_set_matrix(pattern, &matrix);
 1554 
 1555     cairo_rectangle(CONTEXT(d), x, y, w, h);
 1556     my_cairo_fill(CONTEXT(d));
 1557 
 1558     cairo_set_source(CONTEXT(d), save);
 1559     cairo_pattern_destroy(save);
 1560 
 1561     cairo_restore(CONTEXT(d));
 1562 }
 1563 #endif
 1564 
 1565 static void GetPictureInfo(GB_PAINT *d, GB_PICTURE picture, GB_PICTURE_INFO *info)
 1566 {
 1567     gPicture *pic = ((CPICTURE *)picture)->picture;
 1568 
 1569     info->width = pic->width();
 1570     info->height = pic->height();
 1571 }
 1572 
 1573 static void FillRect(GB_PAINT *d, float x, float y, float w, float h, GB_COLOR color)
 1574 {
 1575     cairo_pattern_t *save;
 1576 
 1577     x += DX(d);
 1578     y += DY(d);
 1579 
 1580     save = cairo_get_source(CONTEXT(d));
 1581     cairo_pattern_reference(save);
 1582 
 1583     Background(d, TRUE, &color);
 1584     cairo_rectangle(CONTEXT(d), x, y, w, h);
 1585     my_cairo_fill(CONTEXT(d));
 1586 
 1587     cairo_set_source(CONTEXT(d), save);
 1588     cairo_pattern_destroy(save);
 1589 }
 1590 
 1591 
 1592 GB_PAINT_DESC PAINT_Interface =
 1593 {
 1594     sizeof(GB_PAINT_EXTRA),
 1595     Begin,
 1596     End,
 1597     Save,
 1598     Restore,
 1599     Antialias,
 1600     _Font,
 1601     Background,
 1602     Invert,
 1603     Clip,
 1604     ResetClip,
 1605     ClipExtents,
 1606     ClipRect,
 1607     Fill,
 1608     Stroke,
 1609     PathExtents,
 1610     PathContains,
 1611     PathOutline,
 1612     Dash,
 1613     DashOffset,
 1614     FillRule,
 1615     FillStyle,
 1616     LineCap,
 1617     LineJoin,
 1618     LineWidth,
 1619     MiterLimit,
 1620     Operator,
 1621     NewPath,
 1622     ClosePath,
 1623     Arc,
 1624     Ellipse,
 1625     Rectangle,
 1626     GetCurrentPoint,
 1627     MoveTo,
 1628     LineTo,
 1629     CurveTo,
 1630     Text,
 1631     TextExtents,
 1632     TextSize,
 1633     RichText,
 1634     RichTextExtents,
 1635     RichTextSize,
 1636     Matrix,
 1637     SetBrush,
 1638     BrushOrigin,
 1639     DrawImage,
 1640     DrawPicture,
 1641     GetPictureInfo,
 1642     FillRect,
 1643     {
 1644         BrushFree,
 1645         BrushColor,
 1646         BrushImage,
 1647         BrushLinearGradient,
 1648         BrushRadialGradient,
 1649         BrushMatrix,
 1650     }
 1651 };
 1652 
 1653 GB_PAINT_MATRIX_DESC PAINT_MATRIX_Interface =
 1654 {
 1655     TransformCreate,
 1656     TransformCopy,
 1657     TransformDelete,
 1658     TransformInit,
 1659     TransformTranslate,
 1660     TransformScale,
 1661     TransformRotate,
 1662     TransformInvert,
 1663     TransformMultiply,
 1664     TransformMap
 1665 };
 1666 
 1667 void PAINT_begin(void *device)
 1668 {
 1669     _internal_paint = true;
 1670     DRAW.Paint.Begin(device);
 1671     _internal_paint = false;
 1672 }
 1673 
 1674 bool PAINT_is_internal()
 1675 {
 1676     return _internal_paint;
 1677 }
 1678 
 1679 void PAINT_end()
 1680 {
 1681     DRAW.Paint.End();
 1682 }
 1683 
 1684 void PAINT_clip(int x, int y, int w, int h)
 1685 {
 1686     GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent();
 1687     if (d)
 1688     {
 1689         cairo_rectangle(CONTEXT(d), (double)x, (double)y, (double)w, (double)h);
 1690         cairo_clip(CONTEXT(d));
 1691     }
 1692 }
 1693 
 1694 #ifdef GTK3
 1695 #else
 1696 void PAINT_clip_region(GdkRegion *region)
 1697 {
 1698     GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent();
 1699     if (d)
 1700     {
 1701         gdk_cairo_region(CONTEXT(d), region);
 1702         cairo_clip(CONTEXT(d));
 1703     }
 1704 }
 1705 #endif
 1706 
 1707 cairo_t *PAINT_get_current_context()
 1708 {
 1709     GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent();
 1710     if (d)
 1711         return CONTEXT(d);
 1712 
 1713     GB.Error("No current device");
 1714     return NULL;
 1715 }
 1716 
 1717 void *PAINT_get_current_device()
 1718 {
 1719     GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent();
 1720     if (d)
 1721         return d->device;
 1722 
 1723     GB.Error("No current device");
 1724     return NULL;
 1725 }
 1726 
 1727 bool PAINT_get_clip(int *x, int *y, int *w, int *h)
 1728 {
 1729     GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent();
 1730     GB_EXTENTS ext;
 1731 
 1732     ClipExtents(d, &ext);
 1733 
 1734     *x = ceilf(ext.x1);
 1735     *y = ceilf(ext.y1);
 1736     *w = floorf(ext.x2) - *x;
 1737     *h = floorf(ext.y2) - *y;
 1738 
 1739     return *w <= 0 || *h <= 0;
 1740 }
 1741 
 1742 void PAINT_apply_offset(int *x, int *y)
 1743 {
 1744     GB_PAINT *d = (GB_PAINT *)DRAW.Paint.GetCurrent();
 1745     *x += EXTRA(d)->dx;
 1746     *y += EXTRA(d)->dy;
 1747 }