/* 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #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, 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, int operation, 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, int operation, int x, int y, 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, RPoint * points, int npoints, int mode, 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, int operation, RPoint * points, int npoints, int mode, 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, RColor * color, int 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, RColor * color) { return genericLine(image, x0, y0, x1, y1, color, RNormalOperation, False); } int ROperateLine(RImage * image, int operation, int x0, int y0, int x1, int y1, RColor * color) { return genericLine(image, x0, y0, x1, y1, color, operation, False); } void RDrawLines(RImage * image, RPoint * points, int npoints, int mode, 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, int operation, RPoint * points, int npoints, int mode, 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 RDrawSegments(RImage * image, RSegment * segs, int nsegs, 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, int operation, RSegment * segs, int nsegs, 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++; } }