mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 20:38:08 +01:00
When defining enums as types instead of simple enums allows to use these types at the places where the corresponding enum values are expected, then allowing the compiler to check that, potentially reporting incorrect use of values to the user. This patch adds the types for the drawing operations used by the functions who drawing shapes into an RImage. Signed-off-by: Christophe CURIS <christophe.curis@free.fr>
503 lines
11 KiB
C
503 lines
11 KiB
C
/* draw.c - pixel plotting, line drawing
|
|
*
|
|
* Raster graphics library
|
|
*
|
|
* Copyright (c) 1998-2003 Dan Pascu
|
|
* Copyright (c) 2000-2003 Alfredo K. Kojima
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#include "wraster.h"
|
|
|
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
|
|
/*
|
|
* Returns the color of the pixel at coordinates (x, y) in "color".
|
|
*/
|
|
Bool RGetPixel(RImage * image, int x, int y, RColor * color)
|
|
{
|
|
int ofs;
|
|
|
|
assert(image != NULL);
|
|
if (x < 0 || x >= image->width || y < 0 || y >= image->height)
|
|
return False;
|
|
|
|
if (image->format == RRGBAFormat) {
|
|
ofs = (y * image->width + x) * 4;
|
|
color->red = image->data[ofs++];
|
|
color->green = image->data[ofs++];
|
|
color->blue = image->data[ofs++];
|
|
color->alpha = image->data[ofs];
|
|
} else {
|
|
ofs = (y * image->width + x) * 3;
|
|
color->red = image->data[ofs++];
|
|
color->green = image->data[ofs++];
|
|
color->blue = image->data[ofs];
|
|
/* If the image does not have alpha channel, we consider alpha 255 */
|
|
color->alpha = 255;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
void RPutPixel(RImage *image, int x, int y, const RColor *color)
|
|
{
|
|
unsigned char *ptr;
|
|
|
|
assert(image != NULL);
|
|
assert(color != NULL);
|
|
if (x < 0 || x >= image->width || y < 0 || y >= image->height)
|
|
return;
|
|
|
|
if (image->format == RRGBAFormat) {
|
|
ptr = image->data + (y * image->width + x) * 4;
|
|
} else {
|
|
ptr = image->data + (y * image->width + x) * 3;
|
|
}
|
|
|
|
if (color->alpha == 255) {
|
|
*ptr++ = color->red;
|
|
*ptr++ = color->green;
|
|
*ptr++ = color->blue;
|
|
if (image->format == RRGBAFormat) {
|
|
*ptr = 255;
|
|
}
|
|
} else {
|
|
register int alpha, nalpha, r, g, b;
|
|
|
|
r = color->red;
|
|
g = color->green;
|
|
b = color->blue;
|
|
alpha = color->alpha;
|
|
nalpha = 255 - alpha;
|
|
|
|
*ptr = (((int)*ptr * nalpha) + (r * alpha)) / 256;
|
|
ptr++;
|
|
*ptr = (((int)*ptr * nalpha) + (g * alpha)) / 256;
|
|
ptr++;
|
|
*ptr = (((int)*ptr * nalpha) + (b * alpha)) / 256;
|
|
ptr++;
|
|
if (image->format == RRGBAFormat) {
|
|
*ptr = alpha + ((int)*ptr * nalpha) / 256;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void operatePixel(RImage *image, int ofs, RPixelOperation operation, const RColor *color)
|
|
{
|
|
unsigned char *sr, *sg, *sb, *sa;
|
|
register int alpha, nalpha, tmp;
|
|
int hasAlpha = image->format == RRGBAFormat;
|
|
|
|
alpha = color->alpha;
|
|
nalpha = 255 - alpha;
|
|
|
|
sr = image->data + ofs * (hasAlpha ? 4 : 3);
|
|
sg = image->data + ofs * (hasAlpha ? 4 : 3) + 1;
|
|
sb = image->data + ofs * (hasAlpha ? 4 : 3) + 2;
|
|
sa = image->data + ofs * (hasAlpha ? 4 : 3) + 3;
|
|
|
|
switch (operation) {
|
|
case RClearOperation:
|
|
*sr = 0;
|
|
*sg = 0;
|
|
*sb = 0;
|
|
if (hasAlpha)
|
|
*sa = 0;
|
|
break;
|
|
case RCopyOperation:
|
|
*sr = color->red;
|
|
*sg = color->green;
|
|
*sb = color->blue;
|
|
if (hasAlpha)
|
|
*sa = color->alpha;
|
|
break;
|
|
case RNormalOperation:
|
|
if (color->alpha == 255) {
|
|
*sr = color->red;
|
|
*sg = color->green;
|
|
*sb = color->blue;
|
|
if (hasAlpha)
|
|
*sa = 255;
|
|
} else {
|
|
*sr = (((int)*sr * nalpha) + ((int)color->red * alpha)) / 256;
|
|
*sg = (((int)*sg * nalpha) + ((int)color->green * alpha)) / 256;
|
|
*sb = (((int)*sb * nalpha) + ((int)color->blue * alpha)) / 256;
|
|
*sa = alpha + ((int)*sa * nalpha) / 256;
|
|
}
|
|
break;
|
|
case RAddOperation:
|
|
tmp = color->red + *sr;
|
|
*sr = MIN(255, tmp);
|
|
tmp = color->green + *sg;
|
|
*sg = MIN(255, tmp);
|
|
tmp = color->blue + *sb;
|
|
*sb = MIN(255, tmp);
|
|
if (hasAlpha)
|
|
*sa = MIN(*sa, color->alpha);
|
|
break;
|
|
case RSubtractOperation:
|
|
tmp = *sr - color->red;
|
|
*sr = MAX(0, tmp);
|
|
tmp = *sg - color->green;
|
|
*sg = MAX(0, tmp);
|
|
tmp = *sb - color->blue;
|
|
*sb = MAX(0, tmp);
|
|
if (hasAlpha)
|
|
*sa = MIN(*sa, color->alpha);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ROperatePixel(RImage *image, RPixelOperation operation, int x, int y, const RColor *color)
|
|
{
|
|
int ofs;
|
|
|
|
assert(image != NULL);
|
|
assert(color != NULL);
|
|
assert(x >= 0 && x < image->width);
|
|
assert(y >= 0 && y < image->height);
|
|
|
|
ofs = y * image->width + x;
|
|
|
|
operatePixel(image, ofs, operation, color);
|
|
}
|
|
|
|
void RPutPixels(RImage *image, const RPoint *points, int npoints, RCoordinatesMode mode, const RColor *color)
|
|
{
|
|
register int x, y, i;
|
|
|
|
assert(image != NULL);
|
|
assert(points != NULL);
|
|
|
|
x = y = 0;
|
|
|
|
for (i = 0; i < npoints; i++) {
|
|
if (mode == RAbsoluteCoordinates) {
|
|
x = points[i].x;
|
|
y = points[i].y;
|
|
} else {
|
|
x += points[i].x;
|
|
y += points[i].y;
|
|
}
|
|
RPutPixel(image, x, y, color);
|
|
}
|
|
}
|
|
|
|
void ROperatePixels(RImage *image, RPixelOperation operation,
|
|
const RPoint *points, int npoints, RCoordinatesMode mode,
|
|
const RColor *color)
|
|
{
|
|
register int x, y, i;
|
|
|
|
assert(image != NULL);
|
|
assert(points != NULL);
|
|
|
|
x = y = 0;
|
|
|
|
for (i = 0; i < npoints; i++) {
|
|
if (mode == RAbsoluteCoordinates) {
|
|
x = points[i].x;
|
|
y = points[i].y;
|
|
} else {
|
|
x += points[i].x;
|
|
y += points[i].y;
|
|
}
|
|
ROperatePixel(image, operation, x, y, color);
|
|
}
|
|
}
|
|
|
|
static Bool clipLineInRectangle(int xmin, int ymin, int xmax, int ymax, int *x1, int *y1, int *x2, int *y2)
|
|
{
|
|
#define TOP (1<<0)
|
|
#define BOT (1<<1)
|
|
#define LEF (1<<2)
|
|
#define RIG (1<<3)
|
|
#define CHECK_OUT(X,Y) (((Y) > ymax ? TOP : ((Y) < ymin ? BOT : 0))\
|
|
| ((X) > xmax ? RIG : ((X) < xmin ? LEF : 0)))
|
|
|
|
int ocode1, ocode2, ocode;
|
|
int accept = 0;
|
|
int x, y;
|
|
|
|
ocode1 = CHECK_OUT(*x1, *y1);
|
|
ocode2 = CHECK_OUT(*x2, *y2);
|
|
|
|
for (;;) {
|
|
if (!ocode1 && !ocode2) { /* completely inside */
|
|
accept = 1;
|
|
break;
|
|
} else if (ocode1 & ocode2) {
|
|
break;
|
|
}
|
|
|
|
if (ocode1)
|
|
ocode = ocode1;
|
|
else
|
|
ocode = ocode2;
|
|
|
|
if (ocode & TOP) {
|
|
x = *x1 + (*x2 - *x1) * (ymax - *y1) / (*y2 - *y1);
|
|
y = ymax;
|
|
} else if (ocode & BOT) {
|
|
x = *x1 + (*x2 - *x1) * (ymin - *y1) / (*y2 - *y1);
|
|
y = ymin;
|
|
} else if (ocode & RIG) {
|
|
y = *y1 + (*y2 - *y1) * (xmax - *x1) / (*x2 - *x1);
|
|
x = xmax;
|
|
} else { /* //if (ocode & LEF) { */
|
|
y = *y1 + (*y2 - *y1) * (xmax - *x1) / (*x2 - *x1);
|
|
x = xmin;
|
|
}
|
|
|
|
if (ocode == ocode1) {
|
|
*x1 = x;
|
|
*y1 = y;
|
|
ocode1 = CHECK_OUT(x, y);
|
|
} else {
|
|
*x2 = x;
|
|
*y2 = y;
|
|
ocode2 = CHECK_OUT(x, y);
|
|
}
|
|
}
|
|
|
|
return accept;
|
|
}
|
|
|
|
/*
|
|
* This routine is a generic drawing routine, based on Bresenham's line
|
|
* drawing algorithm.
|
|
*/
|
|
static int genericLine(RImage *image, int x0, int y0, int x1, int y1, const RColor *color,
|
|
RPixelOperation operation, int polyline)
|
|
{
|
|
int i, err, du, dv, du2, dv2, uofs, vofs, last;
|
|
|
|
assert(image != NULL);
|
|
|
|
if (!clipLineInRectangle(0, 0, image->width - 1, image->height - 1, &x0, &y0, &x1, &y1))
|
|
return True;
|
|
|
|
if (x0 < x1) {
|
|
du = x1 - x0;
|
|
uofs = 1;
|
|
} else {
|
|
du = x0 - x1;
|
|
uofs = -1;
|
|
}
|
|
if (y0 < y1) {
|
|
dv = y1 - y0;
|
|
vofs = image->width;
|
|
} else {
|
|
dv = y0 - y1;
|
|
vofs = -image->width;
|
|
}
|
|
|
|
if (du < dv) {
|
|
/* Swap coordinates between them, so that always du>dv */
|
|
i = du;
|
|
du = dv;
|
|
dv = i;
|
|
i = uofs;
|
|
uofs = vofs;
|
|
vofs = i;
|
|
}
|
|
|
|
err = 0;
|
|
du2 = du << 1;
|
|
dv2 = dv << 1;
|
|
last = (polyline) ? du - 1 : du;
|
|
|
|
if (color->alpha == 255 || operation == RCopyOperation) {
|
|
unsigned char *ptr;
|
|
|
|
if (image->format == RRGBAFormat)
|
|
i = (y0 * image->width + x0) * 4;
|
|
else
|
|
i = (y0 * image->width + x0) * 3;
|
|
ptr = image->data + i;
|
|
|
|
for (i = 0; i <= last; i++) {
|
|
/* Draw the pixel */
|
|
*ptr = color->red;
|
|
*(ptr + 1) = color->green;
|
|
*(ptr + 2) = color->blue;
|
|
if (image->format == RRGBAFormat)
|
|
*(ptr + 3) = 255;
|
|
|
|
/* Compute error for NeXT Step */
|
|
err += dv2;
|
|
if (err >= du) {
|
|
if (image->format == RRGBAFormat)
|
|
ptr += vofs * 4;
|
|
else
|
|
ptr += vofs * 3;
|
|
err -= du2;
|
|
}
|
|
if (image->format == RRGBAFormat)
|
|
ptr += uofs * 4;
|
|
else
|
|
ptr += uofs * 3;
|
|
}
|
|
} else {
|
|
register int ofs = y0 * image->width + x0;
|
|
|
|
for (i = 0; i <= last; i++) {
|
|
/* Draw the pixel */
|
|
operatePixel(image, ofs, operation, color);
|
|
|
|
/* Compute error for NeXT Step */
|
|
err += dv2;
|
|
if (err >= du) {
|
|
ofs += vofs;
|
|
err -= du2;
|
|
}
|
|
ofs += uofs;
|
|
}
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
int RDrawLine(RImage * image, int x0, int y0, int x1, int y1, const RColor * color)
|
|
{
|
|
return genericLine(image, x0, y0, x1, y1, color, RNormalOperation, False);
|
|
}
|
|
|
|
int ROperateLine(RImage *image, RPixelOperation operation, int x0, int y0, int x1, int y1, const RColor *color)
|
|
{
|
|
return genericLine(image, x0, y0, x1, y1, color, operation, False);
|
|
}
|
|
|
|
void RDrawLines(RImage *image, const RPoint *points, int npoints, RCoordinatesMode mode, const RColor *color)
|
|
{
|
|
register int x1, y1, x2, y2, i;
|
|
|
|
assert(points != NULL);
|
|
|
|
if (npoints == 0)
|
|
return;
|
|
|
|
x1 = points[0].x;
|
|
y1 = points[0].y;
|
|
x2 = y2 = 0;
|
|
|
|
for (i = 1; i < npoints - 1; i++) {
|
|
if (mode == RAbsoluteCoordinates) {
|
|
x2 = points[i].x;
|
|
y2 = points[i].y;
|
|
} else {
|
|
x2 += points[i - 1].x;
|
|
y2 += points[i - 1].y;
|
|
}
|
|
/* Don't draw pixels at junction points twice */
|
|
genericLine(image, x1, y1, x2, y2, color, RNormalOperation, True);
|
|
x1 = x2;
|
|
y1 = y2;
|
|
}
|
|
i = npoints - 1; /* last point */
|
|
if (mode == RAbsoluteCoordinates) {
|
|
x2 = points[i].x;
|
|
y2 = points[i].y;
|
|
} else {
|
|
x2 += points[i - 1].x;
|
|
y2 += points[i - 1].y;
|
|
}
|
|
i = (points[0].x == x2 && points[0].y == y2 && npoints > 1);
|
|
genericLine(image, x1, y1, x2, y2, color, RNormalOperation, i);
|
|
}
|
|
|
|
void ROperateLines(RImage *image, RPixelOperation operation,
|
|
const RPoint *points, int npoints, RCoordinatesMode mode,
|
|
const RColor *color)
|
|
{
|
|
register int x1, y1, x2, y2, i;
|
|
|
|
assert(points != NULL);
|
|
|
|
if (npoints == 0)
|
|
return;
|
|
|
|
x1 = points[0].x;
|
|
y1 = points[0].y;
|
|
x2 = y2 = 0;
|
|
|
|
for (i = 1; i < npoints - 1; i++) {
|
|
if (mode == RAbsoluteCoordinates) {
|
|
x2 = points[i].x;
|
|
y2 = points[i].y;
|
|
} else {
|
|
x2 += points[i - 1].x;
|
|
y2 += points[i - 1].y;
|
|
}
|
|
/* Don't draw pixels at junction points twice */
|
|
genericLine(image, x1, y1, x2, y2, color, operation, True);
|
|
x1 = x2;
|
|
y1 = y2;
|
|
}
|
|
i = npoints - 1; /* last point */
|
|
if (mode == RAbsoluteCoordinates) {
|
|
x2 = points[i].x;
|
|
y2 = points[i].y;
|
|
} else {
|
|
x2 += points[i - 1].x;
|
|
y2 += points[i - 1].y;
|
|
}
|
|
i = (points[0].x == x2 && points[0].y == y2 && npoints > 1);
|
|
genericLine(image, x1, y1, x2, y2, color, operation, i);
|
|
}
|
|
|
|
void ROperateRectangle(RImage *image, RPixelOperation operation, int x0, int y0, int x1, int y1, const RColor *color)
|
|
{
|
|
int y;
|
|
|
|
for (y = y0; y <= y1; y++) {
|
|
genericLine(image, x0, y, x1, y, color, operation, False);
|
|
}
|
|
}
|
|
|
|
void RDrawSegments(RImage *image, const RSegment *segs, int nsegs, const RColor *color)
|
|
{
|
|
register int i;
|
|
|
|
assert(segs != NULL);
|
|
|
|
for (i = 0; i < nsegs; i++) {
|
|
genericLine(image, segs->x1, segs->y1, segs->x2, segs->y2, color, RNormalOperation, False);
|
|
segs++;
|
|
}
|
|
}
|
|
|
|
void ROperateSegments(RImage *image, RPixelOperation operation, const RSegment *segs, int nsegs, const RColor *color)
|
|
{
|
|
register int i;
|
|
|
|
assert(segs != NULL);
|
|
|
|
for (i = 0; i < nsegs; i++) {
|
|
genericLine(image, segs->x1, segs->y1, segs->x2, segs->y2, color, operation, False);
|
|
segs++;
|
|
}
|
|
}
|