Pillow/src/libImaging/Draw.c

1281 lines
29 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* The Python Imaging Library.
* $Id$
*
* a simple drawing package for the Imaging library
*
* history:
* 1996-04-13 fl Created.
* 1996-04-30 fl Added transforms and polygon support.
* 1996-08-12 fl Added filled polygons.
* 1996-11-05 fl Fixed float/int confusion in polygon filler
* 1997-07-04 fl Support 32-bit images (C++ would have been nice)
* 1998-09-09 fl Eliminated qsort casts; improved rectangle clipping
* 1998-09-10 fl Fixed fill rectangle to include lower edge (!)
* 1998-12-29 fl Added arc, chord, and pieslice primitives
* 1999-01-10 fl Added some level 2 ("arrow") stuff (experimental)
* 1999-02-06 fl Added bitmap primitive
* 1999-07-26 fl Eliminated a compiler warning
* 1999-07-31 fl Pass ink as void* instead of int
* 2002-12-10 fl Added experimental RGBA-on-RGB drawing
* 2004-09-04 fl Support simple wide lines (no joins)
* 2005-05-25 fl Fixed line width calculation
*
* Copyright (c) 1996-2006 by Fredrik Lundh
* Copyright (c) 1997-2006 by Secret Labs AB.
*
* See the README file for information on usage and redistribution.
*/
/* FIXME: support fill/outline attribute for all filled shapes */
/* FIXME: support zero-winding fill */
/* FIXME: add drawing context, support affine transforms */
/* FIXME: support clip window (and mask?) */
#include "Imaging.h"
#include <math.h>
#define CEIL(v) (int) ceil(v)
#define FLOOR(v) ((v) >= 0.0 ? (int) (v) : (int) floor(v))
#define INK8(ink) (*(UINT8*)ink)
/*
2020-01-09 12:00:32 +03:00
* Rounds around zero (up=away from zero, down=towards zero)
* This guarantees that ROUND_UP|DOWN(f) == -ROUND_UP|DOWN(-f)
*/
#define ROUND_UP(f) ((int) ((f) >= 0.0 ? floor((f) + 0.5F) : -floor(fabs(f) + 0.5F)))
#define ROUND_DOWN(f) ((int) ((f) >= 0.0 ? ceil((f) - 0.5F) : -ceil(fabs(f) - 0.5F)))
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
/* Primitives */
/* -------------------------------------------------------------------- */
typedef struct {
/* edge descriptor for polygon engine */
int d;
int x0, y0;
int xmin, ymin, xmax, ymax;
float dx;
} Edge;
/* Type used in "polygon*" functions */
typedef void (*hline_handler)(Imaging, int, int, int, int);
2010-07-31 06:52:47 +04:00
static inline void
point8(Imaging im, int x, int y, int ink)
{
2019-07-01 15:07:45 +03:00
if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {
2019-06-23 00:33:55 +03:00
if (strncmp(im->mode, "I;16", 4) == 0) {
im->image8[y][x*2] = (UINT8) ink;
im->image8[y][x*2+1] = (UINT8) ink;
} else {
im->image8[y][x] = (UINT8) ink;
}
2019-07-01 15:07:45 +03:00
}
2010-07-31 06:52:47 +04:00
}
static inline void
point32(Imaging im, int x, int y, int ink)
{
2020-05-10 12:56:36 +03:00
if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {
2010-07-31 06:52:47 +04:00
im->image32[y][x] = ink;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
static inline void
point32rgba(Imaging im, int x, int y, int ink)
{
2017-08-27 12:40:53 +03:00
unsigned int tmp1;
2010-07-31 06:52:47 +04:00
if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {
UINT8* out = (UINT8*) im->image[y]+x*4;
UINT8* in = (UINT8*) &ink;
2017-08-27 12:40:53 +03:00
out[0] = BLEND(in[3], out[0], in[0], tmp1);
out[1] = BLEND(in[3], out[1], in[1], tmp1);
out[2] = BLEND(in[3], out[2], in[2], tmp1);
2010-07-31 06:52:47 +04:00
}
}
static inline void
hline8(Imaging im, int x0, int y0, int x1, int ink)
{
2019-06-23 00:33:55 +03:00
int tmp, pixelwidth;
2010-07-31 06:52:47 +04:00
if (y0 >= 0 && y0 < im->ysize) {
2020-05-10 12:56:36 +03:00
if (x0 > x1) {
2010-07-31 06:52:47 +04:00
tmp = x0, x0 = x1, x1 = tmp;
2020-05-10 12:56:36 +03:00
}
if (x0 < 0) {
2010-07-31 06:52:47 +04:00
x0 = 0;
2020-05-10 12:56:36 +03:00
} else if (x0 >= im->xsize) {
2010-07-31 06:52:47 +04:00
return;
2020-05-10 12:56:36 +03:00
}
if (x1 < 0) {
2010-07-31 06:52:47 +04:00
return;
2020-05-10 12:56:36 +03:00
} else if (x1 >= im->xsize) {
2010-07-31 06:52:47 +04:00
x1 = im->xsize-1;
2020-05-10 12:56:36 +03:00
}
2019-06-23 00:33:55 +03:00
if (x0 <= x1) {
pixelwidth = strncmp(im->mode, "I;16", 4) == 0 ? 2 : 1;
memset(im->image8[y0] + x0 * pixelwidth, (UINT8) ink,
(x1 - x0 + 1) * pixelwidth);
}
2010-07-31 06:52:47 +04:00
}
}
static inline void
hline32(Imaging im, int x0, int y0, int x1, int ink)
{
int tmp;
INT32* p;
if (y0 >= 0 && y0 < im->ysize) {
2020-05-10 12:56:36 +03:00
if (x0 > x1) {
2010-07-31 06:52:47 +04:00
tmp = x0, x0 = x1, x1 = tmp;
2020-05-10 12:56:36 +03:00
}
if (x0 < 0) {
2010-07-31 06:52:47 +04:00
x0 = 0;
2020-05-10 12:56:36 +03:00
} else if (x0 >= im->xsize) {
2010-07-31 06:52:47 +04:00
return;
2020-05-10 12:56:36 +03:00
}
if (x1 < 0) {
2010-07-31 06:52:47 +04:00
return;
2020-05-10 12:56:36 +03:00
} else if (x1 >= im->xsize) {
2010-07-31 06:52:47 +04:00
x1 = im->xsize-1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
p = im->image32[y0];
2020-05-10 12:56:36 +03:00
while (x0 <= x1) {
2010-07-31 06:52:47 +04:00
p[x0++] = ink;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
}
static inline void
hline32rgba(Imaging im, int x0, int y0, int x1, int ink)
{
int tmp;
2017-08-27 12:40:53 +03:00
unsigned int tmp1;
2010-07-31 06:52:47 +04:00
if (y0 >= 0 && y0 < im->ysize) {
2020-05-10 12:56:36 +03:00
if (x0 > x1) {
2010-07-31 06:52:47 +04:00
tmp = x0, x0 = x1, x1 = tmp;
2020-05-10 12:56:36 +03:00
}
if (x0 < 0) {
2010-07-31 06:52:47 +04:00
x0 = 0;
2020-05-10 12:56:36 +03:00
} else if (x0 >= im->xsize) {
2010-07-31 06:52:47 +04:00
return;
2020-05-10 12:56:36 +03:00
}
if (x1 < 0) {
2010-07-31 06:52:47 +04:00
return;
2020-05-10 12:56:36 +03:00
} else if (x1 >= im->xsize) {
2010-07-31 06:52:47 +04:00
x1 = im->xsize-1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (x0 <= x1) {
UINT8* out = (UINT8*) im->image[y0]+x0*4;
UINT8* in = (UINT8*) &ink;
while (x0 <= x1) {
2017-08-27 12:40:53 +03:00
out[0] = BLEND(in[3], out[0], in[0], tmp1);
out[1] = BLEND(in[3], out[1], in[1], tmp1);
out[2] = BLEND(in[3], out[2], in[2], tmp1);
2010-07-31 06:52:47 +04:00
x0++; out += 4;
}
}
}
}
static inline void
line8(Imaging im, int x0, int y0, int x1, int y1, int ink)
{
int i, n, e;
int dx, dy;
int xs, ys;
/* normalize coordinates */
dx = x1-x0;
2020-05-10 12:56:36 +03:00
if (dx < 0) {
2010-07-31 06:52:47 +04:00
dx = -dx, xs = -1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
xs = 1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
dy = y1-y0;
2020-05-10 12:56:36 +03:00
if (dy < 0) {
2010-07-31 06:52:47 +04:00
dy = -dy, ys = -1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
ys = 1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
n = (dx > dy) ? dx : dy;
2020-05-10 12:56:36 +03:00
if (dx == 0) {
2010-07-31 06:52:47 +04:00
/* vertical */
for (i = 0; i < dy; i++) {
point8(im, x0, y0, ink);
y0 += ys;
}
2020-05-10 12:56:36 +03:00
} else if (dy == 0) {
2010-07-31 06:52:47 +04:00
/* horizontal */
for (i = 0; i < dx; i++) {
point8(im, x0, y0, ink);
x0 += xs;
}
2020-05-10 12:56:36 +03:00
} else if (dx > dy) {
2010-07-31 06:52:47 +04:00
/* bresenham, horizontal slope */
n = dx;
dy += dy;
e = dy - dx;
dx += dx;
for (i = 0; i < n; i++) {
point8(im, x0, y0, ink);
if (e >= 0) {
y0 += ys;
e -= dx;
}
e += dy;
x0 += xs;
}
} else {
/* bresenham, vertical slope */
n = dy;
dx += dx;
e = dx - dy;
dy += dy;
for (i = 0; i < n; i++) {
point8(im, x0, y0, ink);
if (e >= 0) {
x0 += xs;
e -= dy;
}
e += dx;
y0 += ys;
}
}
}
static inline void
line32(Imaging im, int x0, int y0, int x1, int y1, int ink)
{
int i, n, e;
int dx, dy;
int xs, ys;
/* normalize coordinates */
dx = x1-x0;
2020-05-10 12:56:36 +03:00
if (dx < 0) {
2010-07-31 06:52:47 +04:00
dx = -dx, xs = -1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
xs = 1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
dy = y1-y0;
2020-05-10 12:56:36 +03:00
if (dy < 0) {
2010-07-31 06:52:47 +04:00
dy = -dy, ys = -1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
ys = 1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
n = (dx > dy) ? dx : dy;
2020-05-10 12:56:36 +03:00
if (dx == 0) {
2010-07-31 06:52:47 +04:00
/* vertical */
for (i = 0; i < dy; i++) {
point32(im, x0, y0, ink);
y0 += ys;
}
2020-05-10 12:56:36 +03:00
} else if (dy == 0) {
2010-07-31 06:52:47 +04:00
/* horizontal */
for (i = 0; i < dx; i++) {
point32(im, x0, y0, ink);
x0 += xs;
}
2020-05-10 12:56:36 +03:00
} else if (dx > dy) {
2010-07-31 06:52:47 +04:00
/* bresenham, horizontal slope */
n = dx;
dy += dy;
e = dy - dx;
dx += dx;
for (i = 0; i < n; i++) {
point32(im, x0, y0, ink);
if (e >= 0) {
y0 += ys;
e -= dx;
}
e += dy;
x0 += xs;
}
} else {
/* bresenham, vertical slope */
n = dy;
dx += dx;
e = dx - dy;
dy += dy;
for (i = 0; i < n; i++) {
point32(im, x0, y0, ink);
if (e >= 0) {
x0 += xs;
e -= dy;
}
e += dx;
y0 += ys;
}
}
}
static inline void
line32rgba(Imaging im, int x0, int y0, int x1, int y1, int ink)
{
int i, n, e;
int dx, dy;
int xs, ys;
/* normalize coordinates */
dx = x1-x0;
2020-05-10 12:56:36 +03:00
if (dx < 0) {
2010-07-31 06:52:47 +04:00
dx = -dx, xs = -1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
xs = 1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
dy = y1-y0;
2020-05-10 12:56:36 +03:00
if (dy < 0) {
2010-07-31 06:52:47 +04:00
dy = -dy, ys = -1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
ys = 1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
n = (dx > dy) ? dx : dy;
2020-05-10 12:56:36 +03:00
if (dx == 0) {
2010-07-31 06:52:47 +04:00
/* vertical */
for (i = 0; i < dy; i++) {
point32rgba(im, x0, y0, ink);
y0 += ys;
}
2020-05-10 12:56:36 +03:00
} else if (dy == 0) {
2010-07-31 06:52:47 +04:00
/* horizontal */
for (i = 0; i < dx; i++) {
point32rgba(im, x0, y0, ink);
x0 += xs;
}
2020-05-10 12:56:36 +03:00
} else if (dx > dy) {
2010-07-31 06:52:47 +04:00
/* bresenham, horizontal slope */
n = dx;
dy += dy;
e = dy - dx;
dx += dx;
for (i = 0; i < n; i++) {
point32rgba(im, x0, y0, ink);
if (e >= 0) {
y0 += ys;
e -= dx;
}
e += dy;
x0 += xs;
}
} else {
/* bresenham, vertical slope */
n = dy;
dx += dx;
e = dx - dy;
dy += dy;
for (i = 0; i < n; i++) {
point32rgba(im, x0, y0, ink);
if (e >= 0) {
x0 += xs;
e -= dy;
}
e += dx;
y0 += ys;
}
}
}
static int
x_cmp(const void *x0, const void *x1)
{
float diff = *((float*)x0) - *((float*)x1);
2020-05-10 12:56:36 +03:00
if (diff < 0) {
2010-07-31 06:52:47 +04:00
return -1;
2020-05-10 12:56:36 +03:00
} else if (diff > 0) {
2010-07-31 06:52:47 +04:00
return 1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
return 0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
2020-01-05 09:43:51 +03:00
static void
draw_horizontal_lines(Imaging im, int n, Edge *e, int ink, int *x_pos, int y, hline_handler hline)
{
int i;
for (i = 0; i < n; i++) {
if (e[i].ymin == y && e[i].ymin == e[i].ymax) {
int xmax;
int xmin = e[i].xmin;
if (*x_pos < xmin) {
// Line would be after the current position
continue;
}
xmax = e[i].xmax;
if (*x_pos > xmin) {
// Line would be partway through x_pos, so increase the starting point
xmin = *x_pos;
if (xmax < xmin) {
// Line would now end before it started
continue;
}
}
(*hline)(im, xmin, e[i].ymin, xmax, ink);
*x_pos = xmax+1;
}
}
}
/*
* Filled polygon draw function using scan line algorithm.
*/
2010-07-31 06:52:47 +04:00
static inline int
polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill,
hline_handler hline)
2010-07-31 06:52:47 +04:00
{
Edge** edge_table;
float* xx;
int edge_count = 0;
int ymin = im->ysize - 1;
int ymax = 0;
int i;
if (n <= 0) {
2010-07-31 06:52:47 +04:00
return 0;
}
/* Initialize the edge table and find polygon boundaries */
2016-03-16 14:47:18 +03:00
/* malloc check ok, using calloc */
edge_table = calloc(n, sizeof(Edge*));
if (!edge_table) {
2010-07-31 06:52:47 +04:00
return -1;
}
for (i = 0; i < n; i++) {
if (ymin > e[i].ymin) {
ymin = e[i].ymin;
}
if (ymax < e[i].ymax) {
ymax = e[i].ymax;
}
2020-06-07 05:07:13 +03:00
if (e[i].ymin == e[i].ymax) {
continue;
}
edge_table[edge_count++] = (e + i);
}
if (ymin < 0) {
ymin = 0;
}
2015-06-05 04:16:33 +03:00
if (ymax > im->ysize) {
ymax = im->ysize;
}
2010-07-31 06:52:47 +04:00
/* Process the edge table with a scan line searching for intersections */
2016-07-01 14:27:01 +03:00
/* malloc check ok, using calloc */
2016-03-16 14:47:18 +03:00
xx = calloc(edge_count * 2, sizeof(float));
2014-04-04 16:02:36 +04:00
if (!xx) {
free(edge_table);
2014-04-04 16:02:36 +04:00
return -1;
}
for (; ymin <= ymax; ymin++) {
int j = 0;
2020-01-05 09:43:51 +03:00
int x_pos = 0;
for (i = 0; i < edge_count; i++) {
Edge* current = edge_table[i];
if (ymin >= current->ymin && ymin <= current->ymax) {
xx[j++] = (ymin - current->y0) * current->dx + current->x0;
}
/* Needed to draw consistent polygons */
if (ymin == current->ymax && ymin < ymax) {
xx[j] = xx[j - 1];
j++;
2010-07-31 06:52:47 +04:00
}
}
qsort(xx, j, sizeof(float), x_cmp);
for (i = 1; i < j; i += 2) {
2020-01-05 09:43:51 +03:00
int x_end = ROUND_DOWN(xx[i]);
if (x_end < x_pos) {
// Line would be before the current position
continue;
}
draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);
if (x_end < x_pos) {
// Line would be before the current position
continue;
}
int x_start = ROUND_UP(xx[i-1]);
if (x_pos > x_start) {
// Line would be partway through x_pos, so increase the starting point
x_start = x_pos;
if (x_end < x_start) {
// Line would now end before it started
continue;
}
}
(*hline)(im, x_start, ymin, x_end, ink);
x_pos = x_end+1;
2010-07-31 06:52:47 +04:00
}
2020-01-05 09:43:51 +03:00
draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);
2010-07-31 06:52:47 +04:00
}
free(xx);
free(edge_table);
2010-07-31 06:52:47 +04:00
return 0;
}
static inline int
polygon8(Imaging im, int n, Edge *e, int ink, int eofill)
2010-07-31 06:52:47 +04:00
{
return polygon_generic(im, n, e, ink, eofill, hline8);
}
2010-07-31 06:52:47 +04:00
static inline int
polygon32(Imaging im, int n, Edge *e, int ink, int eofill)
{
return polygon_generic(im, n, e, ink, eofill, hline32);
2010-07-31 06:52:47 +04:00
}
static inline int
polygon32rgba(Imaging im, int n, Edge *e, int ink, int eofill)
{
return polygon_generic(im, n, e, ink, eofill, hline32rgba);
2010-07-31 06:52:47 +04:00
}
static inline void
add_edge(Edge *e, int x0, int y0, int x1, int y1)
{
/* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */
2020-05-10 12:56:36 +03:00
if (x0 <= x1) {
2010-07-31 06:52:47 +04:00
e->xmin = x0, e->xmax = x1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
e->xmin = x1, e->xmax = x0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (y0 <= y1) {
2010-07-31 06:52:47 +04:00
e->ymin = y0, e->ymax = y1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
e->ymin = y1, e->ymax = y0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (y0 == y1) {
e->d = 0;
e->dx = 0.0;
} else {
e->dx = ((float)(x1-x0)) / (y1-y0);
2020-05-10 12:56:36 +03:00
if (y0 == e->ymin) {
2010-07-31 06:52:47 +04:00
e->d = 1;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
e->d = -1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
e->x0 = x0;
e->y0 = y0;
}
typedef struct {
void (*point)(Imaging im, int x, int y, int ink);
void (*hline)(Imaging im, int x0, int y0, int x1, int ink);
void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink);
int (*polygon)(Imaging im, int n, Edge *e, int ink, int eofill);
} DRAW;
DRAW draw8 = { point8, hline8, line8, polygon8 };
DRAW draw32 = { point32, hline32, line32, polygon32 };
DRAW draw32rgba = { point32rgba, hline32rgba, line32rgba, polygon32rgba };
/* -------------------------------------------------------------------- */
/* Interface */
/* -------------------------------------------------------------------- */
#define DRAWINIT()\
if (im->image8) {\
draw = &draw8;\
ink = INK8(ink_);\
} else {\
draw = (op) ? &draw32rgba : &draw32; \
memcpy(&ink, ink_, sizeof(ink)); \
2010-07-31 06:52:47 +04:00
}
int
ImagingDrawPoint(Imaging im, int x0, int y0, const void* ink_, int op)
{
DRAW* draw;
INT32 ink;
DRAWINIT();
draw->point(im, x0, y0, ink);
return 0;
}
int
ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink_,
int op)
{
DRAW* draw;
INT32 ink;
DRAWINIT();
draw->line(im, x0, y0, x1, y1, ink);
return 0;
}
int
ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1,
const void* ink_, int width, int op)
{
DRAW* draw;
INT32 ink;
int dx, dy;
double big_hypotenuse, small_hypotenuse, ratio_max, ratio_min;
int dxmin, dxmax, dymin, dymax;
Edge e[4];
2010-07-31 06:52:47 +04:00
DRAWINIT();
dx = x1-x0;
dy = y1-y0;
2010-07-31 06:52:47 +04:00
if (dx == 0 && dy == 0) {
draw->point(im, x0, y0, ink);
return 0;
}
big_hypotenuse = sqrt((double) (dx*dx + dy*dy));
small_hypotenuse = (width - 1) / 2.0;
ratio_max = ROUND_UP(small_hypotenuse) / big_hypotenuse;
ratio_min = ROUND_DOWN(small_hypotenuse) / big_hypotenuse;
dxmin = ROUND_DOWN(ratio_min * dy);
dxmax = ROUND_DOWN(ratio_max * dy);
dymin = ROUND_DOWN(ratio_min * dx);
dymax = ROUND_DOWN(ratio_max * dx);
{
int vertices[4][2] = {
{x0 - dxmin, y0 + dymax},
{x1 - dxmin, y1 + dymax},
{x1 + dxmax, y1 - dymin},
{x0 + dxmax, y0 - dymin}
};
add_edge(e+0, vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1]);
add_edge(e+1, vertices[1][0], vertices[1][1], vertices[2][0], vertices[2][1]);
add_edge(e+2, vertices[2][0], vertices[2][1], vertices[3][0], vertices[3][1]);
add_edge(e+3, vertices[3][0], vertices[3][1], vertices[0][0], vertices[0][1]);
draw->polygon(im, 4, e, ink, 0);
}
2010-07-31 06:52:47 +04:00
return 0;
}
int
ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1,
2018-04-12 18:15:27 +03:00
const void* ink_, int fill, int width, int op)
2010-07-31 06:52:47 +04:00
{
2018-04-12 18:15:27 +03:00
int i;
2010-07-31 06:52:47 +04:00
int y;
int tmp;
DRAW* draw;
INT32 ink;
DRAWINIT();
2020-05-10 12:56:36 +03:00
if (y0 > y1) {
2010-07-31 06:52:47 +04:00
tmp = y0, y0 = y1, y1 = tmp;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (fill) {
2020-05-10 12:56:36 +03:00
if (y0 < 0) {
2010-07-31 06:52:47 +04:00
y0 = 0;
2020-05-10 12:56:36 +03:00
} else if (y0 >= im->ysize) {
2010-07-31 06:52:47 +04:00
return 0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (y1 < 0) {
2010-07-31 06:52:47 +04:00
return 0;
2020-05-10 12:56:36 +03:00
} else if (y1 > im->ysize) {
2010-07-31 06:52:47 +04:00
y1 = im->ysize;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
for (y = y0; y <= y1; y++) {
2010-07-31 06:52:47 +04:00
draw->hline(im, x0, y, x1, ink);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
} else {
/* outline */
2018-04-12 18:15:27 +03:00
if (width == 0) {
width = 1;
}
for (i = 0; i < width; i++) {
draw->hline(im, x0, y0+i, x1, ink);
draw->hline(im, x0, y1-i, x1, ink);
draw->line(im, x1-i, y0, x1-i, y1, ink);
draw->line(im, x0+i, y1, x0+i, y0, ink);
}
2010-07-31 06:52:47 +04:00
}
return 0;
}
int
ImagingDrawPolygon(Imaging im, int count, int* xy, const void* ink_,
int fill, int op)
{
int i, n;
DRAW* draw;
INT32 ink;
2020-05-10 12:56:36 +03:00
if (count <= 0) {
2010-07-31 06:52:47 +04:00
return 0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
DRAWINIT();
if (fill) {
/* Build edge list */
2016-03-16 14:47:18 +03:00
/* malloc check ok, using calloc */
Edge* e = calloc(count, sizeof(Edge));
2010-07-31 06:52:47 +04:00
if (!e) {
(void) ImagingError_MemoryError();
return -1;
}
2020-05-10 12:56:36 +03:00
for (i = n = 0; i < count-1; i++) {
2010-07-31 06:52:47 +04:00
add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3]);
2020-05-10 12:56:36 +03:00
}
if (xy[i+i] != xy[0] || xy[i+i+1] != xy[1]) {
2010-07-31 06:52:47 +04:00
add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[0], xy[1]);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
draw->polygon(im, n, e, ink, 0);
free(e);
} else {
/* Outline */
2020-05-10 12:56:36 +03:00
for (i = 0; i < count-1; i++) {
2010-07-31 06:52:47 +04:00
draw->line(im, xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3], ink);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
draw->line(im, xy[i+i], xy[i+i+1], xy[0], xy[1], ink);
}
2010-07-31 06:52:47 +04:00
return 0;
}
int
ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, const void* ink,
int op)
{
return ImagingFill2(
im, ink, bitmap,
x0, y0, x0 + bitmap->xsize, y0 + bitmap->ysize
);
}
/* -------------------------------------------------------------------- */
/* standard shapes */
#define ARC 0
#define CHORD 1
#define PIESLICE 2
static void
ellipsePoint(int cx, int cy, int w, int h,
float i, int *x, int *y)
{
float i_cos, i_sin;
float x_f, y_f;
double modf_int;
i_cos = cos(i*M_PI/180);
i_sin = sin(i*M_PI/180);
x_f = (i_cos * w/2) + cx;
y_f = (i_sin * h/2) + cy;
if (modf(x_f, &modf_int) == 0.5) {
*x = i_cos > 0 ? FLOOR(x_f) : CEIL(x_f);
} else {
*x = FLOOR(x_f + 0.5);
}
if (modf(y_f, &modf_int) == 0.5) {
*y = i_sin > 0 ? FLOOR(y_f) : CEIL(y_f);
} else {
*y = FLOOR(y_f + 0.5);
}
}
2010-07-31 06:52:47 +04:00
static int
ellipse(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink_, int fill,
int width, int mode, int op)
2010-07-31 06:52:47 +04:00
{
float i;
2019-05-03 16:37:37 +03:00
int inner;
int n;
2019-05-03 16:37:37 +03:00
int maxEdgeCount;
2010-07-31 06:52:47 +04:00
int w, h;
2019-05-03 16:37:37 +03:00
int x, y;
int cx, cy;
int lx = 0, ly = 0;
int sx = 0, sy = 0;
int lx_inner = 0, ly_inner = 0;
int sx_inner = 0, sy_inner = 0;
2010-07-31 06:52:47 +04:00
DRAW* draw;
INT32 ink;
2019-05-03 16:37:37 +03:00
Edge* e;
2010-07-31 06:52:47 +04:00
DRAWINIT();
2020-05-10 12:56:36 +03:00
while (end < start) {
2019-05-03 16:37:37 +03:00
end += 360;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2019-05-03 16:37:37 +03:00
if (end - start > 360) {
// no need to go in loops
end = start + 361;
}
2010-07-31 06:52:47 +04:00
2019-05-03 16:37:37 +03:00
w = x1 - x0;
h = y1 - y0;
2020-05-10 12:56:36 +03:00
if (w <= 0 || h <= 0) {
2019-05-03 16:37:37 +03:00
return 0;
2020-05-10 12:56:36 +03:00
}
2019-05-03 16:37:37 +03:00
cx = (x0 + x1) / 2;
cy = (y0 + y1) / 2;
2010-07-31 06:52:47 +04:00
2019-05-03 16:37:37 +03:00
if (!fill && width <= 1) {
for (i = start; i < end+1; i++) {
if (i > end) {
i = end;
}
ellipsePoint(cx, cy, w, h, i, &x, &y);
2020-05-10 12:56:36 +03:00
if (i != start) {
2019-05-03 16:37:37 +03:00
draw->line(im, lx, ly, x, y, ink);
2020-05-10 12:56:36 +03:00
} else {
2019-05-03 16:37:37 +03:00
sx = x, sy = y;
2020-05-10 12:56:36 +03:00
}
2019-05-03 16:37:37 +03:00
lx = x, ly = y;
}
2019-05-03 16:37:37 +03:00
if (i != start) {
if (mode == PIESLICE) {
if (x != cx || y != cy) {
draw->line(im, x, y, cx, cy, ink);
draw->line(im, cx, cy, sx, sy, ink);
}
} else if (mode == CHORD) {
2020-05-10 12:56:36 +03:00
if (x != sx || y != sy) {
2019-05-03 16:37:37 +03:00
draw->line(im, x, y, sx, sy, ink);
2020-05-10 12:56:36 +03:00
}
2019-05-03 16:37:37 +03:00
}
2010-07-31 06:52:47 +04:00
}
2019-05-03 16:37:37 +03:00
} else {
inner = (mode == ARC || !fill) ? 1 : 0;
2010-07-31 06:52:47 +04:00
2019-05-03 16:37:37 +03:00
// Build edge list
// malloc check UNDONE, FLOAT?
maxEdgeCount = ceil(end - start);
2019-05-03 16:37:37 +03:00
if (inner) {
maxEdgeCount *= 2;
}
maxEdgeCount += 3;
e = calloc(maxEdgeCount, sizeof(Edge));
if (!e) {
ImagingError_MemoryError();
return -1;
}
2010-07-31 06:52:47 +04:00
2019-05-03 16:37:37 +03:00
// Outer circle
n = 0;
for (i = start; i < end+1; i++) {
if (i > end) {
i = end;
}
2019-05-03 16:37:37 +03:00
ellipsePoint(cx, cy, w, h, i, &x, &y);
if (i == start) {
sx = x, sy = y;
} else {
add_edge(&e[n++], lx, ly, x, y);
2010-07-31 06:52:47 +04:00
}
2019-05-03 16:37:37 +03:00
lx = x, ly = y;
}
2020-05-11 00:46:12 +03:00
if (n == 0) {
2019-05-03 16:37:37 +03:00
return 0;
2020-05-11 00:46:12 +03:00
}
2010-07-31 06:52:47 +04:00
2019-05-03 16:37:37 +03:00
if (inner) {
// Inner circle
x0 += width - 1;
y0 += width - 1;
x1 -= width - 1;
y1 -= width - 1;
w = x1 - x0;
h = y1 - y0;
if (w <= 0 || h <= 0) {
// ARC with no gap in the middle is a PIESLICE
mode = PIESLICE;
inner = 0;
} else {
for (i = start; i < end+1; i++) {
if (i > end) {
i = end;
}
2019-05-03 16:37:37 +03:00
ellipsePoint(cx, cy, w, h, i, &x, &y);
2020-05-10 12:56:36 +03:00
if (i == start) {
2019-05-03 16:37:37 +03:00
sx_inner = x, sy_inner = y;
2020-05-10 12:56:36 +03:00
} else {
2019-05-03 16:37:37 +03:00
add_edge(&e[n++], lx_inner, ly_inner, x, y);
2020-05-10 12:56:36 +03:00
}
2019-05-03 16:37:37 +03:00
lx_inner = x, ly_inner = y;
}
}
2019-05-03 16:37:37 +03:00
}
2010-07-31 06:52:47 +04:00
2019-05-03 16:37:37 +03:00
if (end - start < 360) {
// Close polygon
if (mode == PIESLICE) {
if (x != cx || y != cy) {
add_edge(&e[n++], sx, sy, cx, cy);
add_edge(&e[n++], cx, cy, lx, ly);
if (inner) {
ImagingDrawWideLine(im, sx, sy, cx, cy, &ink, width, op);
ImagingDrawWideLine(im, cx, cy, lx, ly, &ink, width, op);
}
2010-07-31 06:52:47 +04:00
}
2019-05-03 16:37:37 +03:00
} else if (mode == CHORD) {
add_edge(&e[n++], sx, sy, lx, ly);
if (inner) {
add_edge(&e[n++], sx_inner, sy_inner, lx_inner, ly_inner);
}
} else if (mode == ARC) {
add_edge(&e[n++], sx, sy, sx_inner, sy_inner);
add_edge(&e[n++], lx, ly, lx_inner, ly_inner);
2010-07-31 06:52:47 +04:00
}
}
2019-05-03 16:37:37 +03:00
draw->polygon(im, n, e, ink, 0);
free(e);
2010-07-31 06:52:47 +04:00
}
2019-05-03 16:37:37 +03:00
2010-07-31 06:52:47 +04:00
return 0;
}
int
ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int width, int op)
2010-07-31 06:52:47 +04:00
{
return ellipse(im, x0, y0, x1, y1, start, end, ink, 0, width, ARC, op);
2010-07-31 06:52:47 +04:00
}
int
ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int fill,
int width, int op)
2010-07-31 06:52:47 +04:00
{
return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, width, CHORD, op);
2010-07-31 06:52:47 +04:00
}
int
ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1,
const void* ink, int fill, int width, int op)
2010-07-31 06:52:47 +04:00
{
return ellipse(im, x0, y0, x1, y1, 0, 360, ink, fill, width, CHORD, op);
2010-07-31 06:52:47 +04:00
}
int
ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1,
float start, float end, const void* ink, int fill,
int width, int op)
2010-07-31 06:52:47 +04:00
{
return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, width, PIESLICE, op);
2010-07-31 06:52:47 +04:00
}
/* -------------------------------------------------------------------- */
/* experimental level 2 ("arrow") graphics stuff. this implements
portions of the arrow api on top of the Edge structure. the
semantics are ok, except that "curve" flattens the bezier curves by
itself */
struct ImagingOutlineInstance {
float x0, y0;
float x, y;
int count;
Edge *edges;
int size;
};
ImagingOutline
ImagingOutlineNew(void)
{
ImagingOutline outline;
outline = calloc(1, sizeof(struct ImagingOutlineInstance));
2020-05-10 12:56:36 +03:00
if (!outline) {
2010-07-31 06:52:47 +04:00
return (ImagingOutline) ImagingError_MemoryError();
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
outline->edges = NULL;
outline->count = outline->size = 0;
ImagingOutlineMove(outline, 0, 0);
return outline;
}
void
ImagingOutlineDelete(ImagingOutline outline)
{
2020-05-10 12:56:36 +03:00
if (!outline) {
2010-07-31 06:52:47 +04:00
return;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (outline->edges) {
2010-07-31 06:52:47 +04:00
free(outline->edges);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
free(outline);
}
static Edge*
allocate(ImagingOutline outline, int extra)
{
Edge* e;
if (outline->count + extra > outline->size) {
/* expand outline buffer */
outline->size += extra + 25;
2016-03-16 19:01:25 +03:00
if (!outline->edges) {
2016-03-16 14:47:18 +03:00
/* malloc check ok, uses calloc for overflow */
e = calloc(outline->size, sizeof(Edge));
2016-03-16 19:01:25 +03:00
} else {
if (outline->size > INT_MAX / sizeof(Edge)) {
2016-03-16 19:01:25 +03:00
return NULL;
}
/* malloc check ok, overflow checked above */
2010-07-31 06:52:47 +04:00
e = realloc(outline->edges, outline->size * sizeof(Edge));
2016-03-16 19:01:25 +03:00
}
2020-05-10 12:56:36 +03:00
if (!e) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
outline->edges = e;
}
e = outline->edges + outline->count;
outline->count += extra;
return e;
}
int
ImagingOutlineMove(ImagingOutline outline, float x0, float y0)
{
outline->x = outline->x0 = x0;
outline->y = outline->y0 = y0;
return 0;
}
int
ImagingOutlineLine(ImagingOutline outline, float x1, float y1)
{
Edge* e;
e = allocate(outline, 1);
2020-05-10 12:56:36 +03:00
if (!e) {
2010-07-31 06:52:47 +04:00
return -1; /* out of memory */
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
add_edge(e, (int) outline->x, (int) outline->y, (int) x1, (int) y1);
outline->x = x1;
outline->y = y1;
2010-07-31 06:52:47 +04:00
return 0;
}
int
ImagingOutlineCurve(ImagingOutline outline, float x1, float y1,
float x2, float y2, float x3, float y3)
{
Edge* e;
int i;
float xo, yo;
#define STEPS 32
e = allocate(outline, STEPS);
2020-05-10 12:56:36 +03:00
if (!e) {
2010-07-31 06:52:47 +04:00
return -1; /* out of memory */
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
xo = outline->x;
yo = outline->y;
/* flatten the bezier segment */
for (i = 1; i <= STEPS; i++) {
float t = ((float) i) / STEPS;
float t2 = t*t;
float t3 = t2*t;
float u = 1.0F - t;
float u2 = u*u;
float u3 = u2*u;
float x = outline->x*u3 + 3*(x1*t*u2 + x2*t2*u) + x3*t3 + 0.5;
float y = outline->y*u3 + 3*(y1*t*u2 + y2*t2*u) + y3*t3 + 0.5;
add_edge(e++, xo, yo, (int) x, (int) y);
xo = x, yo = y;
}
outline->x = xo;
outline->y = yo;
2010-07-31 06:52:47 +04:00
return 0;
}
int
ImagingOutlineClose(ImagingOutline outline)
{
2020-05-10 12:56:36 +03:00
if (outline->x == outline->x0 && outline->y == outline->y0) {
2010-07-31 06:52:47 +04:00
return 0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return ImagingOutlineLine(outline, outline->x0, outline->y0);
}
int
ImagingOutlineTransform(ImagingOutline outline, double a[6])
{
Edge *eIn;
Edge *eOut;
int i, n;
int x0, y0, x1, y1;
int X0, Y0, X1, Y1;
2010-07-31 06:52:47 +04:00
double a0 = a[0]; double a1 = a[1]; double a2 = a[2];
double a3 = a[3]; double a4 = a[4]; double a5 = a[5];
eIn = outline->edges;
n = outline->count;
2010-07-31 06:52:47 +04:00
/* FIXME: ugly! */
outline->edges = NULL;
outline->count = outline->size = 0;
eOut = allocate(outline, n);
if (!eOut) {
outline->edges = eIn;
outline->count = outline->size = n;
ImagingError_MemoryError();
return -1;
}
2010-07-31 06:52:47 +04:00
for (i = 0; i < n; i++) {
2010-07-31 06:52:47 +04:00
x0 = eIn->x0;
y0 = eIn->y0;
2010-07-31 06:52:47 +04:00
/* FIXME: ouch! */
2020-05-10 12:56:36 +03:00
if (eIn->x0 == eIn->xmin) {
2010-07-31 06:52:47 +04:00
x1 = eIn->xmax;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
x1 = eIn->xmin;
2020-05-10 12:56:36 +03:00
}
if (eIn->y0 == eIn->ymin) {
2010-07-31 06:52:47 +04:00
y1 = eIn->ymax;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
y1 = eIn->ymin;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
/* full moon tonight! if this doesn't work, you may need to
upgrade your compiler (make sure you have the right service
pack) */
X0 = (int) (a0*x0 + a1*y0 + a2);
Y0 = (int) (a3*x0 + a4*y0 + a5);
X1 = (int) (a0*x1 + a1*y1 + a2);
Y1 = (int) (a3*x1 + a4*y1 + a5);
add_edge(eOut, X0, Y0, X1, Y1);
eIn++;
eOut++;
}
free(eIn);
return 0;
}
int
ImagingDrawOutline(Imaging im, ImagingOutline outline, const void* ink_,
int fill, int op)
{
DRAW* draw;
INT32 ink;
DRAWINIT();
draw->polygon(im, outline->count, outline->edges, ink, 0);
return 0;
}