Pillow
2002 строки · 52.0 Кб
1/*
2* The Python Imaging Library.
3* $Id$
4*
5* a simple drawing package for the Imaging library
6*
7* history:
8* 1996-04-13 fl Created.
9* 1996-04-30 fl Added transforms and polygon support.
10* 1996-08-12 fl Added filled polygons.
11* 1996-11-05 fl Fixed float/int confusion in polygon filler
12* 1997-07-04 fl Support 32-bit images (C++ would have been nice)
13* 1998-09-09 fl Eliminated qsort casts; improved rectangle clipping
14* 1998-09-10 fl Fixed fill rectangle to include lower edge (!)
15* 1998-12-29 fl Added arc, chord, and pieslice primitives
16* 1999-01-10 fl Added some level 2 ("arrow") stuff (experimental)
17* 1999-02-06 fl Added bitmap primitive
18* 1999-07-26 fl Eliminated a compiler warning
19* 1999-07-31 fl Pass ink as void* instead of int
20* 2002-12-10 fl Added experimental RGBA-on-RGB drawing
21* 2004-09-04 fl Support simple wide lines (no joins)
22* 2005-05-25 fl Fixed line width calculation
23*
24* Copyright (c) 1996-2006 by Fredrik Lundh
25* Copyright (c) 1997-2006 by Secret Labs AB.
26*
27* See the README file for information on usage and redistribution.
28*/
29
30/* FIXME: support fill/outline attribute for all filled shapes */
31/* FIXME: support zero-winding fill */
32/* FIXME: add drawing context, support affine transforms */
33/* FIXME: support clip window (and mask?) */
34
35#include "Imaging.h"36
37#include <math.h>38#include <stdint.h>39
40#define CEIL(v) (int)ceil(v)41#define FLOOR(v) ((v) >= 0.0 ? (int)(v) : (int)floor(v))42
43#define INK8(ink) (*(UINT8 *)ink)44#define INK16(ink) (*(UINT16 *)ink)45
46/*
47* Rounds around zero (up=away from zero, down=towards zero)
48* This guarantees that ROUND_UP|DOWN(f) == -ROUND_UP|DOWN(-f)
49*/
50#define ROUND_UP(f) ((int)((f) >= 0.0 ? floor((f) + 0.5F) : -floor(fabs(f) + 0.5F)))51#define ROUND_DOWN(f) ((int)((f) >= 0.0 ? ceil((f) - 0.5F) : -ceil(fabs(f) - 0.5F)))52
53/* -------------------------------------------------------------------- */
54/* Primitives */
55/* -------------------------------------------------------------------- */
56
57typedef struct {58/* edge descriptor for polygon engine */59int d;60int x0, y0;61int xmin, ymin, xmax, ymax;62float dx;63} Edge;64
65/* Type used in "polygon*" functions */
66typedef void (*hline_handler)(Imaging, int, int, int, int);67
68static inline void69point8(Imaging im, int x, int y, int ink) {70if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {71if (strncmp(im->mode, "I;16", 4) == 0) {72#ifdef WORDS_BIGENDIAN73im->image8[y][x * 2] = (UINT8)(ink >> 8);74im->image8[y][x * 2 + 1] = (UINT8)ink;75#else76im->image8[y][x * 2] = (UINT8)ink;77im->image8[y][x * 2 + 1] = (UINT8)(ink >> 8);78#endif79} else {80im->image8[y][x] = (UINT8)ink;81}82}83}
84
85static inline void86point32(Imaging im, int x, int y, int ink) {87if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {88im->image32[y][x] = ink;89}90}
91
92static inline void93point32rgba(Imaging im, int x, int y, int ink) {94unsigned int tmp;95
96if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {97UINT8 *out = (UINT8 *)im->image[y] + x * 4;98UINT8 *in = (UINT8 *)&ink;99out[0] = BLEND(in[3], out[0], in[0], tmp);100out[1] = BLEND(in[3], out[1], in[1], tmp);101out[2] = BLEND(in[3], out[2], in[2], tmp);102}103}
104
105static inline void106hline8(Imaging im, int x0, int y0, int x1, int ink) {107int pixelwidth;108
109if (y0 >= 0 && y0 < im->ysize) {110if (x0 < 0) {111x0 = 0;112} else if (x0 >= im->xsize) {113return;114}115if (x1 < 0) {116return;117} else if (x1 >= im->xsize) {118x1 = im->xsize - 1;119}120if (x0 <= x1) {121pixelwidth = strncmp(im->mode, "I;16", 4) == 0 ? 2 : 1;122memset(123im->image8[y0] + x0 * pixelwidth, (UINT8)ink, (x1 - x0 + 1) * pixelwidth124);125}126}127}
128
129static inline void130hline32(Imaging im, int x0, int y0, int x1, int ink) {131INT32 *p;132
133if (y0 >= 0 && y0 < im->ysize) {134if (x0 < 0) {135x0 = 0;136} else if (x0 >= im->xsize) {137return;138}139if (x1 < 0) {140return;141} else if (x1 >= im->xsize) {142x1 = im->xsize - 1;143}144p = im->image32[y0];145while (x0 <= x1) {146p[x0++] = ink;147}148}149}
150
151static inline void152hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {153unsigned int tmp;154
155if (y0 >= 0 && y0 < im->ysize) {156if (x0 < 0) {157x0 = 0;158} else if (x0 >= im->xsize) {159return;160}161if (x1 < 0) {162return;163} else if (x1 >= im->xsize) {164x1 = im->xsize - 1;165}166if (x0 <= x1) {167UINT8 *out = (UINT8 *)im->image[y0] + x0 * 4;168UINT8 *in = (UINT8 *)&ink;169while (x0 <= x1) {170out[0] = BLEND(in[3], out[0], in[0], tmp);171out[1] = BLEND(in[3], out[1], in[1], tmp);172out[2] = BLEND(in[3], out[2], in[2], tmp);173x0++;174out += 4;175}176}177}178}
179
180static inline void181line8(Imaging im, int x0, int y0, int x1, int y1, int ink) {182int i, n, e;183int dx, dy;184int xs, ys;185
186/* normalize coordinates */187dx = x1 - x0;188if (dx < 0) {189dx = -dx, xs = -1;190} else {191xs = 1;192}193dy = y1 - y0;194if (dy < 0) {195dy = -dy, ys = -1;196} else {197ys = 1;198}199
200n = (dx > dy) ? dx : dy;201
202if (dx == 0) {203/* vertical */204for (i = 0; i < dy; i++) {205point8(im, x0, y0, ink);206y0 += ys;207}208
209} else if (dy == 0) {210/* horizontal */211for (i = 0; i < dx; i++) {212point8(im, x0, y0, ink);213x0 += xs;214}215
216} else if (dx > dy) {217/* bresenham, horizontal slope */218n = dx;219dy += dy;220e = dy - dx;221dx += dx;222
223for (i = 0; i < n; i++) {224point8(im, x0, y0, ink);225if (e >= 0) {226y0 += ys;227e -= dx;228}229e += dy;230x0 += xs;231}232
233} else {234/* bresenham, vertical slope */235n = dy;236dx += dx;237e = dx - dy;238dy += dy;239
240for (i = 0; i < n; i++) {241point8(im, x0, y0, ink);242if (e >= 0) {243x0 += xs;244e -= dy;245}246e += dx;247y0 += ys;248}249}250}
251
252static inline void253line32(Imaging im, int x0, int y0, int x1, int y1, int ink) {254int i, n, e;255int dx, dy;256int xs, ys;257
258/* normalize coordinates */259dx = x1 - x0;260if (dx < 0) {261dx = -dx, xs = -1;262} else {263xs = 1;264}265dy = y1 - y0;266if (dy < 0) {267dy = -dy, ys = -1;268} else {269ys = 1;270}271
272n = (dx > dy) ? dx : dy;273
274if (dx == 0) {275/* vertical */276for (i = 0; i < dy; i++) {277point32(im, x0, y0, ink);278y0 += ys;279}280
281} else if (dy == 0) {282/* horizontal */283for (i = 0; i < dx; i++) {284point32(im, x0, y0, ink);285x0 += xs;286}287
288} else if (dx > dy) {289/* bresenham, horizontal slope */290n = dx;291dy += dy;292e = dy - dx;293dx += dx;294
295for (i = 0; i < n; i++) {296point32(im, x0, y0, ink);297if (e >= 0) {298y0 += ys;299e -= dx;300}301e += dy;302x0 += xs;303}304
305} else {306/* bresenham, vertical slope */307n = dy;308dx += dx;309e = dx - dy;310dy += dy;311
312for (i = 0; i < n; i++) {313point32(im, x0, y0, ink);314if (e >= 0) {315x0 += xs;316e -= dy;317}318e += dx;319y0 += ys;320}321}322}
323
324static inline void325line32rgba(Imaging im, int x0, int y0, int x1, int y1, int ink) {326int i, n, e;327int dx, dy;328int xs, ys;329
330/* normalize coordinates */331dx = x1 - x0;332if (dx < 0) {333dx = -dx, xs = -1;334} else {335xs = 1;336}337dy = y1 - y0;338if (dy < 0) {339dy = -dy, ys = -1;340} else {341ys = 1;342}343
344n = (dx > dy) ? dx : dy;345
346if (dx == 0) {347/* vertical */348for (i = 0; i < dy; i++) {349point32rgba(im, x0, y0, ink);350y0 += ys;351}352
353} else if (dy == 0) {354/* horizontal */355for (i = 0; i < dx; i++) {356point32rgba(im, x0, y0, ink);357x0 += xs;358}359
360} else if (dx > dy) {361/* bresenham, horizontal slope */362n = dx;363dy += dy;364e = dy - dx;365dx += dx;366
367for (i = 0; i < n; i++) {368point32rgba(im, x0, y0, ink);369if (e >= 0) {370y0 += ys;371e -= dx;372}373e += dy;374x0 += xs;375}376
377} else {378/* bresenham, vertical slope */379n = dy;380dx += dx;381e = dx - dy;382dy += dy;383
384for (i = 0; i < n; i++) {385point32rgba(im, x0, y0, ink);386if (e >= 0) {387x0 += xs;388e -= dy;389}390e += dx;391y0 += ys;392}393}394}
395
396static int397x_cmp(const void *x0, const void *x1) {398float diff = *((float *)x0) - *((float *)x1);399if (diff < 0) {400return -1;401} else if (diff > 0) {402return 1;403} else {404return 0;405}406}
407
408static void409draw_horizontal_lines(410Imaging im, int n, Edge *e, int ink, int *x_pos, int y, hline_handler hline411) {412int i;413for (i = 0; i < n; i++) {414if (e[i].ymin == y && e[i].ymin == e[i].ymax) {415int xmax;416int xmin = e[i].xmin;417if (*x_pos != -1 && *x_pos < xmin) {418// Line would be after the current position419continue;420}421
422xmax = e[i].xmax;423if (*x_pos > xmin) {424// Line would be partway through x_pos, so increase the starting point425xmin = *x_pos;426if (xmax < xmin) {427// Line would now end before it started428continue;429}430}431
432(*hline)(im, xmin, e[i].ymin, xmax, ink);433*x_pos = xmax + 1;434}435}436}
437
438/*
439* Filled polygon draw function using scan line algorithm.
440*/
441static inline int442polygon_generic(443Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline, int hasAlpha444) {445Edge **edge_table;446float *xx;447int edge_count = 0;448int ymin = im->ysize - 1;449int ymax = 0;450int i, j, k;451float adjacent_line_x, adjacent_line_x_other_edge;452
453if (n <= 0) {454return 0;455}456
457/* Initialize the edge table and find polygon boundaries */458/* malloc check ok, using calloc */459edge_table = calloc(n, sizeof(Edge *));460if (!edge_table) {461return -1;462}463
464for (i = 0; i < n; i++) {465if (ymin > e[i].ymin) {466ymin = e[i].ymin;467}468if (ymax < e[i].ymax) {469ymax = e[i].ymax;470}471if (e[i].ymin == e[i].ymax) {472if (hasAlpha != 1) {473(*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink);474}475continue;476}477edge_table[edge_count++] = (e + i);478}479if (ymin < 0) {480ymin = 0;481}482if (ymax > im->ysize) {483ymax = im->ysize;484}485
486/* Process the edge table with a scan line searching for intersections */487/* malloc check ok, using calloc */488xx = calloc(edge_count * 2, sizeof(float));489if (!xx) {490free(edge_table);491return -1;492}493for (; ymin <= ymax; ymin++) {494j = 0;495for (i = 0; i < edge_count; i++) {496Edge *current = edge_table[i];497if (ymin >= current->ymin && ymin <= current->ymax) {498xx[j++] = (ymin - current->y0) * current->dx + current->x0;499
500if (ymin == current->ymax && ymin < ymax) {501// Needed to draw consistent polygons502xx[j] = xx[j - 1];503j++;504} else if (current->dx != 0 && roundf(xx[j - 1]) == xx[j - 1]) {505// Connect discontiguous corners506for (k = 0; k < i; k++) {507Edge *other_edge = edge_table[k];508if ((current->dx > 0 && other_edge->dx <= 0) ||509(current->dx < 0 && other_edge->dx >= 0)) {510continue;511}512// Check if the two edges join to make a corner513if (((ymin == current->ymin && ymin == other_edge->ymin) ||514(ymin == current->ymax && ymin == other_edge->ymax)) &&515xx[j - 1] == (ymin - other_edge->y0) * other_edge->dx +516other_edge->x0) {517// Determine points from the edges on the next row518// Or if this is the last row, check the previous row519int offset = ymin == ymax ? -1 : 1;520adjacent_line_x =521(ymin + offset - current->y0) * current->dx +522current->x0;523adjacent_line_x_other_edge =524(ymin + offset - other_edge->y0) * other_edge->dx +525other_edge->x0;526if (ymin == current->ymax) {527if (current->dx > 0) {528xx[k] =529fmax(530adjacent_line_x, adjacent_line_x_other_edge531) +5321;533} else {534xx[k] =535fmin(536adjacent_line_x, adjacent_line_x_other_edge537) -5381;539}540} else {541if (current->dx > 0) {542xx[k] = fmin(543adjacent_line_x, adjacent_line_x_other_edge544);545} else {546xx[k] =547fmax(548adjacent_line_x, adjacent_line_x_other_edge549) +5501;551}552}553break;554}555}556}557}558}559qsort(xx, j, sizeof(float), x_cmp);560if (hasAlpha == 1) {561int x_pos = j == 0 ? -1 : 0;562for (i = 1; i < j; i += 2) {563int x_end = ROUND_DOWN(xx[i]);564if (x_end < x_pos) {565// Line would be before the current position566continue;567}568draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);569if (x_end < x_pos) {570// Line would be before the current position571continue;572}573
574int x_start = ROUND_UP(xx[i - 1]);575if (x_pos > x_start) {576// Line would be partway through x_pos, so increase the starting577// point578x_start = x_pos;579if (x_end < x_start) {580// Line would now end before it started581continue;582}583}584(*hline)(im, x_start, ymin, x_end, ink);585x_pos = x_end + 1;586}587draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);588} else {589for (i = 1; i < j; i += 2) {590(*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink);591}592}593}594
595free(xx);596free(edge_table);597return 0;598}
599
600static inline int601polygon8(Imaging im, int n, Edge *e, int ink, int eofill) {602return polygon_generic(im, n, e, ink, eofill, hline8, 0);603}
604
605static inline int606polygon32(Imaging im, int n, Edge *e, int ink, int eofill) {607return polygon_generic(im, n, e, ink, eofill, hline32, 0);608}
609
610static inline int611polygon32rgba(Imaging im, int n, Edge *e, int ink, int eofill) {612return polygon_generic(im, n, e, ink, eofill, hline32rgba, 1);613}
614
615static inline void616add_edge(Edge *e, int x0, int y0, int x1, int y1) {617/* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */618
619if (x0 <= x1) {620e->xmin = x0, e->xmax = x1;621} else {622e->xmin = x1, e->xmax = x0;623}624
625if (y0 <= y1) {626e->ymin = y0, e->ymax = y1;627} else {628e->ymin = y1, e->ymax = y0;629}630
631if (y0 == y1) {632e->d = 0;633e->dx = 0.0;634} else {635e->dx = ((float)(x1 - x0)) / (y1 - y0);636if (y0 == e->ymin) {637e->d = 1;638} else {639e->d = -1;640}641}642
643e->x0 = x0;644e->y0 = y0;645}
646
647typedef struct {648void (*point)(Imaging im, int x, int y, int ink);649void (*hline)(Imaging im, int x0, int y0, int x1, int ink);650void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink);651int (*polygon)(Imaging im, int n, Edge *e, int ink, int eofill);652} DRAW;653
654DRAW draw8 = {point8, hline8, line8, polygon8};655DRAW draw32 = {point32, hline32, line32, polygon32};656DRAW draw32rgba = {point32rgba, hline32rgba, line32rgba, polygon32rgba};657
658/* -------------------------------------------------------------------- */
659/* Interface */
660/* -------------------------------------------------------------------- */
661
662#define DRAWINIT() \663if (im->image8) { \664draw = &draw8; \665if (strncmp(im->mode, "I;16", 4) == 0) { \666ink = INK16(ink_); \667} else { \668ink = INK8(ink_); \669} \670} else { \671draw = (op) ? &draw32rgba : &draw32; \672memcpy(&ink, ink_, sizeof(ink)); \673}674
675int
676ImagingDrawPoint(Imaging im, int x0, int y0, const void *ink_, int op) {677DRAW *draw;678INT32 ink;679
680DRAWINIT();681
682draw->point(im, x0, y0, ink);683
684return 0;685}
686
687int
688ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void *ink_, int op) {689DRAW *draw;690INT32 ink;691
692DRAWINIT();693
694draw->line(im, x0, y0, x1, y1, ink);695
696return 0;697}
698
699int
700ImagingDrawWideLine(701Imaging im, int x0, int y0, int x1, int y1, const void *ink_, int width, int op702) {703DRAW *draw;704INT32 ink;705int dx, dy;706double big_hypotenuse, small_hypotenuse, ratio_max, ratio_min;707int dxmin, dxmax, dymin, dymax;708Edge e[4];709
710DRAWINIT();711
712dx = x1 - x0;713dy = y1 - y0;714if (dx == 0 && dy == 0) {715draw->point(im, x0, y0, ink);716return 0;717}718
719big_hypotenuse = hypot(dx, dy);720small_hypotenuse = (width - 1) / 2.0;721ratio_max = ROUND_UP(small_hypotenuse) / big_hypotenuse;722ratio_min = ROUND_DOWN(small_hypotenuse) / big_hypotenuse;723
724dxmin = ROUND_DOWN(ratio_min * dy);725dxmax = ROUND_DOWN(ratio_max * dy);726dymin = ROUND_DOWN(ratio_min * dx);727dymax = ROUND_DOWN(ratio_max * dx);728{729int vertices[4][2] = {730{x0 - dxmin, y0 + dymax},731{x1 - dxmin, y1 + dymax},732{x1 + dxmax, y1 - dymin},733{x0 + dxmax, y0 - dymin}734};735
736add_edge(e + 0, vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1]);737add_edge(e + 1, vertices[1][0], vertices[1][1], vertices[2][0], vertices[2][1]);738add_edge(e + 2, vertices[2][0], vertices[2][1], vertices[3][0], vertices[3][1]);739add_edge(e + 3, vertices[3][0], vertices[3][1], vertices[0][0], vertices[0][1]);740
741draw->polygon(im, 4, e, ink, 0);742}743return 0;744}
745
746int
747ImagingDrawRectangle(748Imaging im,749int x0,750int y0,751int x1,752int y1,753const void *ink_,754int fill,755int width,756int op757) {758int i;759int y;760int tmp;761DRAW *draw;762INT32 ink;763
764DRAWINIT();765
766if (y0 > y1) {767tmp = y0, y0 = y1, y1 = tmp;768}769
770if (fill) {771if (y0 < 0) {772y0 = 0;773} else if (y0 >= im->ysize) {774return 0;775}776
777if (y1 < 0) {778return 0;779} else if (y1 > im->ysize) {780y1 = im->ysize;781}782
783for (y = y0; y <= y1; y++) {784draw->hline(im, x0, y, x1, ink);785}786
787} else {788/* outline */789if (width == 0) {790width = 1;791}792for (i = 0; i < width; i++) {793draw->hline(im, x0, y0 + i, x1, ink);794draw->hline(im, x0, y1 - i, x1, ink);795draw->line(im, x1 - i, y0 + width, x1 - i, y1 - width + 1, ink);796draw->line(im, x0 + i, y0 + width, x0 + i, y1 - width + 1, ink);797}798}799
800return 0;801}
802
803int
804ImagingDrawPolygon(805Imaging im, int count, int *xy, const void *ink_, int fill, int width, int op806) {807int i, n, x0, y0, x1, y1;808DRAW *draw;809INT32 ink;810
811if (count <= 0) {812return 0;813}814
815DRAWINIT();816
817if (fill) {818/* Build edge list */819/* malloc check ok, using calloc */820Edge *e = calloc(count, sizeof(Edge));821if (!e) {822(void)ImagingError_MemoryError();823return -1;824}825for (i = n = 0; i < count - 1; i++) {826x0 = xy[i * 2];827y0 = xy[i * 2 + 1];828x1 = xy[i * 2 + 2];829y1 = xy[i * 2 + 3];830if (y0 == y1 && i != 0 && y0 == xy[i * 2 - 1]) {831// This is a horizontal line,832// that immediately follows another horizontal line833Edge *last_e = &e[n - 1];834if (x1 > x0 && x0 > xy[i * 2 - 2]) {835// They are both increasing in x836last_e->xmax = x1;837continue;838} else if (x1 < x0 && x0 < xy[i * 2 - 2]) {839// They are both decreasing in x840last_e->xmin = x1;841continue;842}843}844add_edge(&e[n++], x0, y0, x1, y1);845}846if (xy[i * 2] != xy[0] || xy[i * 2 + 1] != xy[1]) {847add_edge(&e[n++], xy[i * 2], xy[i * 2 + 1], xy[0], xy[1]);848}849draw->polygon(im, n, e, ink, 0);850free(e);851
852} else {853/* Outline */854if (width == 1) {855for (i = 0; i < count - 1; i++) {856draw->line(857im, xy[i * 2], xy[i * 2 + 1], xy[i * 2 + 2], xy[i * 2 + 3], ink858);859}860draw->line(im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink);861} else {862for (i = 0; i < count - 1; i++) {863ImagingDrawWideLine(864im,865xy[i * 2],866xy[i * 2 + 1],867xy[i * 2 + 2],868xy[i * 2 + 3],869ink_,870width,871op
872);873}874ImagingDrawWideLine(875im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op876);877}878}879
880return 0;881}
882
883int
884ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, const void *ink, int op) {885return ImagingFill2(886im, ink, bitmap, x0, y0, x0 + bitmap->xsize, y0 + bitmap->ysize887);888}
889
890/* -------------------------------------------------------------------- */
891/* standard shapes */
892
893// Imagine 2D plane and ellipse with center in (0, 0) and semi-major axes a and b.
894// Then quarter_* stuff approximates its top right quarter (x, y >= 0) with integer
895// points from set {(2x+x0, 2y+y0) | x,y in Z} where x0, y0 are from {0, 1} and
896// are such that point (a, b) is in the set.
897
898typedef struct {899int32_t a, b, cx, cy, ex, ey;900int64_t a2, b2, a2b2;901int8_t finished;902} quarter_state;903
904void
905quarter_init(quarter_state *s, int32_t a, int32_t b) {906if (a < 0 || b < 0) {907s->finished = 1;908} else {909s->a = a;910s->b = b;911s->cx = a;912s->cy = b % 2;913s->ex = a % 2;914s->ey = b;915s->a2 = a * a;916s->b2 = b * b;917s->a2b2 = s->a2 * s->b2;918s->finished = 0;919}920}
921
922// deviation of the point from ellipse curve, basically a substitution
923// of the point into the ellipse equation
924int64_t
925quarter_delta(quarter_state *s, int64_t x, int64_t y) {926return llabs(s->a2 * y * y + s->b2 * x * x - s->a2b2);927}
928
929int8_t
930quarter_next(quarter_state *s, int32_t *ret_x, int32_t *ret_y) {931if (s->finished) {932return -1;933}934*ret_x = s->cx;935*ret_y = s->cy;936if (s->cx == s->ex && s->cy == s->ey) {937s->finished = 1;938} else {939// Bresenham's algorithm, possible optimization: only consider 2 of 3940// next points depending on current slope941int32_t nx = s->cx;942int32_t ny = s->cy + 2;943int64_t ndelta = quarter_delta(s, nx, ny);944if (nx > 1) {945int64_t newdelta = quarter_delta(s, s->cx - 2, s->cy + 2);946if (ndelta > newdelta) {947nx = s->cx - 2;948ny = s->cy + 2;949ndelta = newdelta;950}951newdelta = quarter_delta(s, s->cx - 2, s->cy);952if (ndelta > newdelta) {953nx = s->cx - 2;954ny = s->cy;955}956}957s->cx = nx;958s->cy = ny;959}960return 0;961}
962
963// quarter_* stuff can "draw" a quarter of an ellipse with thickness 1, great.
964// Now we use ellipse_* stuff to join all four quarters of two different sized
965// ellipses and receive horizontal segments of a complete ellipse with
966// specified thickness.
967//
968// Still using integer grid with step 2 at this point (like in quarter_*)
969// to ease angle clipping in future.
970
971typedef struct {972quarter_state st_o, st_i;973int32_t py, pl, pr;974int32_t cy[4], cl[4], cr[4];975int8_t bufcnt;976int8_t finished;977int8_t leftmost;978} ellipse_state;979
980void
981ellipse_init(ellipse_state *s, int32_t a, int32_t b, int32_t w) {982s->bufcnt = 0;983s->leftmost = a % 2;984quarter_init(&s->st_o, a, b);985if (w < 1 || quarter_next(&s->st_o, &s->pr, &s->py) == -1) {986s->finished = 1;987} else {988s->finished = 0;989quarter_init(&s->st_i, a - 2 * (w - 1), b - 2 * (w - 1));990s->pl = s->leftmost;991}992}
993
994int8_t
995ellipse_next(ellipse_state *s, int32_t *ret_x0, int32_t *ret_y, int32_t *ret_x1) {996if (s->bufcnt == 0) {997if (s->finished) {998return -1;999}1000int32_t y = s->py;1001int32_t l = s->pl;1002int32_t r = s->pr;1003int32_t cx = 0, cy = 0;1004int8_t next_ret;1005while ((next_ret = quarter_next(&s->st_o, &cx, &cy)) != -1 && cy <= y) {1006}1007if (next_ret == -1) {1008s->finished = 1;1009} else {1010s->pr = cx;1011s->py = cy;1012}1013while ((next_ret = quarter_next(&s->st_i, &cx, &cy)) != -1 && cy <= y) {1014l = cx;1015}1016s->pl = next_ret == -1 ? s->leftmost : cx;1017
1018if ((l > 0 || l < r) && y > 0) {1019s->cl[s->bufcnt] = l == 0 ? 2 : l;1020s->cy[s->bufcnt] = y;1021s->cr[s->bufcnt] = r;1022++s->bufcnt;1023}1024if (y > 0) {1025s->cl[s->bufcnt] = -r;1026s->cy[s->bufcnt] = y;1027s->cr[s->bufcnt] = -l;1028++s->bufcnt;1029}1030if (l > 0 || l < r) {1031s->cl[s->bufcnt] = l == 0 ? 2 : l;1032s->cy[s->bufcnt] = -y;1033s->cr[s->bufcnt] = r;1034++s->bufcnt;1035}1036s->cl[s->bufcnt] = -r;1037s->cy[s->bufcnt] = -y;1038s->cr[s->bufcnt] = -l;1039++s->bufcnt;1040}1041--s->bufcnt;1042*ret_x0 = s->cl[s->bufcnt];1043*ret_y = s->cy[s->bufcnt];1044*ret_x1 = s->cr[s->bufcnt];1045return 0;1046}
1047
1048// Clipping tree consists of half-plane clipping nodes and combining nodes.
1049// We can throw a horizontal segment in such a tree and collect an ordered set
1050// of resulting disjoint clipped segments organized into a sorted linked list
1051// of their end points.
1052typedef enum {1053CT_AND, // intersection1054CT_OR, // union1055CT_CLIP // half-plane clipping1056} clip_type;1057
1058typedef struct clip_node {1059clip_type type;1060double a, b, c; // half-plane coeffs, only used in clipping nodes1061struct clip_node *l; // child pointers, are only non-NULL in combining nodes1062struct clip_node *r;1063} clip_node;1064
1065// Linked list for the ends of the clipped horizontal segments.
1066// Since the segment is always horizontal, we don't need to store Y coordinate.
1067typedef struct event_list {1068int32_t x;1069int8_t type; // used internally, 1 for the left end (smaller X), -1 for the1070// right end; pointless in output since the output segments1071// are disjoint, therefore the types would always come in pairs1072// and interchange (1 -1 1 -1 ...)1073struct event_list *next;1074} event_list;1075
1076// Mirrors all the clipping nodes of the tree relative to the y = x line.
1077void
1078clip_tree_transpose(clip_node *root) {1079if (root != NULL) {1080if (root->type == CT_CLIP) {1081double t = root->a;1082root->a = root->b;1083root->b = t;1084}1085clip_tree_transpose(root->l);1086clip_tree_transpose(root->r);1087}1088}
1089
1090// Outputs a sequence of open-close events (types -1 and 1) for
1091// non-intersecting segments sorted by X coordinate.
1092// Combining nodes (AND, OR) may also accept sequences for intersecting
1093// segments, i.e. something like correct bracket sequences.
1094int
1095clip_tree_do_clip(1096clip_node *root, int32_t x0, int32_t y, int32_t x1, event_list **ret1097) {1098if (root == NULL) {1099event_list *start = malloc(sizeof(event_list));1100if (!start) {1101ImagingError_MemoryError();1102return -1;1103}1104event_list *end = malloc(sizeof(event_list));1105if (!end) {1106free(start);1107ImagingError_MemoryError();1108return -1;1109}1110start->x = x0;1111start->type = 1;1112start->next = end;1113end->x = x1;1114end->type = -1;1115end->next = NULL;1116*ret = start;1117return 0;1118}1119if (root->type == CT_CLIP) {1120double eps = 1e-9;1121double A = root->a;1122double B = root->b;1123double C = root->c;1124if (fabs(A) < eps) {1125if (B * y + C < -eps) {1126x0 = 1;1127x1 = 0;1128}1129} else {1130// X of intersection1131double ix = -(B * y + C) / A;1132if (A * x0 + B * y + C < eps) {1133x0 = lround(fmax(x0, ix));1134}1135if (A * x1 + B * y + C < eps) {1136x1 = lround(fmin(x1, ix));1137}1138}1139if (x0 <= x1) {1140event_list *start = malloc(sizeof(event_list));1141if (!start) {1142ImagingError_MemoryError();1143return -1;1144}1145event_list *end = malloc(sizeof(event_list));1146if (!end) {1147free(start);1148ImagingError_MemoryError();1149return -1;1150}1151start->x = x0;1152start->type = 1;1153start->next = end;1154end->x = x1;1155end->type = -1;1156end->next = NULL;1157*ret = start;1158} else {1159*ret = NULL;1160}1161return 0;1162}1163if (root->type == CT_OR || root->type == CT_AND) {1164event_list *l1;1165event_list *l2;1166if (clip_tree_do_clip(root->l, x0, y, x1, &l1) < 0) {1167return -1;1168}1169if (clip_tree_do_clip(root->r, x0, y, x1, &l2) < 0) {1170while (l1) {1171l2 = l1->next;1172free(l1);1173l1 = l2;1174}1175return -1;1176}1177*ret = NULL;1178event_list *tail = NULL;1179int32_t k1 = 0;1180int32_t k2 = 0;1181while (l1 != NULL || l2 != NULL) {1182event_list *t;1183if (l2 == NULL ||1184(l1 != NULL &&1185(l1->x < l2->x || (l1->x == l2->x && l1->type > l2->type)))) {1186t = l1;1187k1 += t->type;1188assert(k1 >= 0);1189l1 = l1->next;1190} else {1191t = l2;1192k2 += t->type;1193assert(k2 >= 0);1194l2 = l2->next;1195}1196t->next = NULL;1197if ((root->type == CT_OR &&1198((t->type == 1 && (tail == NULL || tail->type == -1)) ||1199(t->type == -1 && k1 == 0 && k2 == 0))) ||1200(root->type == CT_AND &&1201((t->type == 1 && (tail == NULL || tail->type == -1) && k1 > 0 &&1202k2 > 0) ||1203(t->type == -1 && tail != NULL && tail->type == 1 &&1204(k1 == 0 || k2 == 0))))) {1205if (tail == NULL) {1206*ret = t;1207} else {1208tail->next = t;1209}1210tail = t;1211} else {1212free(t);1213}1214}1215return 0;1216}1217*ret = NULL;1218return 0;1219}
1220
1221// One more layer of processing on top of the regular ellipse.
1222// Uses the clipping tree.
1223// Used for producing ellipse derivatives such as arc, chord, pie, etc.
1224typedef struct {1225ellipse_state st;1226clip_node *root;1227clip_node nodes[7];1228int32_t node_count;1229event_list *head;1230int32_t y;1231} clip_ellipse_state;1232
1233typedef void (*clip_ellipse_init)(1234clip_ellipse_state *, int32_t, int32_t, int32_t, float, float1235);1236
1237void
1238debug_clip_tree(clip_node *root, int space) {1239if (root == NULL) {1240return;1241}1242if (root->type == CT_CLIP) {1243int t = space;1244while (t--) {1245fputc(' ', stderr);1246}1247fprintf(stderr, "clip %+fx%+fy%+f > 0\n", root->a, root->b, root->c);1248} else {1249debug_clip_tree(root->l, space + 2);1250int t = space;1251while (t--) {1252fputc(' ', stderr);1253}1254fprintf(stderr, "%s\n", root->type == CT_AND ? "and" : "or");1255debug_clip_tree(root->r, space + 2);1256}1257if (space == 0) {1258fputc('\n', stderr);1259}1260}
1261
1262// Resulting angles will satisfy 0 <= al < 360, al <= ar <= al + 360
1263void
1264normalize_angles(float *al, float *ar) {1265if (*ar - *al >= 360) {1266*al = 0;1267*ar = 360;1268} else {1269*al = fmod(*al < 0 ? 360 - (fmod(-*al, 360)) : *al, 360);1270*ar = *al + fmod(*ar < *al ? 360 - fmod(*al - *ar, 360) : *ar - *al, 360);1271}1272}
1273
1274// An arc with caps orthogonal to the ellipse curve.
1275void
1276arc_init(clip_ellipse_state *s, int32_t a, int32_t b, int32_t w, float al, float ar) {1277if (a < b) {1278// transpose the coordinate system1279arc_init(s, b, a, w, 90 - ar, 90 - al);1280ellipse_init(&s->st, a, b, w);1281clip_tree_transpose(s->root);1282} else {1283// a >= b, based on "wide" ellipse1284ellipse_init(&s->st, a, b, w);1285
1286s->head = NULL;1287s->node_count = 0;1288normalize_angles(&al, &ar);1289
1290// building clipping tree, a lot of different cases1291if (ar == al + 360) {1292s->root = NULL;1293} else {1294clip_node *lc = s->nodes + s->node_count++;1295clip_node *rc = s->nodes + s->node_count++;1296lc->l = lc->r = rc->l = rc->r = NULL;1297lc->type = rc->type = CT_CLIP;1298lc->a = -a * sin(al * M_PI / 180.0);1299lc->b = b * cos(al * M_PI / 180.0);1300lc->c = (a * a - b * b) * sin(al * M_PI / 90.0) / 2.0;1301rc->a = a * sin(ar * M_PI / 180.0);1302rc->b = -b * cos(ar * M_PI / 180.0);1303rc->c = (b * b - a * a) * sin(ar * M_PI / 90.0) / 2.0;1304if (fmod(al, 180) == 0 || fmod(ar, 180) == 0) {1305s->root = s->nodes + s->node_count++;1306s->root->l = lc;1307s->root->r = rc;1308s->root->type = ar - al < 180 ? CT_AND : CT_OR;1309} else if (((int)(al / 180) + (int)(ar / 180)) % 2 == 1) {1310s->root = s->nodes + s->node_count++;1311s->root->l = s->nodes + s->node_count++;1312s->root->l->l = s->nodes + s->node_count++;1313s->root->l->r = lc;1314s->root->r = s->nodes + s->node_count++;1315s->root->r->l = s->nodes + s->node_count++;1316s->root->r->r = rc;1317s->root->type = CT_OR;1318s->root->l->type = CT_AND;1319s->root->r->type = CT_AND;1320s->root->l->l->type = CT_CLIP;1321s->root->r->l->type = CT_CLIP;1322s->root->l->l->l = s->root->l->l->r = NULL;1323s->root->r->l->l = s->root->r->l->r = NULL;1324s->root->l->l->a = s->root->l->l->c = 0;1325s->root->r->l->a = s->root->r->l->c = 0;1326s->root->l->l->b = (int)(al / 180) % 2 == 0 ? 1 : -1;1327s->root->r->l->b = (int)(ar / 180) % 2 == 0 ? 1 : -1;1328} else {1329s->root = s->nodes + s->node_count++;1330s->root->l = s->nodes + s->node_count++;1331s->root->r = s->nodes + s->node_count++;1332s->root->type = s->root->l->type = ar - al < 180 ? CT_AND : CT_OR;1333s->root->l->l = lc;1334s->root->l->r = rc;1335s->root->r->type = CT_CLIP;1336s->root->r->l = s->root->r->r = NULL;1337s->root->r->a = s->root->r->c = 0;1338s->root->r->b = ar < 180 || ar > 540 ? 1 : -1;1339}1340}1341}1342}
1343
1344// A chord line.
1345void
1346chord_line_init(1347clip_ellipse_state *s, int32_t a, int32_t b, int32_t w, float al, float ar1348) {1349ellipse_init(&s->st, a, b, a + b + 1);1350
1351s->head = NULL;1352s->node_count = 0;1353
1354// line equation for chord1355double xl = a * cos(al * M_PI / 180.0), xr = a * cos(ar * M_PI / 180.0);1356double yl = b * sin(al * M_PI / 180.0), yr = b * sin(ar * M_PI / 180.0);1357s->root = s->nodes + s->node_count++;1358s->root->l = s->nodes + s->node_count++;1359s->root->r = s->nodes + s->node_count++;1360s->root->type = CT_AND;1361s->root->l->type = s->root->r->type = CT_CLIP;1362s->root->l->l = s->root->l->r = s->root->r->l = s->root->r->r = NULL;1363s->root->l->a = yr - yl;1364s->root->l->b = xl - xr;1365s->root->l->c = -(s->root->l->a * xl + s->root->l->b * yl);1366s->root->r->a = -s->root->l->a;1367s->root->r->b = -s->root->l->b;1368s->root->r->c =13692 * w * sqrt(pow(s->root->l->a, 2.0) + pow(s->root->l->b, 2.0)) - s->root->l->c;1370}
1371
1372// Pie side.
1373void
1374pie_side_init(1375clip_ellipse_state *s, int32_t a, int32_t b, int32_t w, float al, float _1376) {1377ellipse_init(&s->st, a, b, a + b + 1);1378
1379s->head = NULL;1380s->node_count = 0;1381
1382double xl = a * cos(al * M_PI / 180.0);1383double yl = b * sin(al * M_PI / 180.0);1384double a1 = -yl;1385double b1 = xl;1386double c1 = w * sqrt(a1 * a1 + b1 * b1);1387
1388s->root = s->nodes + s->node_count++;1389s->root->type = CT_AND;1390s->root->l = s->nodes + s->node_count++;1391s->root->l->type = CT_AND;1392
1393clip_node *cnode;1394cnode = s->nodes + s->node_count++;1395cnode->l = cnode->r = NULL;1396cnode->type = CT_CLIP;1397cnode->a = a1;1398cnode->b = b1;1399cnode->c = c1;1400s->root->l->l = cnode;1401cnode = s->nodes + s->node_count++;1402cnode->l = cnode->r = NULL;1403cnode->type = CT_CLIP;1404cnode->a = -a1;1405cnode->b = -b1;1406cnode->c = c1;1407s->root->l->r = cnode;1408cnode = s->nodes + s->node_count++;1409cnode->l = cnode->r = NULL;1410cnode->type = CT_CLIP;1411cnode->a = b1;1412cnode->b = -a1;1413cnode->c = 0;1414s->root->r = cnode;1415}
1416
1417// A chord.
1418void
1419chord_init(clip_ellipse_state *s, int32_t a, int32_t b, int32_t w, float al, float ar) {1420ellipse_init(&s->st, a, b, w);1421
1422s->head = NULL;1423s->node_count = 0;1424
1425// line equation for chord1426double xl = a * cos(al * M_PI / 180.0), xr = a * cos(ar * M_PI / 180.0);1427double yl = b * sin(al * M_PI / 180.0), yr = b * sin(ar * M_PI / 180.0);1428s->root = s->nodes + s->node_count++;1429s->root->l = s->root->r = NULL;1430s->root->type = CT_CLIP;1431s->root->a = yr - yl;1432s->root->b = xl - xr;1433s->root->c = -(s->root->a * xl + s->root->b * yl);1434}
1435
1436// A pie. Can also be used to draw an arc with ugly sharp caps.
1437void
1438pie_init(clip_ellipse_state *s, int32_t a, int32_t b, int32_t w, float al, float ar) {1439ellipse_init(&s->st, a, b, w);1440
1441s->head = NULL;1442s->node_count = 0;1443
1444// line equations for pie sides1445double xl = a * cos(al * M_PI / 180.0), xr = a * cos(ar * M_PI / 180.0);1446double yl = b * sin(al * M_PI / 180.0), yr = b * sin(ar * M_PI / 180.0);1447
1448clip_node *lc = s->nodes + s->node_count++;1449clip_node *rc = s->nodes + s->node_count++;1450lc->l = lc->r = rc->l = rc->r = NULL;1451lc->type = rc->type = CT_CLIP;1452lc->a = -yl;1453lc->b = xl;1454lc->c = 0;1455rc->a = yr;1456rc->b = -xr;1457rc->c = 0;1458
1459s->root = s->nodes + s->node_count++;1460s->root->l = lc;1461s->root->r = rc;1462s->root->type = ar - al < 180 ? CT_AND : CT_OR;1463
1464// add one more semiplane to avoid spikes1465if (ar - al < 90) {1466clip_node *old_root = s->root;1467clip_node *spike_clipper = s->nodes + s->node_count++;1468s->root = s->nodes + s->node_count++;1469s->root->l = old_root;1470s->root->r = spike_clipper;1471s->root->type = CT_AND;1472
1473spike_clipper->l = spike_clipper->r = NULL;1474spike_clipper->type = CT_CLIP;1475spike_clipper->a = (xl + xr) / 2.0;1476spike_clipper->b = (yl + yr) / 2.0;1477spike_clipper->c = 0;1478}1479}
1480
1481void
1482clip_ellipse_free(clip_ellipse_state *s) {1483while (s->head != NULL) {1484event_list *t = s->head;1485s->head = s->head->next;1486free(t);1487}1488}
1489
1490int8_t
1491clip_ellipse_next(1492clip_ellipse_state *s, int32_t *ret_x0, int32_t *ret_y, int32_t *ret_x11493) {1494int32_t x0, y, x1;1495while (s->head == NULL && ellipse_next(&s->st, &x0, &y, &x1) >= 0) {1496if (clip_tree_do_clip(s->root, x0, y, x1, &s->head) < 0) {1497return -2;1498}1499s->y = y;1500}1501if (s->head != NULL) {1502*ret_y = s->y;1503event_list *t = s->head;1504s->head = s->head->next;1505*ret_x0 = t->x;1506free(t);1507t = s->head;1508assert(t != NULL);1509s->head = s->head->next;1510*ret_x1 = t->x;1511free(t);1512return 0;1513}1514return -1;1515}
1516
1517static int1518ellipseNew(1519Imaging im,1520int x0,1521int y0,1522int x1,1523int y1,1524const void *ink_,1525int fill,1526int width,1527int op1528) {1529DRAW *draw;1530INT32 ink;1531DRAWINIT();1532
1533int a = x1 - x0;1534int b = y1 - y0;1535if (a < 0 || b < 0) {1536return 0;1537}1538if (fill) {1539width = a + b;1540}1541
1542ellipse_state st;1543ellipse_init(&st, a, b, width);1544int32_t X0, Y, X1;1545while (ellipse_next(&st, &X0, &Y, &X1) != -1) {1546draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink);1547}1548return 0;1549}
1550
1551static int1552clipEllipseNew(1553Imaging im,1554int x0,1555int y0,1556int x1,1557int y1,1558float start,1559float end,1560const void *ink_,1561int width,1562int op,1563clip_ellipse_init init
1564) {1565DRAW *draw;1566INT32 ink;1567DRAWINIT();1568
1569int a = x1 - x0;1570int b = y1 - y0;1571if (a < 0 || b < 0) {1572return 0;1573}1574
1575clip_ellipse_state st;1576init(&st, a, b, width, start, end);1577// debug_clip_tree(st.root, 0);1578int32_t X0, Y, X1;1579int next_code;1580while ((next_code = clip_ellipse_next(&st, &X0, &Y, &X1)) >= 0) {1581draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink);1582}1583clip_ellipse_free(&st);1584return next_code == -1 ? 0 : -1;1585}
1586static int1587arcNew(1588Imaging im,1589int x0,1590int y0,1591int x1,1592int y1,1593float start,1594float end,1595const void *ink_,1596int width,1597int op1598) {1599return clipEllipseNew(im, x0, y0, x1, y1, start, end, ink_, width, op, arc_init);1600}
1601
1602static int1603chordNew(1604Imaging im,1605int x0,1606int y0,1607int x1,1608int y1,1609float start,1610float end,1611const void *ink_,1612int width,1613int op1614) {1615return clipEllipseNew(im, x0, y0, x1, y1, start, end, ink_, width, op, chord_init);1616}
1617
1618static int1619chordLineNew(1620Imaging im,1621int x0,1622int y0,1623int x1,1624int y1,1625float start,1626float end,1627const void *ink_,1628int width,1629int op1630) {1631return clipEllipseNew(1632im, x0, y0, x1, y1, start, end, ink_, width, op, chord_line_init1633);1634}
1635
1636static int1637pieNew(1638Imaging im,1639int x0,1640int y0,1641int x1,1642int y1,1643float start,1644float end,1645const void *ink_,1646int width,1647int op1648) {1649return clipEllipseNew(im, x0, y0, x1, y1, start, end, ink_, width, op, pie_init);1650}
1651
1652static int1653pieSideNew(1654Imaging im,1655int x0,1656int y0,1657int x1,1658int y1,1659float start,1660const void *ink_,1661int width,1662int op1663) {1664return clipEllipseNew(im, x0, y0, x1, y1, start, 0, ink_, width, op, pie_side_init);1665}
1666
1667int
1668ImagingDrawEllipse(1669Imaging im,1670int x0,1671int y0,1672int x1,1673int y1,1674const void *ink,1675int fill,1676int width,1677int op1678) {1679return ellipseNew(im, x0, y0, x1, y1, ink, fill, width, op);1680}
1681
1682int
1683ImagingDrawArc(1684Imaging im,1685int x0,1686int y0,1687int x1,1688int y1,1689float start,1690float end,1691const void *ink,1692int width,1693int op1694) {1695normalize_angles(&start, &end);1696if (start + 360 == end) {1697return ImagingDrawEllipse(im, x0, y0, x1, y1, ink, 0, width, op);1698}1699if (start == end) {1700return 0;1701}1702return arcNew(im, x0, y0, x1, y1, start, end, ink, width, op);1703}
1704
1705int
1706ImagingDrawChord(1707Imaging im,1708int x0,1709int y0,1710int x1,1711int y1,1712float start,1713float end,1714const void *ink,1715int fill,1716int width,1717int op1718) {1719normalize_angles(&start, &end);1720if (start + 360 == end) {1721return ImagingDrawEllipse(im, x0, y0, x1, y1, ink, fill, width, op);1722}1723if (start == end) {1724return 0;1725}1726if (fill) {1727return chordNew(im, x0, y0, x1, y1, start, end, ink, x1 - x0 + y1 - y0 + 1, op);1728} else {1729if (chordLineNew(im, x0, y0, x1, y1, start, end, ink, width, op)) {1730return -1;1731}1732return chordNew(im, x0, y0, x1, y1, start, end, ink, width, op);1733}1734}
1735
1736int
1737ImagingDrawPieslice(1738Imaging im,1739int x0,1740int y0,1741int x1,1742int y1,1743float start,1744float end,1745const void *ink,1746int fill,1747int width,1748int op1749) {1750normalize_angles(&start, &end);1751if (start + 360 == end) {1752return ellipseNew(im, x0, y0, x1, y1, ink, fill, width, op);1753}1754if (start == end) {1755return 0;1756}1757if (fill) {1758return pieNew(im, x0, y0, x1, y1, start, end, ink, x1 + y1 - x0 - y0, op);1759} else {1760if (pieSideNew(im, x0, y0, x1, y1, start, ink, width, op)) {1761return -1;1762}1763if (pieSideNew(im, x0, y0, x1, y1, end, ink, width, op)) {1764return -1;1765}1766int xc = lround((x0 + x1 - width) / 2.0), yc = lround((y0 + y1 - width) / 2.0);1767ellipseNew(im, xc, yc, xc + width - 1, yc + width - 1, ink, 1, 0, op);1768return pieNew(im, x0, y0, x1, y1, start, end, ink, width, op);1769}1770}
1771
1772/* -------------------------------------------------------------------- */
1773
1774/* experimental level 2 ("arrow") graphics stuff. this implements
1775portions of the arrow api on top of the Edge structure. the
1776semantics are ok, except that "curve" flattens the bezier curves by
1777itself */
1778
1779struct ImagingOutlineInstance {1780float x0, y0;1781
1782float x, y;1783
1784int count;1785Edge *edges;1786
1787int size;1788};1789
1790ImagingOutline
1791ImagingOutlineNew(void) {1792ImagingOutline outline;1793
1794outline = calloc(1, sizeof(struct ImagingOutlineInstance));1795if (!outline) {1796return (ImagingOutline)ImagingError_MemoryError();1797}1798
1799outline->edges = NULL;1800outline->count = outline->size = 0;1801
1802ImagingOutlineMove(outline, 0, 0);1803
1804return outline;1805}
1806
1807void
1808ImagingOutlineDelete(ImagingOutline outline) {1809if (!outline) {1810return;1811}1812
1813if (outline->edges) {1814free(outline->edges);1815}1816
1817free(outline);1818}
1819
1820static Edge *1821allocate(ImagingOutline outline, int extra) {1822Edge *e;1823
1824if (outline->count + extra > outline->size) {1825/* expand outline buffer */1826outline->size += extra + 25;1827if (!outline->edges) {1828/* malloc check ok, uses calloc for overflow */1829e = calloc(outline->size, sizeof(Edge));1830} else {1831if (outline->size > INT_MAX / (int)sizeof(Edge)) {1832return NULL;1833}1834/* malloc check ok, overflow checked above */1835e = realloc(outline->edges, outline->size * sizeof(Edge));1836}1837if (!e) {1838return NULL;1839}1840outline->edges = e;1841}1842
1843e = outline->edges + outline->count;1844
1845outline->count += extra;1846
1847return e;1848}
1849
1850int
1851ImagingOutlineMove(ImagingOutline outline, float x0, float y0) {1852outline->x = outline->x0 = x0;1853outline->y = outline->y0 = y0;1854
1855return 0;1856}
1857
1858int
1859ImagingOutlineLine(ImagingOutline outline, float x1, float y1) {1860Edge *e;1861
1862e = allocate(outline, 1);1863if (!e) {1864return -1; /* out of memory */1865}1866
1867add_edge(e, (int)outline->x, (int)outline->y, (int)x1, (int)y1);1868
1869outline->x = x1;1870outline->y = y1;1871
1872return 0;1873}
1874
1875int
1876ImagingOutlineCurve(1877ImagingOutline outline, float x1, float y1, float x2, float y2, float x3, float y31878) {1879Edge *e;1880int i;1881float xo, yo;1882
1883#define STEPS 321884
1885e = allocate(outline, STEPS);1886if (!e) {1887return -1; /* out of memory */1888}1889
1890xo = outline->x;1891yo = outline->y;1892
1893/* flatten the bezier segment */1894
1895for (i = 1; i <= STEPS; i++) {1896float t = ((float)i) / STEPS;1897float t2 = t * t;1898float t3 = t2 * t;1899
1900float u = 1.0F - t;1901float u2 = u * u;1902float u3 = u2 * u;1903
1904float x = outline->x * u3 + 3 * (x1 * t * u2 + x2 * t2 * u) + x3 * t3 + 0.5;1905float y = outline->y * u3 + 3 * (y1 * t * u2 + y2 * t2 * u) + y3 * t3 + 0.5;1906
1907add_edge(e++, xo, yo, (int)x, (int)y);1908
1909xo = x, yo = y;1910}1911
1912outline->x = xo;1913outline->y = yo;1914
1915return 0;1916}
1917
1918int
1919ImagingOutlineClose(ImagingOutline outline) {1920if (outline->x == outline->x0 && outline->y == outline->y0) {1921return 0;1922}1923return ImagingOutlineLine(outline, outline->x0, outline->y0);1924}
1925
1926int
1927ImagingOutlineTransform(ImagingOutline outline, double a[6]) {1928Edge *eIn;1929Edge *eOut;1930int i, n;1931int x0, y0, x1, y1;1932int X0, Y0, X1, Y1;1933
1934double a0 = a[0];1935double a1 = a[1];1936double a2 = a[2];1937double a3 = a[3];1938double a4 = a[4];1939double a5 = a[5];1940
1941eIn = outline->edges;1942n = outline->count;1943
1944eOut = allocate(outline, n);1945if (!eOut) {1946ImagingError_MemoryError();1947return -1;1948}1949
1950for (i = 0; i < n; i++) {1951x0 = eIn->x0;1952y0 = eIn->y0;1953
1954/* FIXME: ouch! */1955if (eIn->x0 == eIn->xmin) {1956x1 = eIn->xmax;1957} else {1958x1 = eIn->xmin;1959}1960if (eIn->y0 == eIn->ymin) {1961y1 = eIn->ymax;1962} else {1963y1 = eIn->ymin;1964}1965
1966/* full moon tonight! if this doesn't work, you may need to1967upgrade your compiler (make sure you have the right service
1968pack) */
1969
1970X0 = (int)(a0 * x0 + a1 * y0 + a2);1971Y0 = (int)(a3 * x0 + a4 * y0 + a5);1972X1 = (int)(a0 * x1 + a1 * y1 + a2);1973Y1 = (int)(a3 * x1 + a4 * y1 + a5);1974
1975add_edge(eOut, X0, Y0, X1, Y1);1976
1977eIn++;1978eOut++;1979}1980
1981free(outline->edges);1982
1983/* FIXME: ugly! */1984outline->edges = NULL;1985outline->count = outline->size = 0;1986
1987return 0;1988}
1989
1990int
1991ImagingDrawOutline(1992Imaging im, ImagingOutline outline, const void *ink_, int fill, int op1993) {1994DRAW *draw;1995INT32 ink;1996
1997DRAWINIT();1998
1999draw->polygon(im, outline->count, outline->edges, ink, 0);2000
2001return 0;2002}
2003