1521 lines
45 KiB
C++
1521 lines
45 KiB
C++
//
|
||
// Support for Cairo graphics for the Fast Light Tool Kit (FLTK).
|
||
//
|
||
// Copyright 2021-2023 by Bill Spitzak and others.
|
||
//
|
||
// This library is free software. Distribution and use rights are outlined in
|
||
// the file "COPYING" which should have been included with this file. If this
|
||
// file is missing or damaged, see the license at:
|
||
//
|
||
// https://www.fltk.org/COPYING.php
|
||
//
|
||
// Please see the following page on how to report bugs and issues:
|
||
//
|
||
// https://www.fltk.org/bugs.php
|
||
//
|
||
|
||
/* \file
|
||
Implementation of class Fl_Cairo_Graphics_Driver .
|
||
*/
|
||
|
||
#include <config.h>
|
||
#if USE_PANGO
|
||
|
||
#include "Fl_Cairo_Graphics_Driver.H"
|
||
#include "../../Fl_Screen_Driver.H"
|
||
#include <FL/platform.H>
|
||
#include <FL/fl_draw.H>
|
||
#include <FL/fl_utf8.h>
|
||
#include <cairo/cairo.h>
|
||
#include <pango/pangocairo.h>
|
||
#if ! PANGO_VERSION_CHECK(1,16,0)
|
||
# error "Requires Pango 1.16 or higher"
|
||
#endif
|
||
#include <math.h>
|
||
#include <stdlib.h> // abs(int)
|
||
#include <string.h> // memcpy()
|
||
#include <stdint.h> // uint32_t
|
||
|
||
extern unsigned fl_cmap[256]; // defined in fl_color.cxx
|
||
|
||
// The predefined fonts that FLTK has with Pango:
|
||
static Fl_Fontdesc built_in_table[] = {
|
||
{"Sans"},
|
||
{"Sans Bold"},
|
||
{"Sans Italic"},
|
||
{"Sans Bold Italic"},
|
||
{"Monospace"},
|
||
{"Monospace Bold"},
|
||
{"Monospace Italic"},
|
||
{"Monospace Bold Italic"},
|
||
{"Serif"},
|
||
{"Serif Bold"},
|
||
{"Serif Italic"},
|
||
{"Serif Bold Italic"},
|
||
{"Standard Symbols PS"}, // FL_SYMBOL
|
||
{"Monospace"},
|
||
{"Monospace Bold"},
|
||
{"D050000L"}, // FL_ZAPF_DINGBATS
|
||
};
|
||
|
||
|
||
FL_EXPORT Fl_Fontdesc *fl_fonts = built_in_table;
|
||
|
||
|
||
// duplicated from Fl_PostScript.cxx
|
||
struct callback_data {
|
||
const uchar *data;
|
||
int D, LD;
|
||
};
|
||
static const int dashes_flat[5][7]={
|
||
{-1,0,0,0,0,0,0},
|
||
{3,1,-1,0,0,0,0},
|
||
{1,1,-1,0,0,0,0},
|
||
{3,1,1,1,-1,0,0},
|
||
{3,1,1,1,1,1,-1}
|
||
};
|
||
static const double dashes_cap[5][7]={
|
||
{-1,0,0,0,0,0,0},
|
||
{2,2,-1,0,0,0,0},
|
||
{0.01,1.99,-1,0,0,0,0},
|
||
{2,2,0.01,1.99,-1,0,0},
|
||
{2,2,0.01,1.99,0.01,1.99,-1}
|
||
};
|
||
static void draw_image_cb(void *data, int x, int y, int w, uchar *buf) {
|
||
struct callback_data *cb_data;
|
||
const uchar *curdata;
|
||
|
||
cb_data = (struct callback_data*)data;
|
||
int last = x+w;
|
||
const size_t aD = abs(cb_data->D);
|
||
curdata = cb_data->data + x*cb_data->D + y*cb_data->LD;
|
||
for (; x<last; x++) {
|
||
memcpy(buf, curdata, aD);
|
||
buf += aD;
|
||
curdata += cb_data->D;
|
||
}
|
||
}
|
||
// end of duplicated part
|
||
|
||
|
||
Fl_Cairo_Graphics_Driver::Fl_Cairo_Graphics_Driver() : Fl_Graphics_Driver() {
|
||
cairo_ = NULL;
|
||
pango_layout_ = NULL;
|
||
pango_context_ = NULL;
|
||
dummy_cairo_ = NULL;
|
||
linestyle_ = FL_SOLID;
|
||
clip_ = NULL;
|
||
scale_x = scale_y = 1;
|
||
wld_scale = 1;
|
||
angle = 0;
|
||
left_margin = top_margin = 0;
|
||
needs_commit_tag_ = NULL;
|
||
what = NONE;
|
||
}
|
||
|
||
Fl_Cairo_Graphics_Driver::~Fl_Cairo_Graphics_Driver() {
|
||
if (pango_layout_) g_object_unref(pango_layout_);
|
||
if (pango_context_) g_object_unref(pango_context_);
|
||
}
|
||
|
||
const cairo_format_t Fl_Cairo_Graphics_Driver::cairo_format = CAIRO_FORMAT_ARGB32;
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::set_cairo(cairo_t *cr, float s) {
|
||
if (dummy_cairo_) {
|
||
cairo_destroy(dummy_cairo_);
|
||
dummy_cairo_ = NULL;
|
||
}
|
||
cairo_ = cr;
|
||
cairo_restore(cairo_);
|
||
line_style(0);
|
||
cairo_save(cairo_);
|
||
if (s == 0) s = scale();
|
||
cairo_scale(cairo_, s, s);
|
||
cairo_translate(cairo_, 0.5, 0.5);
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::rectf(int x, int y, int w, int h) {
|
||
cairo_rectangle(cairo_, x-0.5, y-0.5, w, h);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_fill(cairo_);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::rect(int x, int y, int w, int h) {
|
||
cairo_rectangle(cairo_, x, y, w-1, h-1);
|
||
if (linestyle_ == FL_SOLID) cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_stroke(cairo_);
|
||
if (linestyle_ == FL_SOLID) cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
surface_needs_commit();
|
||
}
|
||
|
||
static bool need_antialias_none(cairo_t *cairo_, int style) {
|
||
cairo_matrix_t matrix;
|
||
cairo_get_matrix(cairo_, &matrix);
|
||
double width = cairo_get_line_width(cairo_) * matrix.xx;
|
||
bool needit = (style == FL_SOLID && width < 1.5);
|
||
if (needit) cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
return needit;
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::line(int x1, int y1, int x2, int y2) {
|
||
cairo_new_path(cairo_);
|
||
cairo_move_to(cairo_, x1, y1);
|
||
cairo_line_to(cairo_, x2, y2);
|
||
bool needit = need_antialias_none(cairo_, linestyle_);
|
||
cairo_stroke(cairo_);
|
||
if (needit) cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::line(int x0, int y0, int x1, int y1, int x2, int y2) {
|
||
cairo_new_path(cairo_);
|
||
cairo_move_to(cairo_, x0, y0);
|
||
cairo_line_to(cairo_, x1, y1);
|
||
cairo_line_to(cairo_, x2, y2);
|
||
bool needit = need_antialias_none(cairo_, linestyle_);
|
||
cairo_stroke(cairo_);
|
||
if (needit) cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::xyline(int x, int y, int x1) {
|
||
cairo_move_to(cairo_, x, y);
|
||
cairo_line_to(cairo_, x1, y);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_stroke(cairo_);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::xyline(int x, int y, int x1, int y2) {
|
||
cairo_move_to(cairo_, x, y);
|
||
cairo_line_to(cairo_, x1, y);
|
||
cairo_line_to(cairo_, x1, y2);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_stroke(cairo_);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::xyline(int x, int y, int x1, int y2, int x3) {
|
||
cairo_move_to(cairo_, x, y);
|
||
cairo_line_to(cairo_, x1, y);
|
||
cairo_line_to(cairo_, x1, y2);
|
||
cairo_line_to(cairo_, x3, y2);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_stroke(cairo_);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::yxline(int x, int y, int y1) {
|
||
cairo_move_to(cairo_, x, y);
|
||
cairo_line_to(cairo_, x, y1);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_stroke(cairo_);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::yxline(int x, int y, int y1, int x2) {
|
||
cairo_move_to(cairo_, x, y);
|
||
cairo_line_to(cairo_, x, y1);
|
||
cairo_line_to(cairo_, x2, y1);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_stroke(cairo_);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::yxline(int x, int y, int y1, int x2, int y3) {
|
||
cairo_move_to(cairo_, x, y);
|
||
cairo_line_to(cairo_, x, y1);
|
||
cairo_line_to(cairo_, x2, y1);
|
||
cairo_line_to(cairo_, x2, y3);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_stroke(cairo_);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2) {
|
||
cairo_save(cairo_);
|
||
cairo_new_path(cairo_);
|
||
cairo_move_to(cairo_, x0, y0);
|
||
cairo_line_to(cairo_, x1, y1);
|
||
cairo_line_to(cairo_, x2, y2);
|
||
cairo_close_path(cairo_);
|
||
cairo_stroke(cairo_);
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
|
||
cairo_save(cairo_);
|
||
cairo_new_path(cairo_);
|
||
cairo_move_to(cairo_, x0, y0);
|
||
cairo_line_to(cairo_, x1, y1);
|
||
cairo_line_to(cairo_, x2, y2);
|
||
cairo_line_to(cairo_, x3, y3);
|
||
cairo_close_path(cairo_);
|
||
if ((y0==y1 && x1==x2 && y2==y3 && x3==x0) || (x0==x1 && y1==y2 && x2==x3 && y3==y0)) {
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
}
|
||
cairo_stroke(cairo_);
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2) {
|
||
cairo_save(cairo_);
|
||
cairo_new_path(cairo_);
|
||
cairo_move_to(cairo_, x0, y0);
|
||
cairo_line_to(cairo_, x1, y1);
|
||
cairo_line_to(cairo_, x2, y2);
|
||
cairo_close_path(cairo_);
|
||
cairo_fill(cairo_);
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
|
||
cairo_save(cairo_);
|
||
cairo_new_path(cairo_);
|
||
cairo_move_to(cairo_, x0, y0);
|
||
cairo_line_to(cairo_, x1, y1);
|
||
cairo_line_to(cairo_, x2, y2);
|
||
cairo_line_to(cairo_, x3, y3);
|
||
cairo_close_path(cairo_);
|
||
if ((y0==y1 && x1==x2 && y2==y3 && x3==x0) || (x0==x1 && y1==y2 && x2==x3 && y3==y0)) {
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
}
|
||
cairo_fill(cairo_);
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::line_style(int style, int width, char* dashes) {
|
||
linestyle_ = style;
|
||
if(dashes){
|
||
if(dashes != linedash_)
|
||
strcpy(linedash_,dashes);
|
||
|
||
} else
|
||
linedash_[0]=0;
|
||
char width0 = 0;
|
||
if (!width){
|
||
width=1; //for screen drawing compatibility
|
||
width0=1;
|
||
}
|
||
cairo_set_line_width(cairo_, width);
|
||
|
||
if(!style && (!dashes || !(*dashes)) && width0) //system lines
|
||
style = FL_CAP_SQUARE;
|
||
|
||
int cap = (style &0xf00);
|
||
cairo_line_cap_t c_cap;
|
||
if (cap == FL_CAP_SQUARE) c_cap = CAIRO_LINE_CAP_SQUARE;
|
||
else if (cap == FL_CAP_FLAT) c_cap = CAIRO_LINE_CAP_BUTT;
|
||
else if (cap == FL_CAP_ROUND) c_cap = CAIRO_LINE_CAP_ROUND;
|
||
else c_cap = CAIRO_LINE_CAP_BUTT;
|
||
cairo_set_line_cap(cairo_, c_cap);
|
||
|
||
int join = (style & 0xf000);
|
||
cairo_line_join_t c_join;
|
||
if (join == FL_JOIN_MITER) c_join = CAIRO_LINE_JOIN_MITER;
|
||
else if (join == FL_JOIN_ROUND)c_join = CAIRO_LINE_JOIN_ROUND;
|
||
else if (join == FL_JOIN_BEVEL) c_join = CAIRO_LINE_JOIN_BEVEL;
|
||
else c_join = CAIRO_LINE_JOIN_MITER;
|
||
cairo_set_line_join(cairo_, c_join);
|
||
|
||
double *ddashes = NULL;
|
||
int l = 0;
|
||
if (dashes && *dashes){
|
||
ddashes = new double[strlen(dashes)];
|
||
while (dashes[l]) {ddashes[l] = dashes[l]; l++; }
|
||
} else if (style & 0xff) {
|
||
ddashes = new double[6];
|
||
if (style & 0x200){ // round and square caps, dash length need to be adjusted
|
||
const double *dt = dashes_cap[style & 0xff];
|
||
while (*dt >= 0){
|
||
ddashes[l++] = width * (*dt);
|
||
dt++;
|
||
}
|
||
} else {
|
||
const int *ds = dashes_flat[style & 0xff];
|
||
while (*ds >= 0){
|
||
ddashes[l++] = width * (*ds);
|
||
ds++;
|
||
}
|
||
}
|
||
}
|
||
cairo_set_dash(cairo_, ddashes, l, 0);
|
||
cairo_set_antialias(cairo_, l ? CAIRO_ANTIALIAS_NONE : CAIRO_ANTIALIAS_DEFAULT);
|
||
delete[] ddashes;
|
||
check_status();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::color(unsigned char r, unsigned char g, unsigned char b) {
|
||
Fl_Graphics_Driver::color( fl_rgb_color(r, g, b) );
|
||
cr_ = r; cg_ = g; cb_ = b;
|
||
double fr, fg, fb;
|
||
fr = r/255.0;
|
||
fg = g/255.0;
|
||
fb = b/255.0;
|
||
cairo_set_source_rgb(cairo_, fr, fg, fb);
|
||
check_status();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::color(Fl_Color i) {
|
||
Fl_Graphics_Driver::color(i);
|
||
if (!cairo_) return; // no context yet? We will assign the color later.
|
||
uchar r, g, b;
|
||
double fa = 1.0;
|
||
if (i & 0xFFFFFF00) {
|
||
// translate rgb colors into color index
|
||
r = i>>24;
|
||
g = i>>16;
|
||
b = i>> 8;
|
||
} else {
|
||
// translate index into rgb:
|
||
unsigned c = fl_cmap[i];
|
||
c = c ^ 0x000000ff; // trick to restore the color's correct alpha value
|
||
r = c>>24;
|
||
g = c>>16;
|
||
b = c>> 8;
|
||
fa = (c & 0xff)/255.0;
|
||
}
|
||
double fr = r/255.0;
|
||
double fg = g/255.0;
|
||
double fb = b/255.0;
|
||
cairo_set_source_rgba(cairo_, fr, fg, fb, fa);
|
||
}
|
||
|
||
Fl_Color Fl_Cairo_Graphics_Driver::color() { return Fl_Graphics_Driver::color(); }
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::concat(){
|
||
cairo_matrix_t mat = {m.a , m.b , m.c , m.d , m.x , m.y};
|
||
cairo_transform(cairo_, &mat);
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::reconcat(){
|
||
cairo_matrix_t mat = {m.a , m.b , m.c , m.d , m.x , m.y};
|
||
cairo_status_t stat = cairo_matrix_invert(&mat);
|
||
if (stat != CAIRO_STATUS_SUCCESS) {
|
||
fputs("error in cairo_matrix_invert\n", stderr);
|
||
}
|
||
cairo_transform(cairo_, &mat);
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::begin_points() {
|
||
cairo_save(cairo_);
|
||
concat();
|
||
cairo_new_path(cairo_);
|
||
gap_=1;
|
||
what=POINTS;
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::begin_line() {
|
||
cairo_save(cairo_);
|
||
concat();
|
||
cairo_new_path(cairo_);
|
||
gap_=1;
|
||
what=LINE;
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::begin_loop() {
|
||
cairo_save(cairo_);
|
||
concat();
|
||
cairo_new_path(cairo_);
|
||
gap_=1;
|
||
what=LOOP;
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::begin_polygon() {
|
||
cairo_save(cairo_);
|
||
concat();
|
||
cairo_new_path(cairo_);
|
||
gap_=1;
|
||
what=POLYGON;
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::vertex(double x, double y) {
|
||
if (what==POINTS){
|
||
cairo_move_to(cairo_, x, y);
|
||
cairo_rectangle(cairo_, x-0.5, y-0.5, 1, 1);
|
||
cairo_fill(cairo_);
|
||
gap_=1;
|
||
return;
|
||
}
|
||
if (gap_){
|
||
cairo_move_to(cairo_, x, y);
|
||
gap_=0;
|
||
} else {
|
||
cairo_line_to(cairo_, x, y);
|
||
surface_needs_commit();
|
||
}
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::curve(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3)
|
||
{
|
||
if(what==NONE) return;
|
||
if (what == POINTS) Fl_Graphics_Driver::curve(x, y, x1, y1, x2, y2, x3, y3);
|
||
else {
|
||
if(gap_)
|
||
cairo_move_to(cairo_, x, y);
|
||
else
|
||
cairo_line_to(cairo_, x, y);
|
||
gap_=0;
|
||
cairo_curve_to(cairo_, x1 , y1 , x2 , y2 , x3 , y3);
|
||
}
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::circle(double x, double y, double r){
|
||
if (what == NONE) {
|
||
cairo_save(cairo_);
|
||
concat();
|
||
cairo_arc(cairo_, x, y, r, 0, 2*M_PI);
|
||
cairo_stroke(cairo_);
|
||
reconcat();
|
||
cairo_restore(cairo_);
|
||
} else {
|
||
cairo_arc(cairo_, x, y, r, 0, 2*M_PI);
|
||
}
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::arc(double x, double y, double r, double start, double a){
|
||
if (what == NONE) return;
|
||
if (gap_ == 1) cairo_new_sub_path(cairo_);
|
||
gap_ = 0;
|
||
if (start > a)
|
||
cairo_arc(cairo_, x, y, r, -start*M_PI/180, -a*M_PI/180);
|
||
else
|
||
cairo_arc_negative(cairo_, x, y, r, -start*M_PI/180, -a*M_PI/180);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::arc(int x, int y, int w, int h, double a1, double a2) {
|
||
if (w <= 1 || h <= 1) return;
|
||
cairo_save(cairo_);
|
||
begin_line();
|
||
cairo_translate(cairo_, x + w/2.0 -0.5 , y + h/2.0 - 0.5);
|
||
cairo_scale(cairo_, (w-1)/2.0 , (h-1)/2.0);
|
||
arc(0,0,1,a2,a1);
|
||
cairo_scale(cairo_, 2.0/(w-1) , 2.0/(h-1));
|
||
cairo_translate(cairo_, -x - w/2.0 +0.5 , -y - h/2.0 +0.5);
|
||
end_line();
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::pie(int x, int y, int w, int h, double a1, double a2) {
|
||
cairo_save(cairo_);
|
||
begin_polygon();
|
||
cairo_translate(cairo_, x + w/2.0 -0.5 , y + h/2.0 - 0.5);
|
||
cairo_scale(cairo_, w/2.0 , h/2.0);
|
||
vertex(0,0);
|
||
arc(0.0,0.0, 1, a2, a1);
|
||
end_polygon();
|
||
cairo_restore(cairo_);
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::end_points() {
|
||
end_line();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::end_line() {
|
||
gap_ = 1;
|
||
reconcat();
|
||
cairo_stroke(cairo_);
|
||
cairo_restore(cairo_);
|
||
what = NONE;
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::end_loop(){
|
||
gap_ = 1;
|
||
reconcat();
|
||
cairo_close_path(cairo_);
|
||
cairo_stroke(cairo_);
|
||
cairo_restore(cairo_);
|
||
what = NONE;
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::end_polygon() {
|
||
gap_ = 1;
|
||
reconcat();
|
||
cairo_close_path(cairo_);
|
||
cairo_fill(cairo_);
|
||
cairo_restore(cairo_);
|
||
what = NONE;
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::transformed_vertex(double x, double y) {
|
||
if (what == POINTS) {
|
||
cairo_move_to(cairo_, x, y);
|
||
point(x, y);
|
||
gap_ = 1;
|
||
} else {
|
||
reconcat();
|
||
if (gap_) {
|
||
cairo_move_to(cairo_, x, y);
|
||
gap_ = 0;
|
||
} else {
|
||
cairo_line_to(cairo_, x, y);
|
||
surface_needs_commit();
|
||
}
|
||
concat();
|
||
}
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::push_clip(int x, int y, int w, int h) {
|
||
Clip *c = new Clip();
|
||
clip_box(x,y,w,h,c->x,c->y,c->w,c->h);
|
||
c->prev = clip_;
|
||
clip_ = c;
|
||
cairo_save(cairo_);
|
||
cairo_rectangle(cairo_, clip_->x - 0.5 , clip_->y - 0.5 , clip_->w, clip_->h);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_clip(cairo_);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT);
|
||
check_status();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::push_no_clip() {
|
||
Clip *c = new Clip();
|
||
c->prev = clip_;
|
||
clip_ = c;
|
||
clip_->x = clip_->y = clip_->w = clip_->h = -1;
|
||
cairo_save(cairo_);
|
||
cairo_reset_clip(cairo_);
|
||
check_status();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::pop_clip() {
|
||
if(!clip_)return;
|
||
Clip *c = clip_;
|
||
clip_ = clip_->prev;
|
||
delete c;
|
||
cairo_restore(cairo_);
|
||
check_status();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::ps_origin(int x, int y) {
|
||
cairo_restore(cairo_);
|
||
cairo_restore(cairo_);
|
||
cairo_save(cairo_);
|
||
cairo_scale(cairo_, scale_x, scale_y);
|
||
cairo_translate(cairo_, x, y);
|
||
cairo_rotate(cairo_, angle * M_PI / 180);
|
||
cairo_save(cairo_);
|
||
check_status();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::ps_translate(int x, int y)
|
||
{
|
||
cairo_save(cairo_);
|
||
cairo_translate(cairo_, x, y);
|
||
cairo_save(cairo_);
|
||
check_status();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::ps_untranslate(void)
|
||
{
|
||
cairo_restore(cairo_);
|
||
cairo_restore(cairo_);
|
||
check_status();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::check_status(void) {
|
||
#ifdef DEBUG
|
||
if (cairo_status(cairo_) != CAIRO_STATUS_SUCCESS) {
|
||
fprintf(stderr,"we have a problem");
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw_image(Fl_Draw_Image_Cb call, void *data, int ix, int iy, int iw, int ih, int D)
|
||
{
|
||
uchar *array = new uchar[iw * D * ih];
|
||
for (int l = 0; l < ih; l++) {
|
||
call(data, 0, l, iw, array + l*D*iw);
|
||
if (D%2 == 0) for (int i = 0; i < iw; i++) {
|
||
*(array + l*D*iw + i*D + D-1) = 0xff;
|
||
}
|
||
}
|
||
Fl_RGB_Image *rgb = new Fl_RGB_Image(array, iw, ih, D);
|
||
rgb->alloc_array = 1;
|
||
draw_rgb(rgb, ix, iy, iw, ih, 0, 0);
|
||
delete rgb;
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw_image_mono(const uchar *data, int ix, int iy, int iw, int ih, int D, int LD)
|
||
{
|
||
struct callback_data cb_data;
|
||
const size_t aD = abs(D);
|
||
if (!LD) LD = iw * aD;
|
||
if (D<0) data += iw * aD;
|
||
cb_data.data = data;
|
||
cb_data.D = D;
|
||
cb_data.LD = LD;
|
||
draw_image(draw_image_cb, &cb_data, ix, iy, iw, ih, abs(D));
|
||
surface_needs_commit();
|
||
}
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw_image_mono(Fl_Draw_Image_Cb call, void *data, int ix, int iy, int iw, int ih, int D)
|
||
{
|
||
draw_image(call, data, ix, iy, iw, ih, D);
|
||
}
|
||
|
||
|
||
int Fl_Cairo_Graphics_Driver::not_clipped(int x, int y, int w, int h) {
|
||
if (!clip_) return 1;
|
||
if (clip_->w < 0) return 1;
|
||
int X = 0, Y = 0, W = 0, H = 0;
|
||
clip_box(x, y, w, h, X, Y, W, H);
|
||
if (W) return 1;
|
||
return 0;
|
||
}
|
||
|
||
|
||
int Fl_Cairo_Graphics_Driver::clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H) {
|
||
if (!clip_) {
|
||
X = x; Y = y; W = w; H = h;
|
||
return 0;
|
||
}
|
||
if (clip_->w < 0) {
|
||
X = x; Y = y; W = w; H = h;
|
||
return 1;
|
||
}
|
||
int ret = 0;
|
||
if (x > (X=clip_->x)) {X=x; ret=1;}
|
||
if (y > (Y=clip_->y)) {Y=y; ret=1;}
|
||
if ((x+w) < (clip_->x+clip_->w)) {
|
||
W=x+w-X;
|
||
|
||
ret=1;
|
||
|
||
}else
|
||
W = clip_->x + clip_->w - X;
|
||
if(W<0){
|
||
W=0;
|
||
return 1;
|
||
}
|
||
if ((y+h) < (clip_->y+clip_->h)) {
|
||
H=y+h-Y;
|
||
ret=1;
|
||
}else
|
||
H = clip_->y + clip_->h - Y;
|
||
if(H<0){
|
||
W=0;
|
||
H=0;
|
||
return 1;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::point(int x, int y) {
|
||
rectf(x, y, 1, 1);
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw_image(const uchar *data, int ix, int iy, int iw, int ih, int D, int LD) {
|
||
if (abs(D)<3){ //mono
|
||
draw_image_mono(data, ix, iy, iw, ih, D, LD);
|
||
return;
|
||
}
|
||
struct callback_data cb_data;
|
||
if (!LD) LD = iw*abs(D);
|
||
if (D<0) data += iw*abs(D);
|
||
cb_data.data = data;
|
||
cb_data.D = D;
|
||
cb_data.LD = LD;
|
||
Fl_Cairo_Graphics_Driver::draw_image(draw_image_cb, &cb_data, ix, iy, iw, ih, abs(D));
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::overlay_rect(int x, int y, int w , int h) {
|
||
cairo_save(cairo_);
|
||
cairo_matrix_t mat;
|
||
cairo_get_matrix(cairo_, &mat);
|
||
float s = (float)mat.xx;
|
||
cairo_matrix_init_identity(&mat);
|
||
cairo_set_matrix(cairo_, &mat); // use drawing units
|
||
int lwidth = s < 1 ? 1 : int(s);
|
||
cairo_set_line_width(cairo_, lwidth);
|
||
cairo_translate(cairo_, lwidth/2., lwidth/2.); // translate by half of line width
|
||
double ddash = (lwidth > 2 ? lwidth : 2);
|
||
if (linestyle_ == FL_DOT) {
|
||
cairo_set_dash(cairo_, &ddash, 1, 0); // dash size = line width
|
||
}
|
||
// rectangle in drawing units
|
||
int Xs = Fl_Scalable_Graphics_Driver::floor(x, s);
|
||
int Ws = Fl_Scalable_Graphics_Driver::floor(x+w-1, s) - Xs;
|
||
int Ys = Fl_Scalable_Graphics_Driver::floor(y, s);
|
||
int Hs = Fl_Scalable_Graphics_Driver::floor(y+h-1, s) - Ys;
|
||
cairo_move_to(cairo_, Xs, Ys);
|
||
cairo_line_to(cairo_, Xs+Ws, Ys);
|
||
cairo_line_to(cairo_, Xs+Ws, Ys+Hs);
|
||
cairo_line_to(cairo_, Xs, Ys+Hs);
|
||
cairo_close_path(cairo_);
|
||
cairo_stroke(cairo_);
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw_cached_pattern_(Fl_Image *img, cairo_pattern_t *pat, int X, int Y, int W, int H, int cx, int cy, int cache_w, int cache_h) {
|
||
// compute size of output image in drawing units
|
||
cairo_matrix_t matrix;
|
||
cairo_get_matrix(cairo_, &matrix);
|
||
float s = (float)matrix.xx;
|
||
int Xs = Fl_Scalable_Graphics_Driver::floor(X - cx, s);
|
||
int Ws = Fl_Scalable_Graphics_Driver::floor(X - cx + img->w(), s) - Xs ;
|
||
int Ys = Fl_Scalable_Graphics_Driver::floor(Y - cy, s);
|
||
int Hs = Fl_Scalable_Graphics_Driver::floor(Y - cy + img->h(), s) - Ys;
|
||
if (Ws == 0 || Hs == 0) return;
|
||
cairo_save(cairo_);
|
||
bool need_extend = (cache_w != Ws || cache_h != Hs || (W >= 2 && H >= 2));
|
||
if (need_extend || cx || cy || W < img->w() || H < img->h()) { // clip when necessary
|
||
cairo_rectangle(cairo_, X - 0.5, Y - 0.5, W + 0.5, H + 0.5);
|
||
cairo_clip(cairo_);
|
||
}
|
||
// remove any scaling and the current "0.5" translation useful for lines but bad for images
|
||
matrix.xx = matrix.yy = 1;
|
||
matrix.x0 -= 0.5 * s; matrix.y0 -= 0.5 * s;
|
||
cairo_set_matrix(cairo_, &matrix);
|
||
if (img->d() >= 1) cairo_set_source(cairo_, pat);
|
||
if (need_extend) {
|
||
bool condition = Fl_RGB_Image::scaling_algorithm() == FL_RGB_SCALING_BILINEAR &&
|
||
(fabs(Ws/float(cache_w) - 1) > 0.02 || fabs(Hs/float(cache_h) - 1) > 0.02);
|
||
cairo_pattern_set_filter(pat, condition ? CAIRO_FILTER_GOOD : CAIRO_FILTER_FAST);
|
||
cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
|
||
}
|
||
cairo_matrix_init_scale(&matrix, double(cache_w)/Ws, double(cache_h)/Hs);
|
||
cairo_matrix_translate(&matrix, -Xs , -Ys );
|
||
cairo_pattern_set_matrix(pat, &matrix);
|
||
if (img->d() > 1) cairo_paint(cairo_);
|
||
else cairo_mask(cairo_, pat);
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw_rgb(Fl_RGB_Image *rgb,int XP, int YP, int WP, int HP, int cx, int cy) {
|
||
int X, Y, W, H;
|
||
// Don't draw an empty image...
|
||
if (!rgb->d() || !rgb->array) {
|
||
Fl_Graphics_Driver::draw_empty(rgb, XP, YP);
|
||
return;
|
||
}
|
||
if (start_image(rgb, XP, YP, WP, HP, cx, cy, X, Y, W, H)) {
|
||
return;
|
||
}
|
||
cairo_pattern_t *pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(rgb);
|
||
if (!pat) {
|
||
cache(rgb);
|
||
pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(rgb);
|
||
}
|
||
draw_cached_pattern_(rgb, pat, X, Y, W, H, cx, cy, rgb->cache_w(), rgb->cache_h());
|
||
}
|
||
|
||
|
||
static cairo_user_data_key_t data_key_for_surface = {};
|
||
|
||
static void dealloc_surface_data(void *data) {
|
||
delete[] (uchar*)data;
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::cache(Fl_RGB_Image *rgb) {
|
||
int stride = cairo_format_stride_for_width(Fl_Cairo_Graphics_Driver::cairo_format, rgb->data_w());
|
||
uchar *BGRA = new uchar[stride * rgb->data_h()];
|
||
memset(BGRA, 0, stride * rgb->data_h());
|
||
int lrgb = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d();
|
||
uchar A = 0xff, R,G,B, *q;
|
||
const uchar *r;
|
||
float f = 1;
|
||
if (rgb->d() >= 3) { // color images
|
||
for (int j = 0; j < rgb->data_h(); j++) {
|
||
r = rgb->array + j * lrgb;
|
||
q = BGRA + j * stride;
|
||
for (int i = 0; i < rgb->data_w(); i++) {
|
||
R = *r;
|
||
G = *(r+1);
|
||
B = *(r+2);
|
||
if (rgb->d() == 4) {
|
||
A = *(r+3);
|
||
f = float(A)/0xff;
|
||
}
|
||
// this produces ARGB data in native endian
|
||
*(uint32_t *)q = A << 24 | (uchar)(R*f) << 16 | (uchar)(G*f) << 8 | (uchar)(B*f);
|
||
r += rgb->d(); q += 4;
|
||
}
|
||
}
|
||
} else if (rgb->d() == 1 || rgb->d() == 2) { // B&W
|
||
for (int j = 0; j < rgb->data_h(); j++) {
|
||
r = rgb->array + j * lrgb;
|
||
q = BGRA + j * stride;
|
||
for (int i = 0; i < rgb->data_w(); i++) {
|
||
G = *r;
|
||
if (rgb->d() == 2) {
|
||
A = *(r+1);
|
||
f = float(A)/0xff;
|
||
}
|
||
G = (uchar)(G * f);
|
||
// this produces ARGB data in native endian
|
||
*(uint32_t *)q = A << 24 | G << 16 | G << 8 | G;
|
||
r += rgb->d(); q += 4;
|
||
}
|
||
}
|
||
}
|
||
cairo_surface_t *surf = cairo_image_surface_create_for_data(BGRA, Fl_Cairo_Graphics_Driver::cairo_format, rgb->data_w(), rgb->data_h(), stride);
|
||
if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) return;
|
||
(void)cairo_surface_set_user_data(surf, &data_key_for_surface, BGRA, dealloc_surface_data);
|
||
cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf);
|
||
cairo_surface_destroy(surf);
|
||
*Fl_Graphics_Driver::id(rgb) = (fl_uintptr_t)pat;
|
||
int *pw, *ph;
|
||
cache_w_h(rgb, pw, ph);
|
||
*pw = rgb->data_w();
|
||
*ph = rgb->data_h();
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::uncache(Fl_RGB_Image *img, fl_uintptr_t &id_, fl_uintptr_t &mask_) {
|
||
cairo_pattern_t *pat = (cairo_pattern_t*)id_;
|
||
if (pat) {
|
||
cairo_pattern_destroy(pat);
|
||
id_ = 0;
|
||
}
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw_fixed(Fl_Bitmap *bm,int XP, int YP, int WP, int HP,
|
||
int cx, int cy) {
|
||
cairo_pattern_t *pat = NULL;
|
||
float s = wld_scale * scale();
|
||
XP = Fl_Scalable_Graphics_Driver::floor(XP, s);
|
||
YP = Fl_Scalable_Graphics_Driver::floor(YP, s);
|
||
cache_size(bm, WP, HP);
|
||
cx *= s; cy *= s;
|
||
cairo_matrix_t matrix; // temporarily remove scaling
|
||
cairo_get_matrix(cairo_, &matrix);
|
||
cairo_translate(cairo_, -0.5, -0.5);
|
||
cairo_scale(cairo_, 1./s, 1/s);
|
||
cairo_translate(cairo_, 0.5, 0.5);
|
||
if (!bm->array) {
|
||
draw_empty(bm, XP, YP);
|
||
} else {
|
||
pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(bm);
|
||
color(color());
|
||
int old_w = bm->w(), old_h = bm->h();
|
||
bm->scale(bm->cache_w(), bm->cache_h(), 0, 1); // transiently
|
||
draw_cached_pattern_(bm, pat, XP, YP, WP, HP, cx, cy, bm->cache_w(), bm->cache_h());
|
||
bm->scale(old_w, old_h, 0, 1); // back
|
||
}
|
||
cairo_set_matrix(cairo_, &matrix);
|
||
}
|
||
|
||
|
||
cairo_pattern_t *Fl_Cairo_Graphics_Driver::bitmap_to_pattern(Fl_Bitmap *bm,
|
||
bool complement, cairo_surface_t **p_surface) {
|
||
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_A1, bm->data_w());
|
||
int w_bitmap = ((bm->data_w() + 7) / 8);
|
||
uchar *BGRA = new uchar[stride * bm->data_h()];
|
||
memset(BGRA, 0, stride * bm->data_h());
|
||
for (int j = 0; j < bm->data_h(); j++) {
|
||
uchar *r = (uchar *)bm->array + j * w_bitmap;
|
||
uint32_t *q = (uint32_t *)(BGRA + j * stride);
|
||
int k = 0;
|
||
uint32_t mask32;
|
||
uchar p;
|
||
for (int i = 0; i < bm->data_w(); i++) {
|
||
if (k == 0) {
|
||
#if WORDS_BIGENDIAN
|
||
mask32 = (uint32_t)(1 << 31);
|
||
#else
|
||
mask32 = 1;
|
||
#endif
|
||
}
|
||
if ((k & 7) == 0) { // (k & 7) == (k % 8)
|
||
p = *r++;
|
||
if (complement)
|
||
p = ~p;
|
||
}
|
||
if (p & 1)
|
||
(*q) |= mask32;
|
||
k++;
|
||
p >>= 1;
|
||
#if WORDS_BIGENDIAN
|
||
mask32 >>= 1;
|
||
#else
|
||
mask32 <<= 1;
|
||
#endif
|
||
if (k == 32) {
|
||
k = 0;
|
||
q++;
|
||
}
|
||
}
|
||
}
|
||
cairo_surface_t *surf = cairo_image_surface_create_for_data(BGRA, CAIRO_FORMAT_A1, bm->data_w(), bm->data_h(), stride);
|
||
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(surf);
|
||
if (p_surface) *p_surface = surf;
|
||
else cairo_surface_destroy(surf);
|
||
return pattern;
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::cache(Fl_Bitmap *bm) {
|
||
cairo_surface_t *surf;
|
||
cairo_pattern_t *pattern = Fl_Cairo_Graphics_Driver::bitmap_to_pattern(bm, false, &surf);
|
||
uchar *BGRA = cairo_image_surface_get_data(surf);
|
||
(void)cairo_surface_set_user_data(surf, &data_key_for_surface, BGRA, dealloc_surface_data);
|
||
cairo_surface_destroy(surf);
|
||
*Fl_Graphics_Driver::id(bm) = (fl_uintptr_t)pattern;
|
||
int *pw, *ph;
|
||
cache_w_h(bm, pw, ph);
|
||
*pw = bm->data_w();
|
||
*ph = bm->data_h();
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw_fixed(Fl_Pixmap *pxm,int XP, int YP, int WP, int HP,
|
||
int cx, int cy) {
|
||
cairo_pattern_t *pat = NULL;
|
||
float s = wld_scale * scale();
|
||
XP = Fl_Scalable_Graphics_Driver::floor(XP, s);
|
||
YP = Fl_Scalable_Graphics_Driver::floor(YP, s);
|
||
cache_size(pxm, WP, HP);
|
||
cx *= s; cy *= s;
|
||
cairo_matrix_t matrix; // temporarily remove scaling
|
||
cairo_get_matrix(cairo_, &matrix);
|
||
cairo_translate(cairo_, -0.5, -0.5);
|
||
cairo_scale(cairo_, 1/s, 1/s);
|
||
cairo_translate(cairo_, 0.5, 0.5);
|
||
// Don't draw an empty image...
|
||
if (!pxm->data() || !pxm->w()) {
|
||
Fl_Graphics_Driver::draw_empty(pxm, XP, YP);
|
||
} else {
|
||
pat = (cairo_pattern_t*)*Fl_Graphics_Driver::id(pxm);
|
||
int old_w = pxm->w(), old_h = pxm->h();
|
||
pxm->scale(pxm->cache_w(), pxm->cache_h(), 0, 1); // transiently
|
||
draw_cached_pattern_(pxm, pat, XP, YP, WP, HP, cx, cy, pxm->cache_w(), pxm->cache_h());
|
||
pxm->scale(old_w, old_h, 0, 1); // back
|
||
}
|
||
cairo_set_matrix(cairo_, &matrix);
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::cache(Fl_Pixmap *pxm) {
|
||
Fl_RGB_Image *rgb = new Fl_RGB_Image(pxm);
|
||
cache(rgb);
|
||
*Fl_Graphics_Driver::id(pxm) = *Fl_Graphics_Driver::id(rgb);
|
||
*Fl_Graphics_Driver::id(rgb) = 0;
|
||
delete rgb;
|
||
int *pw, *ph;
|
||
cache_w_h(pxm, pw, ph);
|
||
*pw = pxm->data_w();
|
||
*ph = pxm->data_h();
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::uncache_pixmap(fl_uintptr_t p) {
|
||
if (p) cairo_pattern_destroy((cairo_pattern_t*)p);
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::delete_bitmask(fl_uintptr_t bm) {
|
||
if (bm) cairo_pattern_destroy((cairo_pattern_t*)bm);
|
||
}
|
||
|
||
|
||
int Fl_Cairo_Graphics_Driver::height() {
|
||
if (!font_descriptor()) font(0, 12);
|
||
return ceil(((Fl_Cairo_Font_Descriptor*)font_descriptor())->line_height / float(PANGO_SCALE));
|
||
}
|
||
|
||
|
||
int Fl_Cairo_Graphics_Driver::descent() {
|
||
return font_descriptor()->descent / float(PANGO_SCALE) + 0.5;
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::init_built_in_fonts() {
|
||
static int i = 0;
|
||
if (!i) {
|
||
while (i < FL_FREE_FONT) {
|
||
i++;
|
||
Fl::set_font((Fl_Font)i-1, fl_fonts[i-1].name);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
// FIXME: this (static) function should likely be in Fl_Graphics_Driver.
|
||
// The code is the same as in src/drivers/Xlib/Fl_Xlib_Graphics_Driver_font_xft.cxx
|
||
|
||
static int font_name_process(const char *name, char &face) {
|
||
int l = strlen(name);
|
||
face = ' ';
|
||
if (l > 8 && !memcmp(name + l - 8, " Regular", 8)) l -= 8;
|
||
else if (l > 6 && !memcmp(name + l - 6, " Plain", 6)) l -= 6;
|
||
else if (l > 12 && !memcmp(name + l - 12, " Bold Italic", 12)) {l -= 12; face = 'P';}
|
||
else if (l > 7 && !memcmp(name + l - 7, " Italic", 7)) {l -= 7; face = 'I';}
|
||
else if (l > 5 && !memcmp(name + l - 5, " Bold", 5)) {l -= 5; face = 'B';}
|
||
return l;
|
||
}
|
||
|
||
typedef int (*sort_f_type)(const void *aa, const void *bb);
|
||
|
||
|
||
static int font_sort(Fl_Fontdesc *fa, Fl_Fontdesc *fb) {
|
||
char face_a, face_b;
|
||
int la = font_name_process(fa->name, face_a);
|
||
int lb = font_name_process(fb->name, face_b);
|
||
int c = strncasecmp(fa->name, fb->name, la >= lb ? lb : la);
|
||
return (c == 0 ? face_a - face_b : c);
|
||
}
|
||
|
||
|
||
Fl_Font Fl_Cairo_Graphics_Driver::set_fonts(const char* /*pattern_name*/)
|
||
{
|
||
fl_open_display();
|
||
int n_families, count = 0;
|
||
PangoFontFamily **families;
|
||
char *saved_lang = fl_getenv("LANG");
|
||
const char *Clang = "LANG=C";
|
||
if (saved_lang && strcmp(saved_lang, Clang)) {
|
||
// Force LANG=C to prevent pango_font_face_get_face_name() below from returning
|
||
// translated versions of Bold, Italic, etc… (see issue #732).
|
||
// Unfortunately, using setlocale() doesn't do the job.
|
||
char *p = saved_lang;
|
||
saved_lang = (char*)malloc(strlen(p) + 6);
|
||
memcpy(saved_lang, "LANG=", 5);
|
||
strcpy(saved_lang + 5, p);
|
||
fl_putenv(Clang);
|
||
} else saved_lang = NULL;
|
||
static PangoFontMap *pfmap_ = pango_cairo_font_map_get_default(); // 1.10
|
||
Fl_Cairo_Graphics_Driver::init_built_in_fonts();
|
||
pango_font_map_list_families(pfmap_, &families, &n_families);
|
||
for (int fam = 0; fam < n_families; fam++) {
|
||
PangoFontFace **faces;
|
||
int n_faces;
|
||
const char *fam_name = pango_font_family_get_name (families[fam]);
|
||
int lfam = strlen(fam_name);
|
||
pango_font_family_list_faces(families[fam], &faces, &n_faces);
|
||
for (int j = 0; j < n_faces; j++) {
|
||
const char *p = pango_font_face_get_face_name(faces[j]);
|
||
// Remove " Regular" suffix from font names
|
||
if (!strcasecmp(p, "regular")) p = NULL;
|
||
// build the font's FLTK name
|
||
int lfont = lfam + (p ? strlen(p) + 2 : 1);
|
||
char *q = new char[lfont];
|
||
if (p) snprintf(q, lfont, "%s %s", fam_name, p);
|
||
else strcpy(q, fam_name);
|
||
Fl::set_font((Fl_Font)(count++ + FL_FREE_FONT), q);
|
||
}
|
||
/*g_*/free(faces); // glib source code shows that g_free is equivalent to free
|
||
}
|
||
/*g_*/free(families);
|
||
if (saved_lang) {
|
||
fl_putenv(saved_lang);
|
||
free(saved_lang);
|
||
}
|
||
// Sort the list into alphabetic order
|
||
qsort(fl_fonts + FL_FREE_FONT, count, sizeof(Fl_Fontdesc), (sort_f_type)font_sort);
|
||
return FL_FREE_FONT + count;
|
||
}
|
||
|
||
|
||
const char *Fl_Cairo_Graphics_Driver::font_name(int num) {
|
||
return fl_fonts[num].name;
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::font_name(int num, const char *name) {
|
||
Fl_Fontdesc *s = fl_fonts + num;
|
||
if (s->name) {
|
||
if (!strcmp(s->name, name)) {s->name = name; return;}
|
||
for (Fl_Font_Descriptor* f = s->first; f;) {
|
||
Fl_Font_Descriptor* n = f->next; delete f; f = n;
|
||
}
|
||
s->first = 0;
|
||
}
|
||
s->name = name;
|
||
s->fontname[0] = 0;
|
||
s->first = 0;
|
||
}
|
||
|
||
|
||
// turn a stored font name into a pretty name:
|
||
#define ENDOFBUFFER sizeof(fl_fonts->fontname)-1
|
||
|
||
const char* Fl_Cairo_Graphics_Driver::get_font_name(Fl_Font fnum, int* ap) {
|
||
Fl_Fontdesc *f = fl_fonts + fnum;
|
||
if (!f->fontname[0]) {
|
||
strcpy(f->fontname, f->name); // to check
|
||
const char* thisFont = f->name;
|
||
if (!thisFont || !*thisFont) {if (ap) *ap = 0; return "";}
|
||
int type = 0;
|
||
if (strstr(f->name, "Bold")) type |= FL_BOLD;
|
||
if (strstr(f->name, "Italic") || strstr(f->name, "Oblique")) type |= FL_ITALIC;
|
||
f->fontname[ENDOFBUFFER] = (char)type;
|
||
}
|
||
if (ap) *ap = f->fontname[ENDOFBUFFER];
|
||
return f->fontname;
|
||
}
|
||
|
||
|
||
int Fl_Cairo_Graphics_Driver::get_font_sizes(Fl_Font fnum, int*& sizep) {
|
||
static int array[128];
|
||
if (!fl_fonts) fl_fonts = calc_fl_fonts();
|
||
Fl_Fontdesc *s = fl_fonts+fnum;
|
||
if (!s->name) s = fl_fonts; // empty slot in table, use entry 0
|
||
int cnt = 0;
|
||
|
||
array[0] = 0;
|
||
sizep = array;
|
||
cnt = 1;
|
||
|
||
return cnt;
|
||
}
|
||
|
||
|
||
Fl_Cairo_Font_Descriptor::Fl_Cairo_Font_Descriptor(const char* name, Fl_Fontsize size,
|
||
PangoContext *context) :
|
||
Fl_Font_Descriptor(name, size) {
|
||
char *string = new char[strlen(name) + 10];
|
||
strcpy(string, name);
|
||
snprintf(string + strlen(string), 10, " %dpx", size);
|
||
//A PangoFontDescription describes a font in an implementation-independent manner.
|
||
fontref = pango_font_description_from_string(string);
|
||
delete[] string;
|
||
width = NULL;
|
||
//A PangoFontset represents a set of PangoFont to use when rendering text.
|
||
PangoFontset *fontset = pango_font_map_load_fontset(
|
||
pango_cairo_font_map_get_default(), // 1.10
|
||
context, fontref,
|
||
pango_language_get_default() // 1.16
|
||
);
|
||
PangoFontMetrics *metrics = pango_fontset_get_metrics(fontset);
|
||
ascent = pango_font_metrics_get_ascent(metrics);
|
||
descent = pango_font_metrics_get_descent(metrics);
|
||
/* Function pango_font_metrics_get_height() giving the line height of a pango font
|
||
appears with pango version 1.44. However, with pango version 1.48.10 and below,
|
||
this function gives values that make the underscore invisible on lowdpi display
|
||
and at 100% scaling (the underscore becomes visible starting at 120% scaling).
|
||
With pango version 1.50.6 (Ubuntu 22.04) this problem disappears.
|
||
Consequently, function pango_font_metrics_get_height() is not used until version 1.50.6
|
||
*/
|
||
//#if PANGO_VERSION_CHECK(1,44,0)
|
||
#if PANGO_VERSION_CHECK(1,50,6)
|
||
line_height = pango_font_metrics_get_height(metrics); // 1.44
|
||
#else
|
||
line_height = (pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics)) * 1.025 + 0.5;
|
||
#endif
|
||
pango_font_metrics_unref(metrics);
|
||
g_object_unref(fontset);
|
||
//fprintf(stderr, "[%s](%d) ascent=%d descent=%d line_height=%d\n", name, size, ascent, descent, line_height);
|
||
}
|
||
|
||
|
||
Fl_Cairo_Font_Descriptor::~Fl_Cairo_Font_Descriptor() {
|
||
pango_font_description_free(fontref);
|
||
if (width) {
|
||
for (int i = 0; i < 64; i++) delete[] width[i];
|
||
}
|
||
delete[] width;
|
||
}
|
||
|
||
|
||
static Fl_Font_Descriptor* find(Fl_Font fnum, Fl_Fontsize size, PangoContext *context) {
|
||
Fl_Fontdesc* s = fl_fonts+fnum;
|
||
if (!s->name) s = fl_fonts; // use 0 if fnum undefined
|
||
Fl_Font_Descriptor* f;
|
||
for (f = s->first; f; f = f->next)
|
||
if (f->size == size) return f;
|
||
f = new Fl_Cairo_Font_Descriptor(s->name, size, context);
|
||
f->next = s->first;
|
||
s->first = f;
|
||
return f;
|
||
}
|
||
|
||
|
||
/* Implementation note :
|
||
* The pixel width of a drawn string equals the sum of the widths of its
|
||
* characters, except when kerning occurs.
|
||
*/
|
||
void Fl_Cairo_Graphics_Driver::font(Fl_Font fnum, Fl_Fontsize s) {
|
||
if (!font_descriptor()) fl_open_display();
|
||
if (!cairo_) {
|
||
cairo_surface_t *surf = cairo_image_surface_create(Fl_Cairo_Graphics_Driver::cairo_format, 100, 100);
|
||
cairo_ = cairo_create(surf);
|
||
cairo_surface_destroy(surf);
|
||
dummy_cairo_ = cairo_;
|
||
}
|
||
if (s == 0) return;
|
||
if (fnum == -1) {
|
||
Fl_Graphics_Driver::font(0, 0);
|
||
return;
|
||
}
|
||
Fl_Graphics_Driver::font(fnum, s);
|
||
if (!pango_context_) {
|
||
//A PangoFontMap represents the set of fonts available for a particular rendering system.
|
||
PangoFontMap *def_font_map = pango_cairo_font_map_get_default(); // 1.10
|
||
//A PangoContext stores global information used to control the itemization process.
|
||
#if PANGO_VERSION_CHECK(1,22,0)
|
||
pango_context_ = pango_font_map_create_context(def_font_map); // 1.22
|
||
#else
|
||
pango_context_ = pango_context_new();
|
||
pango_context_set_font_map(pango_context_, def_font_map);
|
||
#endif
|
||
pango_layout_ = pango_layout_new(pango_context_);
|
||
}
|
||
font_descriptor( find(fnum, s, pango_context_) );
|
||
//If no font description is set on the layout, the font description from the layout’s context is used.
|
||
pango_context_set_font_description(pango_context_,
|
||
((Fl_Cairo_Font_Descriptor*)font_descriptor())->fontref);
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw(const char* str, int n, float x, float y) {
|
||
if (!n) return;
|
||
cairo_save(cairo_);
|
||
Fl_Cairo_Font_Descriptor *fd = (Fl_Cairo_Font_Descriptor*)font_descriptor();
|
||
cairo_translate(cairo_, x - 1, y - (fd->line_height - fd->descent) / float(PANGO_SCALE) - 1);
|
||
pango_layout_set_text(pango_layout_, str, n);
|
||
pango_cairo_show_layout(cairo_, pango_layout_); // 1.1O
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::draw(int rotation, const char *str, int n, int x, int y)
|
||
{
|
||
cairo_save(cairo_);
|
||
cairo_translate(cairo_, x, y);
|
||
cairo_rotate(cairo_, -rotation * M_PI / 180);
|
||
this->draw(str, n, 0, 0);
|
||
cairo_restore(cairo_);
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::rtl_draw(const char* str, int n, int x, int y) {
|
||
int w = (int)width(str, n);
|
||
draw(str, n, x - w, y);
|
||
}
|
||
|
||
|
||
// cache the widths of single Unicode characters
|
||
double Fl_Cairo_Graphics_Driver::width(unsigned int utf32) {
|
||
unsigned r=0;
|
||
Fl_Cairo_Font_Descriptor *desc = NULL;
|
||
if (utf32 <= 0xFFFF) {
|
||
desc = (Fl_Cairo_Font_Descriptor*)font_descriptor();
|
||
r = (utf32 & 0xFC00) >> 10;
|
||
if (!desc->width) {
|
||
desc->width = (int**)new int*[64];
|
||
memset(desc->width, 0, 64*sizeof(int*));
|
||
}
|
||
if (!desc->width[r]) {
|
||
desc->width[r] = (int*)new int[0x0400];
|
||
for (int i = 0; i < 0x0400; i++) desc->width[r][i] = -1;
|
||
} else {
|
||
if ( desc->width[r][utf32&0x03FF] >= 0 ) { // already cached
|
||
return desc->width[r][utf32 & 0x03FF] / double(PANGO_SCALE);
|
||
}
|
||
}
|
||
}
|
||
char buf4[4];
|
||
int n = fl_utf8encode(utf32, buf4);
|
||
int width = do_width_unscaled_(buf4, n);
|
||
if (utf32 <= 0xFFFF) {
|
||
desc->width[r][utf32 & 0x03FF] = width;
|
||
}
|
||
return width / double(PANGO_SCALE);
|
||
}
|
||
|
||
|
||
double Fl_Cairo_Graphics_Driver::width(const char* str, int n) {
|
||
if (!font_descriptor()) return -1;
|
||
if ((str == NULL) || (n == 0)) return 0.;
|
||
if (n == fl_utf8len(*str)) { // str contains a single unicode character
|
||
int l;
|
||
unsigned c = fl_utf8decode(str, str+n, &l);
|
||
return width(c); // that character's width may have been cached
|
||
}
|
||
return do_width_unscaled_(str, n) / double(PANGO_SCALE); // full width computation for multi-char strings
|
||
}
|
||
|
||
|
||
int Fl_Cairo_Graphics_Driver::do_width_unscaled_(const char* str, int n) {
|
||
if (!n) return 0;
|
||
pango_layout_set_text(pango_layout_, str, n);
|
||
PangoRectangle p_rect;
|
||
pango_layout_get_extents(pango_layout_, NULL, &p_rect);
|
||
return p_rect.width;
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::text_extents(const char* txt, int n, int& dx, int& dy, int& w, int& h) {
|
||
pango_layout_set_text(pango_layout_, txt, n);
|
||
PangoRectangle ink_rect;
|
||
pango_layout_get_extents(pango_layout_, &ink_rect, NULL);
|
||
double f = PANGO_SCALE;
|
||
Fl_Cairo_Font_Descriptor *fd = (Fl_Cairo_Font_Descriptor*)font_descriptor();
|
||
dx = ink_rect.x / f - 1;
|
||
dy = (ink_rect.y - fd->line_height + fd->descent) / f - 1;
|
||
w = ceil(ink_rect.width / f);
|
||
h = ceil(ink_rect.height / f);
|
||
}
|
||
|
||
//
|
||
// Region-handling member functions.
|
||
// They are used ONLY if the cairo graphics driver is the display graphics driver.
|
||
// They are not used if the cairo graphics driver is used to draw PostScript.
|
||
//
|
||
|
||
Fl_Region Fl_Cairo_Graphics_Driver::XRectangleRegion(int x, int y, int w, int h) {
|
||
struct flCairoRegion *R = (struct flCairoRegion*)malloc(sizeof(*R));
|
||
R->count = 1;
|
||
R->rects = (cairo_rectangle_t *)malloc(sizeof(cairo_rectangle_t));
|
||
R->rects->x=x, R->rects->y=y, R->rects->width=w; R->rects->height=h;
|
||
return (Fl_Region)R;
|
||
}
|
||
|
||
|
||
// r1 ⊂ r2
|
||
static bool CairoRectContainsRect(cairo_rectangle_t *r1, cairo_rectangle_t *r2) {
|
||
return r1->x >= r2->x && r1->y >= r2->y && r1->x+r1->width <= r2->x+r2->width &&
|
||
r1->y+r1->height <= r2->y+r2->height;
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::add_rectangle_to_region(Fl_Region r_, int X, int Y, int W, int H) {
|
||
struct flCairoRegion *r = (struct flCairoRegion*)r_;
|
||
cairo_rectangle_t arg = {double(X), double(Y), double(W), double(H)};
|
||
int j; // don't add a rectangle totally inside the Fl_Region
|
||
for (j = 0; j < r->count; j++) {
|
||
if (CairoRectContainsRect(&arg, &(r->rects[j]))) break;
|
||
}
|
||
if (j >= r->count) {
|
||
r->rects = (cairo_rectangle_t*)realloc(r->rects, (++(r->count)) * sizeof(cairo_rectangle_t));
|
||
r->rects[r->count - 1] = arg;
|
||
}
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::XDestroyRegion(Fl_Region r_) {
|
||
if (r_) {
|
||
struct flCairoRegion *r = (struct flCairoRegion*)r_;
|
||
free(r->rects);
|
||
free(r);
|
||
}
|
||
}
|
||
|
||
#define fl_max(a,b) ((a) > (b) ? (a) : (b))
|
||
#define fl_min(a,b) ((a) < (b) ? (a) : (b))
|
||
|
||
void Fl_Cairo_Graphics_Driver::restore_clip() {
|
||
if (cairo_) {
|
||
cairo_reset_clip(cairo_);
|
||
// apply what's in rstack
|
||
struct flCairoRegion *r = (struct flCairoRegion*)rstack[rstackptr];
|
||
if (r) {
|
||
if (!clip_) {
|
||
clip_ = new Clip();
|
||
clip_->prev = NULL;
|
||
}
|
||
for (int i = 0; i < r->count; i++) {
|
||
cairo_rectangle(cairo_, r->rects[i].x - 0.5, r->rects[i].y - 0.5, r->rects[i].width, r->rects[i].height);
|
||
// put in clip_ the bounding rect of region r
|
||
if (i == 0) {
|
||
clip_->x = r->rects[0].x; clip_->y = r->rects[0].y;
|
||
clip_->w = r->rects[0].width; clip_->h = r->rects[0].height;
|
||
} else {
|
||
int R = fl_max(r->rects[i].x + r->rects[i].width, clip_->x + clip_->w);
|
||
int B = fl_max(r->rects[i].y + r->rects[i].height, clip_->y + clip_->h);
|
||
clip_->x = fl_min(r->rects[i].x, clip_->x) ;
|
||
clip_->y = fl_min(r->rects[i].y, clip_->y);
|
||
clip_->w = R - clip_->x;
|
||
clip_->h = B - clip_->y;
|
||
}
|
||
}
|
||
cairo_clip(cairo_);
|
||
} else if (clip_) {
|
||
clip_->w = -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
char Fl_Cairo_Graphics_Driver::can_do_alpha_blending() {
|
||
return 1;
|
||
}
|
||
|
||
|
||
float Fl_Cairo_Graphics_Driver::override_scale() {
|
||
float s = scale();
|
||
if (s != 1.f && Fl_Display_Device::display_device()->is_current()) {
|
||
Fl::screen_driver()->scale(0, 1.f);
|
||
cairo_scale(cairo_, 1/s, 1/s);
|
||
}
|
||
return s;
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::restore_scale(float s) {
|
||
if (s != 1.f && Fl_Display_Device::display_device()->is_current()) {
|
||
Fl::screen_driver()->scale(0, s);
|
||
cairo_scale(cairo_, s, s);
|
||
}
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::antialias(int state) {
|
||
cairo_set_antialias(cairo_, state ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
|
||
}
|
||
|
||
|
||
int Fl_Cairo_Graphics_Driver::antialias() {
|
||
return (cairo_get_antialias(cairo_) != CAIRO_ANTIALIAS_NONE);
|
||
}
|
||
|
||
|
||
void Fl_Cairo_Graphics_Driver::focus_rect(int x, int y, int w, int h)
|
||
{
|
||
cairo_save(cairo_);
|
||
cairo_set_line_width(cairo_, 1);
|
||
cairo_set_line_cap(cairo_, CAIRO_LINE_CAP_BUTT);
|
||
cairo_set_line_join(cairo_, CAIRO_LINE_JOIN_MITER);
|
||
static double dots[2] = {1., 1.};
|
||
cairo_set_dash(cairo_, dots, 2, 1);
|
||
cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE);
|
||
cairo_rectangle(cairo_, x, y, w-1, h-1);
|
||
cairo_stroke(cairo_);
|
||
cairo_restore(cairo_);
|
||
surface_needs_commit();
|
||
}
|
||
|
||
#endif // USE_PANGO
|