mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice-common
synced 2026-01-08 13:07:17 +00:00
Move canvas_draw_stroke to canvas_base
This commit is contained in:
parent
82a7c42a86
commit
6522564e0d
455
cairo_canvas.c
455
cairo_canvas.c
@ -24,7 +24,6 @@
|
||||
#include "rop3.h"
|
||||
#include "rect.h"
|
||||
#include "region.h"
|
||||
#include "lines.h"
|
||||
#include "pixman_utils.h"
|
||||
|
||||
typedef struct CairoCanvas CairoCanvas;
|
||||
@ -837,459 +836,6 @@ static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
|
||||
pixman_region32_fini(&dest_region);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
lineGC base;
|
||||
pixman_image_t *dest;
|
||||
pixman_region32_t dest_region;
|
||||
SpiceROP fore_rop;
|
||||
SpiceROP back_rop;
|
||||
int solid;
|
||||
uint32_t color;
|
||||
pixman_image_t *tile;
|
||||
int tile_offset_x;
|
||||
int tile_offset_y;
|
||||
} StrokeGC;
|
||||
|
||||
static void stroke_fill_spans(lineGC * pGC,
|
||||
int num_spans,
|
||||
SpicePoint *points,
|
||||
int *widths,
|
||||
int sorted,
|
||||
int foreground)
|
||||
{
|
||||
StrokeGC *strokeGC;
|
||||
int i;
|
||||
pixman_image_t *dest;
|
||||
SpiceROP rop;
|
||||
|
||||
strokeGC = (StrokeGC *)pGC;
|
||||
dest = strokeGC->dest;
|
||||
|
||||
num_spans = spice_canvas_clip_spans(&strokeGC->dest_region,
|
||||
points, widths, num_spans,
|
||||
points, widths, sorted);
|
||||
|
||||
if (foreground) {
|
||||
rop = strokeGC->fore_rop;
|
||||
} else {
|
||||
rop = strokeGC->back_rop;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_spans; i++) {
|
||||
if (strokeGC->solid) {
|
||||
if (rop == SPICE_ROP_COPY) {
|
||||
spice_pixman_fill_rect(dest, points[i].x, points[i].y, widths[i], 1,
|
||||
strokeGC->color);
|
||||
} else {
|
||||
spice_pixman_fill_rect_rop(dest, points[i].x, points[i].y, widths[i], 1,
|
||||
strokeGC->color, rop);
|
||||
}
|
||||
} else {
|
||||
if (rop == SPICE_ROP_COPY) {
|
||||
spice_pixman_tile_rect(dest,
|
||||
points[i].x, points[i].y,
|
||||
widths[i], 1,
|
||||
strokeGC->tile,
|
||||
strokeGC->tile_offset_x,
|
||||
strokeGC->tile_offset_y);
|
||||
} else {
|
||||
spice_pixman_tile_rect_rop(dest,
|
||||
points[i].x, points[i].y,
|
||||
widths[i], 1,
|
||||
strokeGC->tile,
|
||||
strokeGC->tile_offset_x,
|
||||
strokeGC->tile_offset_y,
|
||||
rop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void stroke_fill_rects(lineGC * pGC,
|
||||
int num_rects,
|
||||
pixman_rectangle32_t *rects,
|
||||
int foreground)
|
||||
{
|
||||
pixman_region32_t area;
|
||||
pixman_box32_t *boxes;
|
||||
StrokeGC *strokeGC;
|
||||
pixman_image_t *dest;
|
||||
SpiceROP rop;
|
||||
int i;
|
||||
pixman_box32_t *area_rects;
|
||||
int n_area_rects;
|
||||
|
||||
strokeGC = (StrokeGC *)pGC;
|
||||
dest = strokeGC->dest;
|
||||
|
||||
if (foreground) {
|
||||
rop = strokeGC->fore_rop;
|
||||
} else {
|
||||
rop = strokeGC->back_rop;
|
||||
}
|
||||
|
||||
/* TODO: We can optimize this for more common cases where
|
||||
dest is one rect */
|
||||
|
||||
boxes = (pixman_box32_t *)malloc(num_rects * sizeof(pixman_box32_t));
|
||||
for (i = 0; i < num_rects; i++) {
|
||||
boxes[i].x1 = rects[i].x;
|
||||
boxes[i].y1 = rects[i].y;
|
||||
boxes[i].x2 = rects[i].x + rects[i].width;
|
||||
boxes[i].y2 = rects[i].y + rects[i].height;
|
||||
}
|
||||
pixman_region32_init_rects(&area, boxes, num_rects);
|
||||
pixman_region32_intersect(&area, &area, &strokeGC->dest_region);
|
||||
free(boxes);
|
||||
|
||||
area_rects = pixman_region32_rectangles(&area, &n_area_rects);
|
||||
|
||||
for (i = 0; i < n_area_rects; i++) {
|
||||
if (strokeGC->solid) {
|
||||
if (rop == SPICE_ROP_COPY) {
|
||||
spice_pixman_fill_rect(dest,
|
||||
area_rects[i].x1,
|
||||
area_rects[i].y1,
|
||||
area_rects[i].x2 - area_rects[i].x1,
|
||||
area_rects[i].y2 - area_rects[i].y1,
|
||||
strokeGC->color);
|
||||
} else {
|
||||
spice_pixman_fill_rect_rop(dest,
|
||||
area_rects[i].x1,
|
||||
area_rects[i].y1,
|
||||
area_rects[i].x2 - area_rects[i].x1,
|
||||
area_rects[i].y2 - area_rects[i].y1,
|
||||
strokeGC->color, rop);
|
||||
}
|
||||
} else {
|
||||
if (rop == SPICE_ROP_COPY) {
|
||||
spice_pixman_tile_rect(dest,
|
||||
area_rects[i].x1,
|
||||
area_rects[i].y1,
|
||||
area_rects[i].x2 - area_rects[i].x1,
|
||||
area_rects[i].y2 - area_rects[i].y1,
|
||||
strokeGC->tile,
|
||||
strokeGC->tile_offset_x,
|
||||
strokeGC->tile_offset_y);
|
||||
} else {
|
||||
spice_pixman_tile_rect_rop(dest,
|
||||
area_rects[i].x1,
|
||||
area_rects[i].y1,
|
||||
area_rects[i].x2 - area_rects[i].x1,
|
||||
area_rects[i].y2 - area_rects[i].y1,
|
||||
strokeGC->tile,
|
||||
strokeGC->tile_offset_x,
|
||||
strokeGC->tile_offset_y,
|
||||
rop);
|
||||
}
|
||||
}
|
||||
}
|
||||
pixman_region32_fini(&area);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
SpicePoint *points;
|
||||
int num_points;
|
||||
int size;
|
||||
} StrokeLines;
|
||||
|
||||
static void stroke_lines_init(StrokeLines *lines)
|
||||
{
|
||||
lines->points = (SpicePoint *)malloc(10*sizeof(SpicePoint));
|
||||
lines->size = 10;
|
||||
lines->num_points = 0;
|
||||
}
|
||||
|
||||
static void stroke_lines_free(StrokeLines *lines)
|
||||
{
|
||||
free(lines->points);
|
||||
}
|
||||
|
||||
static void stroke_lines_append(StrokeLines *lines,
|
||||
int x, int y)
|
||||
{
|
||||
if (lines->num_points == lines->size) {
|
||||
lines->size *= 2;
|
||||
lines->points = (SpicePoint *)realloc(lines->points,
|
||||
lines->size * sizeof(SpicePoint));
|
||||
}
|
||||
lines->points[lines->num_points].x = x;
|
||||
lines->points[lines->num_points].y = y;
|
||||
lines->num_points++;
|
||||
}
|
||||
|
||||
static void stroke_lines_append_fix(StrokeLines *lines,
|
||||
SpicePointFix *point)
|
||||
{
|
||||
stroke_lines_append(lines,
|
||||
fix_to_int(point->x),
|
||||
fix_to_int(point->y));
|
||||
}
|
||||
|
||||
static inline int64_t dot(SPICE_FIXED28_4 x1,
|
||||
SPICE_FIXED28_4 y1,
|
||||
SPICE_FIXED28_4 x2,
|
||||
SPICE_FIXED28_4 y2)
|
||||
{
|
||||
return (((int64_t)x1) *((int64_t)x2) +
|
||||
((int64_t)y1) *((int64_t)y2)) >> 4;
|
||||
}
|
||||
|
||||
static inline int64_t dot2(SPICE_FIXED28_4 x,
|
||||
SPICE_FIXED28_4 y)
|
||||
{
|
||||
return (((int64_t)x) *((int64_t)x) +
|
||||
((int64_t)y) *((int64_t)y)) >> 4;
|
||||
}
|
||||
|
||||
static void subdivide_bezier(StrokeLines *lines,
|
||||
SpicePointFix point0,
|
||||
SpicePointFix point1,
|
||||
SpicePointFix point2,
|
||||
SpicePointFix point3)
|
||||
{
|
||||
int64_t A2, B2, C2, AB, CB, h1, h2;
|
||||
|
||||
A2 = dot2(point1.x - point0.x,
|
||||
point1.y - point0.y);
|
||||
B2 = dot2(point3.x - point0.x,
|
||||
point3.y - point0.y);
|
||||
C2 = dot2(point2.x - point3.x,
|
||||
point2.y - point3.y);
|
||||
|
||||
AB = dot(point1.x - point0.x,
|
||||
point1.y - point0.y,
|
||||
point3.x - point0.x,
|
||||
point3.y - point0.y);
|
||||
|
||||
CB = dot(point2.x - point3.x,
|
||||
point2.y - point3.y,
|
||||
point0.x - point3.x,
|
||||
point0.y - point3.y);
|
||||
|
||||
h1 = (A2*B2 - AB*AB) >> 3;
|
||||
h2 = (C2*B2 - CB*CB) >> 3;
|
||||
|
||||
if (h1 < B2 && h2 < B2) {
|
||||
/* deviation squared less than half a pixel, use straight line */
|
||||
stroke_lines_append_fix(lines, &point3);
|
||||
} else {
|
||||
SpicePointFix point01, point23, point12, point012, point123, point0123;
|
||||
|
||||
point01.x = (point0.x + point1.x) / 2;
|
||||
point01.y = (point0.y + point1.y) / 2;
|
||||
point12.x = (point1.x + point2.x) / 2;
|
||||
point12.y = (point1.y + point2.y) / 2;
|
||||
point23.x = (point2.x + point3.x) / 2;
|
||||
point23.y = (point2.y + point3.y) / 2;
|
||||
point012.x = (point01.x + point12.x) / 2;
|
||||
point012.y = (point01.y + point12.y) / 2;
|
||||
point123.x = (point12.x + point23.x) / 2;
|
||||
point123.y = (point12.y + point23.y) / 2;
|
||||
point0123.x = (point012.x + point123.x) / 2;
|
||||
point0123.y = (point012.y + point123.y) / 2;
|
||||
|
||||
subdivide_bezier(lines, point0, point01, point012, point0123);
|
||||
subdivide_bezier(lines, point0123, point123, point23, point3);
|
||||
}
|
||||
}
|
||||
|
||||
static void stroke_lines_append_bezier(StrokeLines *lines,
|
||||
SpicePointFix *point1,
|
||||
SpicePointFix *point2,
|
||||
SpicePointFix *point3)
|
||||
{
|
||||
SpicePointFix point0;
|
||||
|
||||
point0.x = int_to_fix(lines->points[lines->num_points-1].x);
|
||||
point0.y = int_to_fix(lines->points[lines->num_points-1].y);
|
||||
|
||||
subdivide_bezier(lines, point0, *point1, *point2, *point3);
|
||||
}
|
||||
|
||||
static void stroke_lines_draw(StrokeLines *lines,
|
||||
lineGC *gc,
|
||||
int dashed)
|
||||
{
|
||||
if (lines->num_points != 0) {
|
||||
if (dashed) {
|
||||
spice_canvas_zero_dash_line(gc, CoordModeOrigin,
|
||||
lines->num_points, lines->points);
|
||||
} else {
|
||||
spice_canvas_zero_line(gc, CoordModeOrigin,
|
||||
lines->num_points, lines->points);
|
||||
}
|
||||
lines->num_points = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
|
||||
{
|
||||
CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
|
||||
StrokeGC gc = { {0} };
|
||||
lineGCOps ops = {
|
||||
stroke_fill_spans,
|
||||
stroke_fill_rects
|
||||
};
|
||||
uint32_t *data_size;
|
||||
uint32_t more;
|
||||
SpicePathSeg *seg;
|
||||
StrokeLines lines;
|
||||
int i;
|
||||
int dashed;
|
||||
|
||||
pixman_region32_init_rect(&gc.dest_region,
|
||||
bbox->left, bbox->top,
|
||||
bbox->right - bbox->left,
|
||||
bbox->bottom - bbox->top);
|
||||
|
||||
canvas_clip_pixman(&canvas->base, &gc.dest_region, clip);
|
||||
|
||||
if (pixman_region32_n_rects(&gc.dest_region) == 0) {
|
||||
touch_brush(&canvas->base, &stroke->brush);
|
||||
pixman_region32_fini(&gc.dest_region);
|
||||
return;
|
||||
}
|
||||
|
||||
gc.fore_rop = ropd_descriptor_to_rop(stroke->fore_mode,
|
||||
ROP_INPUT_BRUSH,
|
||||
ROP_INPUT_DEST);
|
||||
gc.back_rop = ropd_descriptor_to_rop(stroke->back_mode,
|
||||
ROP_INPUT_BRUSH,
|
||||
ROP_INPUT_DEST);
|
||||
|
||||
gc.dest = canvas->image;
|
||||
gc.base.width = pixman_image_get_width(gc.dest);
|
||||
gc.base.height = pixman_image_get_height(gc.dest);
|
||||
gc.base.alu = gc.fore_rop;
|
||||
gc.base.lineWidth = 0;
|
||||
|
||||
/* dash */
|
||||
gc.base.dashOffset = 0;
|
||||
gc.base.dash = NULL;
|
||||
gc.base.numInDashList = 0;
|
||||
gc.base.lineStyle = LineSolid;
|
||||
/* win32 cosmetic lines are endpoint-exclusive, so use CapNotLast */
|
||||
gc.base.capStyle = CapNotLast;
|
||||
gc.base.joinStyle = JoinMiter;
|
||||
gc.base.ops = &ops;
|
||||
|
||||
dashed = 0;
|
||||
if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
|
||||
SPICE_FIXED28_4 *style = (SPICE_FIXED28_4*)SPICE_GET_ADDRESS(stroke->attr.style);
|
||||
int nseg;
|
||||
|
||||
dashed = 1;
|
||||
|
||||
nseg = stroke->attr.style_nseg;
|
||||
|
||||
/* To truly handle back_mode we should use LineDoubleDash here
|
||||
and treat !foreground as back_rop using the same brush.
|
||||
However, using the same brush for that seems wrong.
|
||||
The old cairo backend was stroking the non-dashed line with
|
||||
rop_mode before enabling dashes for the foreground which is
|
||||
not right either. The gl an gdi backend don't use back_mode
|
||||
at all */
|
||||
gc.base.lineStyle = LineOnOffDash;
|
||||
gc.base.dash = (unsigned char *)malloc(nseg);
|
||||
gc.base.numInDashList = nseg;
|
||||
access_test(&canvas->base, style, nseg * sizeof(*style));
|
||||
|
||||
if (stroke->attr.flags & SPICE_LINE_FLAGS_START_WITH_GAP) {
|
||||
gc.base.dash[stroke->attr.style_nseg - 1] = fix_to_int(style[0]);
|
||||
for (i = 0; i < stroke->attr.style_nseg - 1; i++) {
|
||||
gc.base.dash[i] = fix_to_int(style[i+1]);
|
||||
}
|
||||
gc.base.dashOffset = gc.base.dash[0];
|
||||
} else {
|
||||
for (i = 0; i < stroke->attr.style_nseg; i++) {
|
||||
gc.base.dash[i] = fix_to_int(style[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (stroke->brush.type) {
|
||||
case SPICE_BRUSH_TYPE_NONE:
|
||||
gc.solid = TRUE;
|
||||
gc.color = 0;
|
||||
break;
|
||||
case SPICE_BRUSH_TYPE_SOLID:
|
||||
gc.solid = TRUE;
|
||||
gc.color = stroke->brush.u.color;
|
||||
break;
|
||||
case SPICE_BRUSH_TYPE_PATTERN:
|
||||
gc.solid = FALSE;
|
||||
gc.tile = canvas_get_image(&canvas->base,
|
||||
stroke->brush.u.pattern.pat);
|
||||
gc.tile_offset_x = stroke->brush.u.pattern.pos.x;
|
||||
gc.tile_offset_y = stroke->brush.u.pattern.pos.y;
|
||||
break;
|
||||
default:
|
||||
CANVAS_ERROR("invalid brush type");
|
||||
}
|
||||
|
||||
data_size = (uint32_t*)SPICE_GET_ADDRESS(stroke->path);
|
||||
access_test(&canvas->base, data_size, sizeof(uint32_t));
|
||||
more = *data_size;
|
||||
seg = (SpicePathSeg*)(data_size + 1);
|
||||
|
||||
stroke_lines_init(&lines);
|
||||
|
||||
do {
|
||||
access_test(&canvas->base, seg, sizeof(SpicePathSeg));
|
||||
|
||||
uint32_t flags = seg->flags;
|
||||
SpicePointFix* point = (SpicePointFix*)seg->data;
|
||||
SpicePointFix* end_point = point + seg->count;
|
||||
access_test(&canvas->base, point, (unsigned long)end_point - (unsigned long)point);
|
||||
ASSERT(point < end_point);
|
||||
more -= ((unsigned long)end_point - (unsigned long)seg);
|
||||
seg = (SpicePathSeg*)end_point;
|
||||
|
||||
if (flags & SPICE_PATH_BEGIN) {
|
||||
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
||||
stroke_lines_append_fix(&lines, point);
|
||||
point++;
|
||||
}
|
||||
|
||||
if (flags & SPICE_PATH_BEZIER) {
|
||||
ASSERT((point - end_point) % 3 == 0);
|
||||
for (; point + 2 < end_point; point += 3) {
|
||||
stroke_lines_append_bezier(&lines,
|
||||
&point[0],
|
||||
&point[1],
|
||||
&point[2]);
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (; point < end_point; point++) {
|
||||
stroke_lines_append_fix(&lines, point);
|
||||
}
|
||||
}
|
||||
if (flags & SPICE_PATH_END) {
|
||||
if (flags & SPICE_PATH_CLOSE) {
|
||||
stroke_lines_append(&lines,
|
||||
lines.points[0].x, lines.points[0].y);
|
||||
}
|
||||
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
||||
}
|
||||
} while (more);
|
||||
|
||||
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
||||
|
||||
if (gc.base.dash) {
|
||||
free(gc.base.dash);
|
||||
}
|
||||
stroke_lines_free(&lines);
|
||||
|
||||
if (!gc.solid && gc.tile) {
|
||||
pixman_image_unref(gc.tile);
|
||||
}
|
||||
|
||||
pixman_region32_fini(&gc.dest_region);
|
||||
}
|
||||
|
||||
static void canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
|
||||
{
|
||||
CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
|
||||
@ -1397,7 +943,6 @@ void cairo_canvas_init() //unsafe global function
|
||||
|
||||
canvas_base_init_ops(&cairo_canvas_ops);
|
||||
cairo_canvas_ops.draw_text = canvas_draw_text;
|
||||
cairo_canvas_ops.draw_stroke = canvas_draw_stroke;
|
||||
cairo_canvas_ops.draw_rop3 = canvas_draw_rop3;
|
||||
cairo_canvas_ops.put_image = canvas_put_image;
|
||||
cairo_canvas_ops.clear = canvas_clear;
|
||||
|
||||
450
canvas_base.c
450
canvas_base.c
@ -29,6 +29,7 @@
|
||||
#include "pixman_utils.h"
|
||||
#include "canvas_utils.h"
|
||||
#include "rect.h"
|
||||
#include "lines.h"
|
||||
|
||||
#include "mutex.h"
|
||||
|
||||
@ -2355,6 +2356,454 @@ static void canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spice
|
||||
pixman_region32_fini(&dest_region);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
lineGC base;
|
||||
SpiceCanvas *canvas;
|
||||
pixman_region32_t dest_region;
|
||||
SpiceROP fore_rop;
|
||||
SpiceROP back_rop;
|
||||
int solid;
|
||||
uint32_t color;
|
||||
pixman_image_t *tile;
|
||||
int tile_offset_x;
|
||||
int tile_offset_y;
|
||||
} StrokeGC;
|
||||
|
||||
static void stroke_fill_spans(lineGC * pGC,
|
||||
int num_spans,
|
||||
SpicePoint *points,
|
||||
int *widths,
|
||||
int sorted,
|
||||
int foreground)
|
||||
{
|
||||
SpiceCanvas *canvas;
|
||||
StrokeGC *strokeGC;
|
||||
int i;
|
||||
SpiceROP rop;
|
||||
|
||||
strokeGC = (StrokeGC *)pGC;
|
||||
canvas = strokeGC->canvas;
|
||||
|
||||
num_spans = spice_canvas_clip_spans(&strokeGC->dest_region,
|
||||
points, widths, num_spans,
|
||||
points, widths, sorted);
|
||||
|
||||
if (foreground) {
|
||||
rop = strokeGC->fore_rop;
|
||||
} else {
|
||||
rop = strokeGC->back_rop;
|
||||
}
|
||||
|
||||
if (strokeGC->solid) {
|
||||
if (rop == SPICE_ROP_COPY) {
|
||||
canvas->ops->fill_solid_spans(canvas, points, widths, num_spans,
|
||||
strokeGC->color);
|
||||
} else {
|
||||
for (i = 0; i < num_spans; i++) {
|
||||
pixman_box32_t r;
|
||||
r.x1 = points[i].x;
|
||||
r.y1 = points[i].y;
|
||||
r.x2 = points[i].x + widths[i];
|
||||
canvas->ops->fill_solid_rects_rop(canvas, &r, 1,
|
||||
strokeGC->color, rop);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rop == SPICE_ROP_COPY) {
|
||||
for (i = 0; i < num_spans; i++) {
|
||||
pixman_box32_t r;
|
||||
r.x1 = points[i].x;
|
||||
r.y1 = points[i].y;
|
||||
r.x2 = points[i].x + widths[i];
|
||||
canvas->ops->fill_tiled_rects(canvas, &r, 1,
|
||||
strokeGC->tile,
|
||||
strokeGC->tile_offset_x,
|
||||
strokeGC->tile_offset_y);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < num_spans; i++) {
|
||||
pixman_box32_t r;
|
||||
r.x1 = points[i].x;
|
||||
r.y1 = points[i].y;
|
||||
r.x2 = points[i].x + widths[i];
|
||||
canvas->ops->fill_tiled_rects_rop(canvas, &r, 1,
|
||||
strokeGC->tile,
|
||||
strokeGC->tile_offset_x,
|
||||
strokeGC->tile_offset_y, rop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void stroke_fill_rects(lineGC * pGC,
|
||||
int num_rects,
|
||||
pixman_rectangle32_t *rects,
|
||||
int foreground)
|
||||
{
|
||||
SpiceCanvas *canvas;
|
||||
pixman_region32_t area;
|
||||
pixman_box32_t *boxes;
|
||||
StrokeGC *strokeGC;
|
||||
SpiceROP rop;
|
||||
int i;
|
||||
pixman_box32_t *area_rects;
|
||||
int n_area_rects;
|
||||
|
||||
strokeGC = (StrokeGC *)pGC;
|
||||
canvas = strokeGC->canvas;
|
||||
|
||||
if (foreground) {
|
||||
rop = strokeGC->fore_rop;
|
||||
} else {
|
||||
rop = strokeGC->back_rop;
|
||||
}
|
||||
|
||||
/* TODO: We can optimize this for more common cases where
|
||||
dest is one rect */
|
||||
|
||||
boxes = (pixman_box32_t *)malloc(num_rects * sizeof(pixman_box32_t));
|
||||
for (i = 0; i < num_rects; i++) {
|
||||
boxes[i].x1 = rects[i].x;
|
||||
boxes[i].y1 = rects[i].y;
|
||||
boxes[i].x2 = rects[i].x + rects[i].width;
|
||||
boxes[i].y2 = rects[i].y + rects[i].height;
|
||||
}
|
||||
pixman_region32_init_rects(&area, boxes, num_rects);
|
||||
pixman_region32_intersect(&area, &area, &strokeGC->dest_region);
|
||||
free(boxes);
|
||||
|
||||
area_rects = pixman_region32_rectangles(&area, &n_area_rects);
|
||||
|
||||
if (strokeGC->solid) {
|
||||
if (rop == SPICE_ROP_COPY) {
|
||||
canvas->ops->fill_solid_rects(canvas, area_rects, n_area_rects,
|
||||
strokeGC->color);
|
||||
} else {
|
||||
canvas->ops->fill_solid_rects_rop(canvas, area_rects, n_area_rects,
|
||||
strokeGC->color, rop);
|
||||
}
|
||||
} else {
|
||||
if (rop == SPICE_ROP_COPY) {
|
||||
canvas->ops->fill_tiled_rects(canvas, area_rects, n_area_rects,
|
||||
strokeGC->tile,
|
||||
strokeGC->tile_offset_x,
|
||||
strokeGC->tile_offset_y);
|
||||
} else {
|
||||
canvas->ops->fill_tiled_rects_rop(canvas, area_rects, n_area_rects,
|
||||
strokeGC->tile,
|
||||
strokeGC->tile_offset_x,
|
||||
strokeGC->tile_offset_y,
|
||||
rop);
|
||||
}
|
||||
}
|
||||
|
||||
pixman_region32_fini(&area);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
SpicePoint *points;
|
||||
int num_points;
|
||||
int size;
|
||||
} StrokeLines;
|
||||
|
||||
static void stroke_lines_init(StrokeLines *lines)
|
||||
{
|
||||
lines->points = (SpicePoint *)malloc(10*sizeof(SpicePoint));
|
||||
lines->size = 10;
|
||||
lines->num_points = 0;
|
||||
}
|
||||
|
||||
static void stroke_lines_free(StrokeLines *lines)
|
||||
{
|
||||
free(lines->points);
|
||||
}
|
||||
|
||||
static void stroke_lines_append(StrokeLines *lines,
|
||||
int x, int y)
|
||||
{
|
||||
if (lines->num_points == lines->size) {
|
||||
lines->size *= 2;
|
||||
lines->points = (SpicePoint *)realloc(lines->points,
|
||||
lines->size * sizeof(SpicePoint));
|
||||
}
|
||||
lines->points[lines->num_points].x = x;
|
||||
lines->points[lines->num_points].y = y;
|
||||
lines->num_points++;
|
||||
}
|
||||
|
||||
static void stroke_lines_append_fix(StrokeLines *lines,
|
||||
SpicePointFix *point)
|
||||
{
|
||||
stroke_lines_append(lines,
|
||||
fix_to_int(point->x),
|
||||
fix_to_int(point->y));
|
||||
}
|
||||
|
||||
static inline int64_t dot(SPICE_FIXED28_4 x1,
|
||||
SPICE_FIXED28_4 y1,
|
||||
SPICE_FIXED28_4 x2,
|
||||
SPICE_FIXED28_4 y2)
|
||||
{
|
||||
return (((int64_t)x1) *((int64_t)x2) +
|
||||
((int64_t)y1) *((int64_t)y2)) >> 4;
|
||||
}
|
||||
|
||||
static inline int64_t dot2(SPICE_FIXED28_4 x,
|
||||
SPICE_FIXED28_4 y)
|
||||
{
|
||||
return (((int64_t)x) *((int64_t)x) +
|
||||
((int64_t)y) *((int64_t)y)) >> 4;
|
||||
}
|
||||
|
||||
static void subdivide_bezier(StrokeLines *lines,
|
||||
SpicePointFix point0,
|
||||
SpicePointFix point1,
|
||||
SpicePointFix point2,
|
||||
SpicePointFix point3)
|
||||
{
|
||||
int64_t A2, B2, C2, AB, CB, h1, h2;
|
||||
|
||||
A2 = dot2(point1.x - point0.x,
|
||||
point1.y - point0.y);
|
||||
B2 = dot2(point3.x - point0.x,
|
||||
point3.y - point0.y);
|
||||
C2 = dot2(point2.x - point3.x,
|
||||
point2.y - point3.y);
|
||||
|
||||
AB = dot(point1.x - point0.x,
|
||||
point1.y - point0.y,
|
||||
point3.x - point0.x,
|
||||
point3.y - point0.y);
|
||||
|
||||
CB = dot(point2.x - point3.x,
|
||||
point2.y - point3.y,
|
||||
point0.x - point3.x,
|
||||
point0.y - point3.y);
|
||||
|
||||
h1 = (A2*B2 - AB*AB) >> 3;
|
||||
h2 = (C2*B2 - CB*CB) >> 3;
|
||||
|
||||
if (h1 < B2 && h2 < B2) {
|
||||
/* deviation squared less than half a pixel, use straight line */
|
||||
stroke_lines_append_fix(lines, &point3);
|
||||
} else {
|
||||
SpicePointFix point01, point23, point12, point012, point123, point0123;
|
||||
|
||||
point01.x = (point0.x + point1.x) / 2;
|
||||
point01.y = (point0.y + point1.y) / 2;
|
||||
point12.x = (point1.x + point2.x) / 2;
|
||||
point12.y = (point1.y + point2.y) / 2;
|
||||
point23.x = (point2.x + point3.x) / 2;
|
||||
point23.y = (point2.y + point3.y) / 2;
|
||||
point012.x = (point01.x + point12.x) / 2;
|
||||
point012.y = (point01.y + point12.y) / 2;
|
||||
point123.x = (point12.x + point23.x) / 2;
|
||||
point123.y = (point12.y + point23.y) / 2;
|
||||
point0123.x = (point012.x + point123.x) / 2;
|
||||
point0123.y = (point012.y + point123.y) / 2;
|
||||
|
||||
subdivide_bezier(lines, point0, point01, point012, point0123);
|
||||
subdivide_bezier(lines, point0123, point123, point23, point3);
|
||||
}
|
||||
}
|
||||
|
||||
static void stroke_lines_append_bezier(StrokeLines *lines,
|
||||
SpicePointFix *point1,
|
||||
SpicePointFix *point2,
|
||||
SpicePointFix *point3)
|
||||
{
|
||||
SpicePointFix point0;
|
||||
|
||||
point0.x = int_to_fix(lines->points[lines->num_points-1].x);
|
||||
point0.y = int_to_fix(lines->points[lines->num_points-1].y);
|
||||
|
||||
subdivide_bezier(lines, point0, *point1, *point2, *point3);
|
||||
}
|
||||
|
||||
static void stroke_lines_draw(StrokeLines *lines,
|
||||
lineGC *gc,
|
||||
int dashed)
|
||||
{
|
||||
if (lines->num_points != 0) {
|
||||
if (dashed) {
|
||||
spice_canvas_zero_dash_line(gc, CoordModeOrigin,
|
||||
lines->num_points, lines->points);
|
||||
} else {
|
||||
spice_canvas_zero_line(gc, CoordModeOrigin,
|
||||
lines->num_points, lines->points);
|
||||
}
|
||||
lines->num_points = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
|
||||
SpiceClip *clip, SpiceStroke *stroke)
|
||||
{
|
||||
CanvasBase *canvas = (CanvasBase *)spice_canvas;
|
||||
StrokeGC gc = { { 0 } };
|
||||
lineGCOps ops = {
|
||||
stroke_fill_spans,
|
||||
stroke_fill_rects
|
||||
};
|
||||
uint32_t *data_size;
|
||||
uint32_t more;
|
||||
SpicePathSeg *seg;
|
||||
StrokeLines lines;
|
||||
int i;
|
||||
int dashed;
|
||||
|
||||
pixman_region32_init_rect(&gc.dest_region,
|
||||
bbox->left, bbox->top,
|
||||
bbox->right - bbox->left,
|
||||
bbox->bottom - bbox->top);
|
||||
|
||||
canvas_clip_pixman(canvas, &gc.dest_region, clip);
|
||||
|
||||
if (pixman_region32_n_rects(&gc.dest_region) == 0) {
|
||||
touch_brush(canvas, &stroke->brush);
|
||||
pixman_region32_fini(&gc.dest_region);
|
||||
return;
|
||||
}
|
||||
|
||||
gc.canvas = spice_canvas;
|
||||
gc.fore_rop = ropd_descriptor_to_rop(stroke->fore_mode,
|
||||
ROP_INPUT_BRUSH,
|
||||
ROP_INPUT_DEST);
|
||||
gc.back_rop = ropd_descriptor_to_rop(stroke->back_mode,
|
||||
ROP_INPUT_BRUSH,
|
||||
ROP_INPUT_DEST);
|
||||
|
||||
gc.base.width = canvas->width;
|
||||
gc.base.height = canvas->height;
|
||||
gc.base.alu = gc.fore_rop;
|
||||
gc.base.lineWidth = 0;
|
||||
|
||||
/* dash */
|
||||
gc.base.dashOffset = 0;
|
||||
gc.base.dash = NULL;
|
||||
gc.base.numInDashList = 0;
|
||||
gc.base.lineStyle = LineSolid;
|
||||
/* win32 cosmetic lines are endpoint-exclusive, so use CapNotLast */
|
||||
gc.base.capStyle = CapNotLast;
|
||||
gc.base.joinStyle = JoinMiter;
|
||||
gc.base.ops = &ops;
|
||||
|
||||
dashed = 0;
|
||||
if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
|
||||
SPICE_FIXED28_4 *style = (SPICE_FIXED28_4*)SPICE_GET_ADDRESS(stroke->attr.style);
|
||||
int nseg;
|
||||
|
||||
dashed = 1;
|
||||
|
||||
nseg = stroke->attr.style_nseg;
|
||||
|
||||
/* To truly handle back_mode we should use LineDoubleDash here
|
||||
and treat !foreground as back_rop using the same brush.
|
||||
However, using the same brush for that seems wrong.
|
||||
The old cairo backend was stroking the non-dashed line with
|
||||
rop_mode before enabling dashes for the foreground which is
|
||||
not right either. The gl an gdi backend don't use back_mode
|
||||
at all */
|
||||
gc.base.lineStyle = LineOnOffDash;
|
||||
gc.base.dash = (unsigned char *)malloc(nseg);
|
||||
gc.base.numInDashList = nseg;
|
||||
access_test(canvas, style, nseg * sizeof(*style));
|
||||
|
||||
if (stroke->attr.flags & SPICE_LINE_FLAGS_START_WITH_GAP) {
|
||||
gc.base.dash[stroke->attr.style_nseg - 1] = fix_to_int(style[0]);
|
||||
for (i = 0; i < stroke->attr.style_nseg - 1; i++) {
|
||||
gc.base.dash[i] = fix_to_int(style[i+1]);
|
||||
}
|
||||
gc.base.dashOffset = gc.base.dash[0];
|
||||
} else {
|
||||
for (i = 0; i < stroke->attr.style_nseg; i++) {
|
||||
gc.base.dash[i] = fix_to_int(style[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (stroke->brush.type) {
|
||||
case SPICE_BRUSH_TYPE_NONE:
|
||||
gc.solid = TRUE;
|
||||
gc.color = 0;
|
||||
break;
|
||||
case SPICE_BRUSH_TYPE_SOLID:
|
||||
gc.solid = TRUE;
|
||||
gc.color = stroke->brush.u.color;
|
||||
break;
|
||||
case SPICE_BRUSH_TYPE_PATTERN:
|
||||
gc.solid = FALSE;
|
||||
gc.tile = canvas_get_image(canvas,
|
||||
stroke->brush.u.pattern.pat);
|
||||
gc.tile_offset_x = stroke->brush.u.pattern.pos.x;
|
||||
gc.tile_offset_y = stroke->brush.u.pattern.pos.y;
|
||||
break;
|
||||
default:
|
||||
CANVAS_ERROR("invalid brush type");
|
||||
}
|
||||
|
||||
data_size = (uint32_t*)SPICE_GET_ADDRESS(stroke->path);
|
||||
access_test(canvas, data_size, sizeof(uint32_t));
|
||||
more = *data_size;
|
||||
seg = (SpicePathSeg*)(data_size + 1);
|
||||
|
||||
stroke_lines_init(&lines);
|
||||
|
||||
do {
|
||||
access_test(canvas, seg, sizeof(SpicePathSeg));
|
||||
|
||||
uint32_t flags = seg->flags;
|
||||
SpicePointFix* point = (SpicePointFix*)seg->data;
|
||||
SpicePointFix* end_point = point + seg->count;
|
||||
access_test(canvas, point, (unsigned long)end_point - (unsigned long)point);
|
||||
ASSERT(point < end_point);
|
||||
more -= ((unsigned long)end_point - (unsigned long)seg);
|
||||
seg = (SpicePathSeg*)end_point;
|
||||
|
||||
if (flags & SPICE_PATH_BEGIN) {
|
||||
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
||||
stroke_lines_append_fix(&lines, point);
|
||||
point++;
|
||||
}
|
||||
|
||||
if (flags & SPICE_PATH_BEZIER) {
|
||||
ASSERT((point - end_point) % 3 == 0);
|
||||
for (; point + 2 < end_point; point += 3) {
|
||||
stroke_lines_append_bezier(&lines,
|
||||
&point[0],
|
||||
&point[1],
|
||||
&point[2]);
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (; point < end_point; point++) {
|
||||
stroke_lines_append_fix(&lines, point);
|
||||
}
|
||||
}
|
||||
if (flags & SPICE_PATH_END) {
|
||||
if (flags & SPICE_PATH_CLOSE) {
|
||||
stroke_lines_append(&lines,
|
||||
lines.points[0].x, lines.points[0].y);
|
||||
}
|
||||
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
||||
}
|
||||
} while (more);
|
||||
|
||||
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
||||
|
||||
if (gc.base.dash) {
|
||||
free(gc.base.dash);
|
||||
}
|
||||
stroke_lines_free(&lines);
|
||||
|
||||
if (!gc.solid && gc.tile) {
|
||||
pixman_image_unref(gc.tile);
|
||||
}
|
||||
|
||||
pixman_region32_fini(&gc.dest_region);
|
||||
}
|
||||
|
||||
static void canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
|
||||
{
|
||||
CanvasBase *canvas = (CanvasBase *)spice_canvas;
|
||||
@ -2440,6 +2889,7 @@ inline static void canvas_base_init_ops(SpiceCanvasOps *ops)
|
||||
ops->draw_invers = canvas_draw_invers;
|
||||
ops->draw_transparent = canvas_draw_transparent;
|
||||
ops->draw_alpha_blend = canvas_draw_alpha_blend;
|
||||
ops->draw_stroke = canvas_draw_stroke;
|
||||
ops->group_start = canvas_base_group_start;
|
||||
ops->group_end = canvas_base_group_end;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user