From 44d41b19b5e644fb22a6d6ff8402e9b3d45091b6 Mon Sep 17 00:00:00 2001 From: Olof S Kylander/GIMP Date: Tue, 7 Sep 1999 01:33:44 +0000 Subject: [PATCH] Okay I have committed the ugly airbrush now --- ChangeLog | 13 + app/Makefile.am | 6 +- app/airbrush_blob.c | 1797 +++++++++++++++++++++++++ app/airbrush_blob.h | 154 +++ app/pixmaps2.h | 32 + app/tools.c | 37 +- app/tools/airbrush_blob.c | 1797 +++++++++++++++++++++++++ app/tools/airbrush_blob.h | 154 +++ app/tools/tools.c | 37 +- app/tools/xinput_airbrush.c | 2448 +++++++++++++++++++++++++++++++++++ app/tools/xinput_airbrush.h | 30 + app/toolsF.h | 3 +- app/xinput_airbrush.c | 2448 +++++++++++++++++++++++++++++++++++ app/xinput_airbrush.h | 30 + 14 files changed, 8964 insertions(+), 22 deletions(-) create mode 100644 app/airbrush_blob.c create mode 100644 app/airbrush_blob.h create mode 100644 app/tools/airbrush_blob.c create mode 100644 app/tools/airbrush_blob.h create mode 100644 app/tools/xinput_airbrush.c create mode 100644 app/tools/xinput_airbrush.h create mode 100644 app/xinput_airbrush.c create mode 100644 app/xinput_airbrush.h diff --git a/ChangeLog b/ChangeLog index 5b4092f421..95ec1e28b2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Tue Sep 7 03:15:20 CEST 1999 Olof S Kylander + + * app/airbrush_blob.[ch] new file + * app/xinput_airbrush.[ch] new file + * gimp/app/Makefile.am + * gimp/app/pixmaps2.h + * gimp/app/tool_options.c + * gimp/app/tools.c + * gimp/app/toolsF.h + + Added my far from finished xinput airbrush tool please read + my dev-mail about it. + Tue Sep 7 03:16:56 MEST 1999 Sven Neumann * configure.in: build the Makefile in plug-ins/gflare/gflares. diff --git a/app/Makefile.am b/app/Makefile.am index f44052ae57..cc57bd9493 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -34,6 +34,8 @@ gimp_SOURCES = \ actionarea.h \ airbrush.c \ airbrush.h \ + airbrush_blob.c \ + airbrush_blob.h \ app_procs.c \ app_procs.h \ appenv.h \ @@ -417,7 +419,9 @@ gimp_SOURCES = \ vector2d.c \ vector2d.h \ xcf.c \ - xcf.h + xcf.h \ + xinput_airbrush.c \ + xinput_airbrush.h EXTRA_DIST = \ makefile.cygwin \ diff --git a/app/airbrush_blob.c b/app/airbrush_blob.c new file mode 100644 index 0000000000..bcc7e106e4 --- /dev/null +++ b/app/airbrush_blob.c @@ -0,0 +1,1797 @@ +/* airbrush_blob.c: routines for manipulating scan converted convex + * polygons. + * + * Copyright 1998-1999, Owen Taylor + * + * > Please contact the above author before modifying the copy < + * > of this file in the GIMP distribution. Thanks. < + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "airbrush_blob.h" +#include + +#include +#include +#include +#include + + +#define ROUND(A) floor((A)+0.5) + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* M_PI */ + +#ifndef M_PI_H +#define M_PI_H 3.14159265358979323846/2 +#endif + +#define SUBSAMPLE 8.0 + + + +static AirBrushBlob * +airbrush_blob_new (int y, int height) +{ + + AirBrushBlob *result; + + result = g_malloc (sizeof (AirBrushBlob) + sizeof(AirBrushBlobSpan) * (height-1)); + result->y = y; + result->height = height; + return result; +} + + +static AirBlob * +airblob_new(int y) +{ + AirBlob *result; + + result = g_malloc (sizeof (AirBlob)); + return result; +} + +static AirLine * +airline_new(int y) +{ + AirLine *result; + + result = g_malloc(sizeof (AirLine)); + return result; +} + + + + + +typedef enum { + EDGE_NONE = 0, + EDGE_LEFT = 1 << 0, + EDGE_RIGHT = 1 << 1 +} EdgeType; + +static void +airbrush_blob_fill (AirBrushBlob *b, EdgeType *present) +{ + + int start; + int x1, x2, i1, i2; + int i; + + /* Mark empty lines at top and bottom as unused */ + + start = 0; + while (!(present[start])) + { + b->data[start].left = 0; + b->data[start].right = -1; + start++; + } + if (present[start] != (EDGE_RIGHT | EDGE_LEFT)) + { + if (present[start] == EDGE_RIGHT) + b->data[start].left = b->data[start].right; + else + b->data[start].right = b->data[start].left; + + present[start] = EDGE_RIGHT | EDGE_LEFT; + } + + for (i=b->height-1;!present[i];i--) + { + b->data[i].left = 0; + b->data[i].right = -1; + } + if (present[i] != (EDGE_RIGHT | EDGE_LEFT)) + { + if (present[i] == EDGE_RIGHT) + b->data[i].left = b->data[i].right; + else + b->data[i].right = b->data[i].left; + + present[i] = EDGE_RIGHT | EDGE_LEFT; + } + + + /* Restore missing edges */ + + /* We fill only interior regions of convex hull, as if we were filling + polygons. But since we draw ellipses with nearest points, not interior + points, maybe it would look better if we did the same here. Probably + not a big deal either way after anti-aliasing */ + + /* left edge */ + for (i1=start; i1height-2; i1++) + { + /* Find empty gaps */ + if (!(present[i1+1] & EDGE_LEFT)) + { + int increment; /* fractional part */ + int denom; /* denominator of fraction */ + int step; /* integral step */ + int frac; /* fractional step */ + int reverse; + + /* find bottom of gap */ + i2 = i1+2; + while (!(present[i2] & EDGE_LEFT) && i2 < b->height) i2++; + + if (i2 < b->height) + { + denom = i2-i1; + x1 = b->data[i1].left; + x2 = b->data[i2].left; + step = (x2-x1)/denom; + frac = x2-x1 - step*denom; + if (frac < 0) + { + frac = -frac; + reverse = 1; + } + else + reverse = 0; + + increment = 0; + for (i=i1+1; i= denom) + { + increment -= denom; + x1 += reverse ? -1 : 1; + } + if (increment == 0 || reverse) + b->data[i].left = x1; + else + b->data[i].left = x1 + 1; + } + } + i1 = i2-1; /* advance to next possibility */ + } + } + + /* right edge */ + for (i1=start; i1height-2; i1++) + { + /* Find empty gaps */ + if (!(present[i1+1] & EDGE_RIGHT)) + { + int increment; /* fractional part */ + int denom; /* denominator of fraction */ + int step; /* integral step */ + int frac; /* fractional step */ + int reverse; + + /* find bottom of gap */ + i2 = i1+2; + while (!(present[i2] & EDGE_RIGHT) && i2 < b->height) i2++; + + if (i2 < b->height) + { + denom = i2-i1; + x1 = b->data[i1].right; + x2 = b->data[i2].right; + step = (x2-x1)/denom; + frac = x2-x1 - step*denom; + if (frac < 0) + { + frac = -frac; + reverse = 1; + } + else + reverse = 0; + + increment = 0; + for (i=i1+1; i= denom) + { + increment -= denom; + x1 += reverse ? -1 : 1; + } + if (reverse && increment != 0) + b->data[i].right = x1 - 1; + else + b->data[i].right = x1; + } + } + i1 = i2-1; /* advance to next possibility */ + } + } + +} + +static void +airbrush_blob_make_convex (AirBrushBlob *b, EdgeType *present) +{ + int x1, x2, y1, y2, i1, i2; + int i; + int start; + + /* Walk through edges, deleting points that aren't on convex hull */ + + start = 0; + while (!(present[start])) start++; + + /* left edge */ + + i1 = start-1; + i2 = start; + x1 = b->data[start].left - b->data[start].right; + y1 = 0; + + for (i=start+1;iheight;i++) + { + if (!(present[i] & EDGE_LEFT)) + continue; + + x2 = b->data[i].left - b->data[i2].left; + y2 = i-i2; + + while (x2*y1 - x1*y2 < 0) /* clockwise rotation */ + { + present[i2] &= ~EDGE_LEFT; + i2 = i1; + while (!(present[--i1] & EDGE_LEFT) && i1>=start); + + if (i1data[start].left - b->data[start].right; + y1 = 0; + } + else + { + x1 = b->data[i2].left - b->data[i1].left; + y1 = i2 - i1; + } + x2 = b->data[i].left - b->data[i2].left; + y2 = i - i2; + } + x1 = x2; + y1 = y2; + i1 = i2; + i2 = i; + } + + /* Right edge */ + + i1 = start -1; + i2 = start; + x1 = b->data[start].right - b->data[start].left; + y1 = 0; + + for (i=start+1;iheight;i++) + { + if (!(present[i] & EDGE_RIGHT)) + continue; + + x2 = b->data[i].right - b->data[i2].right; + y2 = i-i2; + + while (x2*y1 - x1*y2 > 0) /* counter-clockwise rotation */ + { + present[i2] &= ~EDGE_RIGHT; + i2 = i1; + while (!(present[--i1] & EDGE_RIGHT) && i1>=start); + + if (i1data[start].right - b->data[start].left; + y1 = 0; + } + else + { + x1 = b->data[i2].right - b->data[i1].right; + y1 = i2 - i1; + } + x2 = b->data[i].right - b->data[i2].right; + y2 = i - i2; + } + x1 = x2; + y1 = y2; + i1 = i2; + i2 = i; + } + + airbrush_blob_fill (b, present); +} + +AirBrushBlob * +airbrush_blob_convex_union (AirBrushBlob *b1, AirBrushBlob *b2) +{ + AirBrushBlob *result; + int y; + int i, j; + EdgeType *present; + + + /* Create the storage for the result */ + + y = MIN(b1->y,b2->y); + result = airbrush_blob_new (y, MAX(b1->y+b1->height,b2->y+b2->height)-y); + + if (result->height == 0) + return result; + + present = g_new0 (EdgeType, result->height); + + /* Initialize spans from original objects */ + + for (i=0, j=b1->y-y; iheight; i++,j++) + { + if (b1->data[i].right >= b1->data[i].left) + { + present[j] = EDGE_LEFT | EDGE_RIGHT; + result->data[j].left = b1->data[i].left; + result->data[j].right = b1->data[i].right; + } + } + + for (i=0, j=b2->y-y; iheight; i++,j++) + { + if (b2->data[i].right >= b2->data[i].left) + { + if (present[j]) + { + if (result->data[j].left > b2->data[i].left) + result->data[j].left = b2->data[i].left; + if (result->data[j].right < b2->data[i].right) + result->data[j].right = b2->data[i].right; + } + else + { + present[j] = EDGE_LEFT | EDGE_RIGHT; + result->data[j].left = b2->data[i].left; + result->data[j].right = b2->data[i].right; + } + } + } + + airbrush_blob_make_convex (result, present); + + g_free (present); + return result; +} + +/* You must be able to divide TABLE_SIZE with 4*/ + +#define TABLE_SIZE 256 +#define TABLE_QUARTER TABLE_SIZE/4 + +#define ELLIPSE_SHIFT 2 +#define TABLE_SHIFT 14 +#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) + +static int trig_initialized = 0; +static int trig_table[TABLE_SIZE]; +#define POWFAC 0.999 + + + +/* Scan convert an ellipse specified by _offsets_ of major and + minor axes, and by center into a airbrush_blob */ + +/* Warning UGLY code ahead :-)*/ + +AirBrushBlob * +airbrush_blob_ellipse (double xc, double yc, double xt, double yt, double xr, double yr, double xb, double yb, double xl, double yl) +{ + int i; + AirBrushBlob *rma, *rmi, *rtot; + gint maxyma, minyma, maxymi, minymi; + gint step; + double max_radius; + double ma_ang1, ma_ang2; + double x1, x2, y1, y2; + double xtotma, ytotma; + double xcma, ycma, ytma, xrma, ybma, xlma; + double xcmi, ycmi, ytmi, xrmi, ybmi, xlmi; + double mapoint_i, mapoint; + + gint xcma_shift, ycma_shift; + gint xcmi_shift, ycmi_shift; + gint ytma_shift, ytmi_shift; + gint xrma_shift, xrmi_shift; + gint ybma_shift, ybmi_shift; + gint xlma_shift, xlmi_shift; + + EdgeType *presentma; + EdgeType *presentmi; + + + if ((yt == yb) && (xr == xl) && (yt == xr)) + { + /*Zero*/ + + ytma = ytmi = xrma = xrmi = ybma = ybmi = xlma = xlmi = yt; + ycma = ycmi = yc; + xcma = xcmi = xc; + + } + else if (xr == xl) + { + if (yt > yb) + { + /*South*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = xl; + mapoint_i = (((yt * yt) / yb) - yt); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc; + ycma = yc + mapoint; + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = xl/2; + xcmi = xc; + ycmi = yc - mapoint/2; + + } + else + { + /*North*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = xl; + mapoint_i = (((yb * yb) / yt) - yb); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc; + ycma = yc - mapoint; + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = xl/2; + xcmi = xc; + ycmi = yc + mapoint/2; + + } + } + else if (yt == yb) + { + if (xr > xl) + { + /*East*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = yt; + mapoint_i = (((xr * xr) /xl) -xr); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = mapoint + xc; + ycma = yc; + + /*The Min Circle*/ + ytmi = ybmi = xrmi = xlmi = yt/2; + xcmi = xc - mapoint/2; + ycmi = yc; + + } + else + { + /*West*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = yt; + mapoint_i = (((xl * xl) /xr) - xl); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - mapoint; + ycma = yc; + + + /*The Min Circle*/ + ytmi = ybmi = xrmi = xlmi = yt/2; + xcmi = xc + mapoint/2; + ycmi = yc; + + } + } + else if ((yt > yb) && (xr > xl)) + { + /*SouthEast*/ + + /*The Max circle*/ + ma_ang1 = atan(yt/xr); + x1 = cos(ma_ang1) * xl; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xr; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yt * yt + xr * xr) / hypot(yb, xl)) - hypot(yt, xr)); + mapoint = mapoint_i * pow(POWFAC , mapoint_i); + xcma = xc + (cos(ma_ang1) * mapoint); + ycma = yc + (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yb, xl)/2; + xcmi = xc - (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc - (sin(ma_ang1) * mapoint * 0.5); + + } + else if ((yt > yb) && (xl > xr)) + { + /*SouthWest*/ + + /*The Max circle*/ + ma_ang1 = atan(yt/xl); + x1 = cos(ma_ang1) * xr; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xl; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yt * yt + xl * xl) / hypot(yb, xr)) - hypot(yt, xl)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - (cos(ma_ang1) * mapoint); + ycma = yc + (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yb, xr)/2; + xcmi = xc + (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc - (sin(ma_ang1) * mapoint * 0.5); + + } + else if ((yb > yt) && (xl > xr)) + { + /*NorthWest*/ + + /*The Max circle*/ + ma_ang1 = atan(yb/xl); + x1 = cos(ma_ang1) * xl; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xr; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yb * yb + xl * xl) / hypot(yt, xr)) - hypot(yb, xl)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - (cos(ma_ang1) * mapoint); + ycma = yc - (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yt, xr)/2; + xcmi = xc + (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc + (sin(ma_ang1) * mapoint * 0.5); + + } + else +/*if ((yb > yt) && (xr > xl))*/ + { + /*NorthEast*/ + + /*The Max circle*/ + ma_ang1 = atan(yb/xr); + x1 = cos(ma_ang1) * xr; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xl; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yb * yb + xr * xr) / hypot(yt, xl)) - hypot(yb, xr)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc + (cos(ma_ang1) * mapoint); + ycma = yc - (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yt, xl)/2; + xcmi = xc - (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc + (sin(ma_ang1) * mapoint * 0.5); + + } + if (ytmi <= 0) + { + ytmi = ybmi = xrmi = xlmi = 1; + } + + if (ytma <= 0) + { + ytma = ybma = xrma = xlma = 1; + } + + if (!trig_initialized) + { + trig_initialized = 1; + for (i=0; i<256; i++) + trig_table[i] = 0.5 + sin(i * (M_PI / 128.)) * (1 << TABLE_SHIFT); + } + + +/*Make the Max circle*/ + + maxyma = ceil (ycma + fabs (ytma)); + minyma = floor (ycma - fabs (ybma)); + + + + rma = airbrush_blob_new (minyma, maxyma - minyma + 1); + + + presentma = g_new0 (EdgeType, rma->height); + + max_radius = ytma; + + step = TABLE_SIZE; + + while (step > 1 && (TABLE_SIZE / step < 4*max_radius)) + step >>= 1; + + /* Fill in the edge points */ + + xcma_shift = 0.5 + xcma * (1 << TOTAL_SHIFT); + ycma_shift = 0.5 + ycma * (1 << TOTAL_SHIFT); + ytma_shift = 0.5 + ytma * (1 << ELLIPSE_SHIFT); + xrma_shift = 0.5 + xrma * (1 << ELLIPSE_SHIFT); + ybma_shift = 0.5 + ybma * (1 << ELLIPSE_SHIFT); + xlma_shift = 0.5 + xlma * (1 << ELLIPSE_SHIFT); + + + for (i = 0 ; i < TABLE_SIZE ; i += step) + { + + gint x, y, yi, dydi; + + gint s = trig_table[i]; + gint c = trig_table[(TABLE_SIZE + TABLE_SIZE/4 - i) % TABLE_SIZE]; + + if (i < TABLE_QUARTER ) + { + x = (xcma_shift + c * xrma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycma_shift + s * ytma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = yi - rma->y; + dydi = 1; + + } + else if ( i < (2 * TABLE_QUARTER) ) + { + x = (xcma_shift + c * xlma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycma_shift + s * ytma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + + y = yi - rma->y; + + dydi = -1; + } + else if ( i < (3 * TABLE_QUARTER) ) + { + x = (xcma_shift + c * xlma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycma_shift + s * ybma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = yi - rma->y; + dydi = -1; + + } + else + { + x = (xcma_shift + c * xrma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = ((ycma_shift + s * ybma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT) - rma->y; + + dydi = 1; + + } + + + + + if (dydi <= 0) /* left edge */ + { + if (presentma[y] & EDGE_LEFT) + { + rma->data[y].left = MIN (rma->data[y].left, x); + } + else + { + presentma[y] |= EDGE_LEFT; + rma->data[y].left = x; + } + } + + if (dydi > 0) /* right edge */ + { + if (presentma[y] & EDGE_RIGHT) + { + rma->data[y].right = MAX (rma->data[y].right, x); + } + else + { + presentma[y] |= EDGE_RIGHT; + rma->data[y].right = x; + } + } + } + + /* Now fill in missing points */ + + airbrush_blob_fill (rma, presentma); + g_free (presentma); + +/*Make the Min circle*/ + + + maxymi = ceil (ycmi + fabs (ytmi)); + minymi = floor (ycmi - fabs (ybmi)); + + + rmi = airbrush_blob_new (minymi, maxymi - minymi + 1); + + + presentmi = g_new0 (EdgeType, rmi->height); + + max_radius = ytmi; + + step = TABLE_SIZE; + + while (step > 1 && (TABLE_SIZE / step < 4*max_radius)) + step >>= 1; + + /* Fill in the edge points */ + + xcmi_shift = 0.5 + xcmi * (1 << TOTAL_SHIFT); + ycmi_shift = 0.5 + ycmi * (1 << TOTAL_SHIFT); + ytmi_shift = 0.5 + ytmi * (1 << ELLIPSE_SHIFT); + xrmi_shift = 0.5 + xrmi * (1 << ELLIPSE_SHIFT); + ybmi_shift = 0.5 + ybmi * (1 << ELLIPSE_SHIFT); + xlmi_shift = 0.5 + xlmi * (1 << ELLIPSE_SHIFT); + + + + for (i = 0 ; i < TABLE_SIZE ; i += step) + { + + gint x, y, yi, dydi; + + gint s = trig_table[i]; + gint c = trig_table[(TABLE_SIZE + TABLE_SIZE/4 - i) % TABLE_SIZE]; + + if (i < TABLE_QUARTER ) + { + x = (xcmi_shift + c * xrmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycmi_shift + s * ytmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = yi - rmi->y; + dydi = 1; + + } + else if ( i < (2 * TABLE_QUARTER) ) + { + x = (xcmi_shift + c * xlmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycmi_shift + s * ytmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + + y = yi - rmi->y; + + dydi = -1; + } + else if ( i < (3 * TABLE_QUARTER) ) + { + x = (xcmi_shift + c * xlmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycmi_shift + s * ybmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = yi - rmi->y; + dydi = -1; + + } + else + { + x = (xcmi_shift + c * xrmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = ((ycmi_shift + s * ybmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT) - rmi->y; + + dydi = 1; + + } + + + + + if (dydi <= 0) /* left edge */ + { + if (presentmi[y] & EDGE_LEFT) + { + rmi->data[y].left = MIN (rmi->data[y].left, x); + } + else + { + presentmi[y] |= EDGE_LEFT; + rmi->data[y].left = x; + } + } + + if (dydi > 0) /* right edge */ + { + if (presentmi[y] & EDGE_RIGHT) + { + rmi->data[y].right = MAX (rmi->data[y].right, x); + } + else + { + presentmi[y] |= EDGE_RIGHT; + rmi->data[y].right = x; + } + } + } + + /* Now fill in missing points */ + + airbrush_blob_fill (rmi, presentmi); + g_free (presentmi); + + rtot = airbrush_blob_convex_union(rma, rmi); + + g_free (rma); + g_free (rmi); + + return rtot; +} + +void +airbrush_blob_bounds(AirBrushBlob *b, int *x, int *y, int *width, int *height) +{ + int i; + int x0, x1, y0, y1; + + i = 0; + while (iheight && b->data[i].left > b->data[i].right) + i++; + + if (iheight) + { + y0 = b->y + i; + x0 = b->data[i].left; + x1 = b->data[i].right + 1; + while (iheight && b->data[i].left <= b->data[i].right) + { + x0 = MIN(b->data[i].left, x0); + x1 = MAX(b->data[i].right+1, x1); + i++; + } + y1 = b->y + i; + } + else + { + x0 = y0 = 0; + x1 = y1 = 0; + } + + *x = x0; + *y = y0; + *width = x1 - x0; + *height = y1 - y0; +} + +void +airbrush_blob_dump(AirBrushBlob *b) { + + int i,j; + + + for (i=0; iheight; i++) + { + for (j=0;jdata[i].left;j++) + putchar(' '); + for (j=b->data[i].left;j<=b->data[i].right;j++) + putchar('*'); + putchar('\n'); + } +} + +/* This is just a first try to see how it works i.e ugly code :-) */ + + +AirBlob * +create_air_blob (double xc, + double yc, + double xt, + double yt, + double xr, + double yr, + double xb, + double yb, + double xl, + double yl, + double direction_abs, + double direction) +{ + AirBlob *air_blob; + double ma_ang1, ma_ang2; + double x1, x2, y1, y2; + double xtotma, ytotma; + double xcma, ycma, ytma, xrma, ybma, xlma; + double xcmi, ycmi, ytmi, xrmi, ybmi, xlmi; + double mapoint_i, mapoint; + + air_blob = airblob_new(1); + + air_blob->direction_abs = direction_abs; + air_blob->direction = direction; + air_blob->ycenter = yc; + air_blob->xcenter = xc; + + + + + if ((yt == yb) && (xr == xl) && (yt == xr)) + { + /*Zero*/ + + ytma = ytmi = xrma = xrmi = ybma = ybmi = xlma = xlmi = yt; + ycma = ycmi = yc; + xcma = xcmi = xc; + + air_blob->main_line.size = yt; + air_blob->minor_line.size = yt; + + air_blob->maincross_line.size = yt * 2; + air_blob->maincross_line.dist = 0.0; + + air_blob->minorcross_line.size = yt * 2; + air_blob->minorcross_line.dist = 0.0; + + air_blob->direction = M_PI_H; + + } + else if (xr == xl) + { + if (yt > yb) + { + /*South*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = xl; + mapoint_i = (((yt * yt) / yb) - yt); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc; + ycma = yc + mapoint; + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = xl/2; + xcmi = xc; + ycmi = yc - mapoint/2; + + air_blob->main_line.size = mapoint + xl; + air_blob->minor_line.size = mapoint/2 + xl/2; + + air_blob->maincross_line.size = xl * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xl/2; + air_blob->minorcross_line.dist = mapoint/2; + + + + } + else + { + /*North*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = xl; + mapoint_i = (((yb * yb) / yt) - yb); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc; + ycma = yc - mapoint; + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = xl/2; + xcmi = xc; + ycmi = yc + mapoint/2; + + + air_blob->main_line.size = mapoint + xl; + air_blob->minor_line.size = mapoint/2 + xl/2; + + air_blob->maincross_line.size = xl * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xl/2; + air_blob->minorcross_line.dist = mapoint/2; + + + } + } + else if (yt == yb) + { + if (xr > xl) + { + /*East*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = yt; + mapoint_i = (((xr * xr) /xl) -xr); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = mapoint + xc; + ycma = yc; + + /*The Min Circle*/ + ytmi = ybmi = xrmi = xlmi = yt/2; + xcmi = xc - mapoint/2; + ycmi = yc; + + air_blob->main_line.size = mapoint + yt; + air_blob->minor_line.size = mapoint/2 + yt/2; + + air_blob->maincross_line.size = xl * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xl/2; + air_blob->minorcross_line.dist = mapoint/2; + + + } + else + { + /*West*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = yt; + mapoint_i = (((xl * xl) /xr) - xl); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - mapoint; + ycma = yc; + + + /*The Min Circle*/ + ytmi = ybmi = xrmi = xlmi = yt/2; + xcmi = xc + mapoint/2; + ycmi = yc; + + air_blob->main_line.size = mapoint + yt; + air_blob->minor_line.size = mapoint/2 + yt/2; + + air_blob->maincross_line.size = xl * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xl/2; + air_blob->minorcross_line.dist = mapoint/2; + + + } + } + else if ((yt > yb) && (xr > xl)) + { + /*SouthEast*/ + + /*The Max circle*/ + ma_ang1 = atan(yt/xr); + x1 = cos(ma_ang1) * xl; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xr; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yt * yt + xr * xr) / hypot(yb, xl)) - hypot(yt, xr)); + mapoint = mapoint_i * pow(POWFAC , mapoint_i); + xcma = xc + (cos(ma_ang1) * mapoint); + ycma = yc + (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yb, xl)/2; + xcmi = xc - (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc - (sin(ma_ang1) * mapoint * 0.5); + + + air_blob->main_line.size = mapoint + xlma; + air_blob->minor_line.size = mapoint/2 + xlmi; + + + air_blob->maincross_line.size = xlma * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xlmi; + air_blob->minorcross_line.dist = mapoint/2; + + + + + } + else if ((yt > yb) && (xl > xr)) + { + /*SouthWest*/ + + /*The Max circle*/ + ma_ang1 = atan(yt/xl); + x1 = cos(ma_ang1) * xr; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xl; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yt * yt + xl * xl) / hypot(yb, xr)) - hypot(yt, xl)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - (cos(ma_ang1) * mapoint); + ycma = yc + (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yb, xr)/2; + xcmi = xc + (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc - (sin(ma_ang1) * mapoint * 0.5); + + air_blob->main_line.size = mapoint + xlma; + air_blob->minor_line.size = mapoint/2 + xlmi; + + air_blob->maincross_line.size = xlma * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xlmi; + air_blob->minorcross_line.dist = mapoint/2; + + } + else if ((yb > yt) && (xl > xr)) + { + /*NorthWest*/ + + /*The Max circle*/ + ma_ang1 = atan(yb/xl); + x1 = cos(ma_ang1) * xl; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xr; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yb * yb + xl * xl) / hypot(yt, xr)) - hypot(yb, xl)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - (cos(ma_ang1) * mapoint); + ycma = yc - (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yt, xr)/2; + xcmi = xc + (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc + (sin(ma_ang1) * mapoint * 0.5); + + air_blob->main_line.size = mapoint + xlma; + air_blob->minor_line.size = mapoint/2 + xlmi; + + air_blob->maincross_line.size = xlma * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xlmi; + air_blob->minorcross_line.dist = mapoint/2; + + + + + } + else +/*if ((yb > yt) && (xr > xl))*/ + { + /*NorthEast*/ + + /*The Max circle*/ + ma_ang1 = atan(yb/xr); + x1 = cos(ma_ang1) * xr; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xl; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yb * yb + xr * xr) / hypot(yt, xl)) - hypot(yb, xr)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc + (cos(ma_ang1) * mapoint); + ycma = yc - (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yt, xl)/2; + xcmi = xc - (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc + (sin(ma_ang1) * mapoint * 0.5); + + air_blob->main_line.size = mapoint + xlma; + air_blob->minor_line.size = mapoint/2 + xlmi; + + + air_blob->maincross_line.size = xlma * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xlmi; + air_blob->minorcross_line.dist = mapoint/2; + + + } + + return air_blob; +} + +AirLine * +create_air_line(AirBlob *airblob) +{ + + int i; + double xcenter, ycenter; + + double direction; + + double masupport, misupport; + double ma_angsupport, mi_angsupport, iang; + + int min_x, max_x; + int min_y, max_y; + + AirLine *airline; + + /* Yes I know I can do a cal of number of lines, but it is for + the moment much easier to just set a side mem for 16 lines + */ + + airline = airline_new(1); + + xcenter = airblob->xcenter; + ycenter = airblob->ycenter; + + airline->xcenter = xcenter/SUBSAMPLE; + airline->ycenter = ycenter/SUBSAMPLE; + + direction = airblob->direction_abs; + + if(direction == 0.0) + + { + + airline->line[0].x = (xcenter + airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[0].y = (ycenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[1].x = (xcenter - airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter + airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter)/SUBSAMPLE; + airline->line[3].x = (xcenter - airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter)/SUBSAMPLE; + + airline->line[4].x = (xcenter + airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter - airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->nlines = 6; + + + } + + else if(direction == M_PI_H) + + { + + airline->line[0].x = (xcenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[0].y = (ycenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[1].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter)/SUBSAMPLE; + airline->line[2].y = (ycenter - airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter)/SUBSAMPLE; + airline->line[3].y = (ycenter + airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter + airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter - airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->nlines = 6; + + } + + + else if(direction == M_PI) + + { + airline->line[0].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[0].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[1].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter - airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter)/SUBSAMPLE; + airline->line[3].x = (xcenter + airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter)/SUBSAMPLE; + + airline->line[4].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->nlines = 6; + + } + + else if(direction == -M_PI_H) + + { + airline->line[0].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[0].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[1].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter)/SUBSAMPLE; + airline->line[2].y = (ycenter + airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter)/SUBSAMPLE; + airline->line[3].y = (ycenter - airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter + airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter - airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + } + + + else if(direction == -M_PI) + + { + airline->line[0].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[0].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[1].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter - airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter)/SUBSAMPLE; + airline->line[3].x = (xcenter + airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter)/SUBSAMPLE; + + airline->line[4].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->nlines = 6; + + } + + + else if ((direction < M_PI) && (direction > M_PI_H)) + + { + + + masupport = hypot(airblob->maincross_line.dist, airblob->maincross_line.size/2); + misupport = hypot(airblob->minorcross_line.dist,airblob->minorcross_line.size/2); + + ma_angsupport = atan(airblob->maincross_line.size/2/airblob->maincross_line.dist); + mi_angsupport = atan(airblob->minorcross_line.size/2/airblob->minorcross_line.dist); + + iang = airblob->direction_abs - M_PI_H; + + airline->line[0].x = (xcenter - sin(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[0].y = (ycenter - cos(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[1].x = (xcenter + sin(iang + mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[1].y = (ycenter + cos(iang + mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[2].x = (xcenter - sin(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter - cos(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter + sin(iang) * airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter + cos(iang) * airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter - sin(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[4].y = (ycenter - cos(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[5].x = (xcenter + sin(iang - mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[5].y = (ycenter + cos(iang - mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[6].x = (xcenter - sin(iang + ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[6].y = (ycenter - cos(iang + ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[7].x = (xcenter - sin(iang - ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[7].y = (ycenter - cos(iang - ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + + + + airline->nlines = 8; + + } + + else if ((direction < M_PI_H) && (direction > 0.0)) + + { + + + masupport = hypot(airblob->maincross_line.dist, airblob->maincross_line.size/2); + misupport = hypot(airblob->minorcross_line.dist,airblob->minorcross_line.size/2); + + ma_angsupport = atan(airblob->maincross_line.size/2/airblob->maincross_line.dist); + mi_angsupport = atan(airblob->minorcross_line.size/2/airblob->minorcross_line.dist); + + iang = airblob->direction_abs; + + airline->line[0].x = (xcenter + cos(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[0].y = (ycenter - sin(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[1].x = (xcenter - cos(iang + mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[1].y = (ycenter + sin(iang + mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[2].x = (xcenter + cos(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter - sin(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter - cos(iang) * airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter + sin(iang) * airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter + cos(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[4].y = (ycenter - sin(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[5].x = (xcenter - cos(iang - mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[5].y = (ycenter + sin(iang - mi_angsupport) * misupport)/SUBSAMPLE; + + + airline->line[6].x = (xcenter + cos(iang + ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[6].y = (ycenter - sin(iang + ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[7].x = (xcenter + cos(iang - ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[7].y = (ycenter - sin(iang - ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + + + + + airline->nlines = 8; + + } + + + else if ((direction < 0.0) && (direction > -M_PI_H)) + + { + + + masupport = hypot(airblob->maincross_line.dist, airblob->maincross_line.size/2); + misupport = hypot(airblob->minorcross_line.dist,airblob->minorcross_line.size/2); + + ma_angsupport = atan(airblob->maincross_line.size/2/airblob->maincross_line.dist); + mi_angsupport = atan(airblob->minorcross_line.size/2/airblob->minorcross_line.dist); + + iang = fabs(airblob->direction_abs); + + airline->line[0].x = (xcenter + cos(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[0].y = (ycenter + sin(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[1].x = (xcenter - cos(iang + mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[1].y = (ycenter - sin(iang + mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[2].x = (xcenter + cos(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter + sin(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter - cos(iang) * airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter - sin(iang) * airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter + cos(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[4].y = (ycenter + sin(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[5].x = (xcenter - cos(iang - mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[5].y = (ycenter - sin(iang - mi_angsupport) * misupport)/SUBSAMPLE; + + airline->nlines = 6; + + } + + + else if ((direction < -M_PI_H) && (direction > -M_PI)) + + { + + + masupport = hypot(airblob->maincross_line.dist, airblob->maincross_line.size/2); + misupport = hypot(airblob->minorcross_line.dist,airblob->minorcross_line.size/2); + + ma_angsupport = atan(airblob->maincross_line.size/2/airblob->maincross_line.dist); + mi_angsupport = atan(airblob->minorcross_line.size/2/airblob->minorcross_line.dist); + + iang = fabs(airblob->direction_abs) - M_PI_H; + + airline->line[0].x = (xcenter - sin(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[0].y = (ycenter + cos(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[1].x = (xcenter + sin(iang + mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[1].y = (ycenter - cos(iang + mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[2].x = (xcenter - sin(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter + cos(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter + sin(iang) * airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter - cos(iang) * airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter - sin(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[4].y = (ycenter + cos(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[5].x = (xcenter + sin(iang - mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[5].y = (ycenter - cos(iang - mi_angsupport) * misupport)/SUBSAMPLE; + + airline->nlines = 6; + + } + else + { + printf("Hmm a bug in the create_air_line"); + } + + + min_x = max_x = airline->xcenter; + min_y = max_y = airline->ycenter; + + + + for (i=0; i < airline->nlines ; i++) + { + min_x = MIN(airline->line[i].x, min_x); + max_x = MAX(airline->line[i].x, max_x); + min_y = MIN(airline->line[i].y, min_y); + max_y = MAX(airline->line[i].y, max_y); + } + + airline->width = max_x - min_x + 1; + airline->height = max_y - min_y + 1; + + airline->min_x = min_x; + airline->min_y = min_y; + airline->max_x = max_x; + airline->max_y = max_y; + + return airline; + +} + +AirBlob * +trans_air_blob(AirBlob *airblob_last, AirBlob *airblob_present, double dist, int xcen, int ycen) +{ + + AirBlob *trans_airblob; + + double direction_last_abs, direction_present_abs; + double idirection_abs; + + double direction_last, direction_present; + double idirection; + + double main_line_present, main_line_last; + + + double minor_line_present, minor_line_last; + + + double maincross_line_dist_present, maincross_line_dist_last; + + double minorcross_line_dist_present, minorcross_line_dist_last; + + + double maincross_line_size_present, maincross_line_size_last; + + double minorcross_line_size_present, minorcross_line_size_last; + + + + trans_airblob = airblob_new(1); + + direction_last_abs = airblob_last->direction_abs + M_PI; + direction_present_abs = airblob_present->direction_abs + M_PI; + + idirection_abs = direction_present_abs - direction_last_abs; + + direction_last = airblob_last->direction + M_PI_H; + direction_present = airblob_present->direction + M_PI_H; + + idirection = direction_present - direction_last; + + main_line_present = airblob_present->main_line.size; + main_line_last = airblob_last->main_line.size; + + minor_line_present = airblob_present->minor_line.size; + minor_line_last = airblob_last->minor_line.size; + + maincross_line_dist_present = airblob_present->maincross_line.dist; + maincross_line_size_present = airblob_present->maincross_line.size; + minorcross_line_dist_present = airblob_present->minorcross_line.dist; + minorcross_line_size_present = airblob_present->minorcross_line.size; + + maincross_line_dist_last = airblob_last->maincross_line.dist; + maincross_line_size_last = airblob_last->maincross_line.size; + minorcross_line_dist_last = airblob_last->minorcross_line.dist; + minorcross_line_size_last = airblob_last->minorcross_line.size; + + + + + /* + Now we have to guess a little :-). Why? + Well we can't know if the users is painting + up/down or if she is painting a circle at high speed. + As you may notice it be so that the last airblob has + a direction more or less M_PI rad differernt from the + present airblob. But we can't know if she tured the + airbrush quickly (so that there was no mouse capture + during the turn) or if she paints just up and down the + same direction. + + There for we guess that we want to pait up and down + when the diff in direction is bigger than 171 deg and + smaller than 189 deg. + + */ + + if ((fabs(idirection_abs) > (M_PI - 0.1571)) && (fabs(idirection_abs) < (M_PI + 0.1571))) + { + /* We asume that the artist meant to paint in a "strait line" by just tilting the airbrush*/ + + idirection_abs = idirection_abs - M_PI; + + if ((idirection_abs * dist) > (idirection_abs/2)) + { + if ((direction_present_abs - idirection_abs * dist) > 2 * M_PI) + { + trans_airblob->direction_abs = direction_present_abs - idirection_abs * dist - 2 * M_PI - M_PI; + } + else if ((direction_present_abs - idirection_abs * dist) < 0.0) + { + trans_airblob->direction_abs = direction_present_abs - idirection_abs * dist + 2 * M_PI - M_PI; + } + else + { + trans_airblob->direction_abs = direction_present_abs - idirection_abs * dist - M_PI; + } + } + else + { + if ((direction_present_abs + idirection_abs * dist) > 2 * M_PI) + { + trans_airblob->direction_abs = direction_present_abs + idirection_abs * dist - 2 * M_PI - M_PI; + } + else if ((direction_present_abs + idirection_abs * dist) < 0.0) + { + trans_airblob->direction_abs = direction_present_abs + idirection_abs * dist + 2 * M_PI - M_PI; + } + else + { + trans_airblob->direction_abs = direction_present_abs + idirection_abs * dist - M_PI; + } + } + + trans_airblob->main_line.size = main_line_last + ((minor_line_present - main_line_last) * dist); + trans_airblob->minor_line.size = minor_line_last + ((main_line_present - minor_line_last) * dist); + + trans_airblob->maincross_line.dist = maincross_line_dist_last + ((minorcross_line_dist_present - maincross_line_dist_last) * dist); + trans_airblob->maincross_line.size = maincross_line_size_last + ((minorcross_line_size_present - maincross_line_size_last) * dist); + + trans_airblob->minorcross_line.dist = minorcross_line_dist_last + ((maincross_line_dist_present - minorcross_line_dist_last) * dist); + trans_airblob->minorcross_line.size = minorcross_line_size_last + ((maincross_line_size_present - minorcross_line_size_last) * dist); + + } + + else if (fabs(idirection_abs) < (M_PI - 0.1571)) + { + if ((direction_last_abs + idirection_abs * dist) > 2*M_PI) + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist - 2 * M_PI - M_PI; + } + else if((direction_last_abs + idirection_abs * dist) < 0.0) + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist + 2 * M_PI - M_PI; + } + else + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist - M_PI; + } + + trans_airblob->main_line.size = main_line_last + ((main_line_present - main_line_last) * dist); + trans_airblob->minor_line.size = minor_line_last + ((minor_line_present - minor_line_last) * dist); + + trans_airblob->maincross_line.dist = maincross_line_dist_last + ((maincross_line_dist_present - maincross_line_dist_last) * dist); + trans_airblob->maincross_line.size = maincross_line_size_last + ((maincross_line_size_present - maincross_line_size_last) * dist); + + trans_airblob->minorcross_line.dist = minorcross_line_dist_last + ((minorcross_line_dist_present - minorcross_line_dist_last) * dist); + trans_airblob->minorcross_line.size = minorcross_line_size_last + ((minorcross_line_size_present - minorcross_line_size_last) * dist); + } + else + { + + /* We asume that the artist always travels the shortest way around the "clock" */ + + idirection_abs = idirection_abs - M_PI; + + if ((direction_last_abs + idirection_abs * dist) > 2*M_PI) + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist - 2 * M_PI - M_PI; + } + else if((direction_last_abs + idirection_abs * dist) < 0.0) + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist + 2 * M_PI - M_PI; + } + else + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist - M_PI; + } + + trans_airblob->main_line.size = main_line_last + ((main_line_present - main_line_last) * dist); + trans_airblob->minor_line.size = minor_line_last + ((minor_line_present - minor_line_last) * dist); + + trans_airblob->maincross_line.dist = maincross_line_dist_last + ((maincross_line_dist_present - maincross_line_dist_last) * dist); + trans_airblob->maincross_line.size = maincross_line_size_last + ((maincross_line_size_present - maincross_line_size_last) * dist); + + trans_airblob->minorcross_line.dist = minorcross_line_dist_last + ((minorcross_line_dist_present - minorcross_line_dist_last) * dist); + trans_airblob->minorcross_line.size = minorcross_line_size_last + ((minorcross_line_size_present - minorcross_line_size_last) * dist); + + } + + + trans_airblob->xcenter = xcen * SUBSAMPLE; + trans_airblob->ycenter = ycen * SUBSAMPLE; + + + return trans_airblob; + +} + +int +number_of_steps(int x0, int y0, int x1, int y1) +{ + + + int dx, dy; + + dx = abs(x0 - x1); + + dy = abs(y0 - y1); + + if (dy > dx) + { + return dy + 1; + } + else + { + return dx + 1; + } +} + + + + + + + + + + + + + + + diff --git a/app/airbrush_blob.h b/app/airbrush_blob.h new file mode 100644 index 0000000000..55c70f4577 --- /dev/null +++ b/app/airbrush_blob.h @@ -0,0 +1,154 @@ +/* airbrush_blob.h: routines for manipulating scan converted convex + * polygons. + * + * Copyright 1998, Owen Taylor + * + * > Please contact the above author before modifying the copy < + * > of this file in the GIMP distribution. Thanks. < + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * +*/ + +#ifndef __AIRBRUSHBLOB_H__ +#define __AIRBRUSHBLOB_H__ + +typedef enum { + CROSS = 0, + CROSS_LEFT = 1, + CROSS_RIGHT = 2, + CROSS_WHOLE_LINE = 3, + CROSS_NORMAL = 4 +} CrossType; + +typedef enum { + RIGHT_LEFT = 0, + LEFT_RIGHT = 1, + TOP_BOT = 2, + BOT_TOP = 3, + NONE = 4 +} MoveType; + +/* The AirBlob, which is a abstract of a real AirBrushBlob */ + +typedef struct _AirBlob AirBlob; +typedef struct _AirPoint AirPoint; +typedef struct _SupportLine SupportLine; + + +struct _AirPoint { + int x; + int y; +}; + +struct _SupportLine { + double size; + double dist; +}; + +struct _AirBlob { + double direction_abs; + double direction; + double ycenter; + double xcenter; + SupportLine main_line; + SupportLine minor_line; + SupportLine maincross_line; + SupportLine minorcross_line; +}; + + +/* The AirLine is a reslut of a AirBlob */ +typedef struct _AirLine AirLine; + +struct _AirLine { + int xcenter; + int ycenter; + AirPoint line[16]; + int min_x, min_y; + int max_x, max_y; + int width, height; + int nlines; +}; + + +typedef struct _AirBrushBlobPoint AirBrushBlobPoint; +typedef struct _AirBrushBlobSpan AirBrushBlobSpan; +typedef struct _AirBrushBlob AirBrushBlob; + +struct _AirBrushBlobPoint { + int x; + int y; +}; + +struct _AirBrushBlobSpan { + int left; + double angle_left; + double angle_left_abs; + double dist_left; + int right; + double angle_right; + double angle_right_abs; + double dist_right; + + CrossType cross_type; + int x_cross; + + int center; + double dist; + +}; + +struct _AirBrushBlob { + int y; + int height; + int width; + int min_x; + int max_x; + MoveType move; + double direction_abs; + double direction; + CrossType cross; + AirBrushBlobSpan data[1]; +}; + + +typedef struct _AirBrush AirBrush; + +struct _AirBrush { + AirBrushBlob airbrush_blob; + AirBlob airblob; +}; + + + + +AirBlob *create_air_blob (double xc, double yc, double xt, double yt, double xr, double yr, double xb, double yb, double xl, double yl, double direction_abs, double direction); +AirBlob *trans_air_blob(AirBlob *airblob_last, AirBlob *airblob_present, double dist, int xc, int yc); +AirLine *create_air_line(AirBlob *airblob); +int number_of_steps(int x0, int y0, int x1, int y1); + + + + + +AirBrushBlob *airbrush_blob_convex_union (AirBrushBlob *b1, AirBrushBlob *b2); +AirBrushBlob *airbrush_blob_ellipse (double xc, double yc, double xt, double yt, double xr, double yr, double xb, double yb, double xl, double yl); +void airbrush_blob_bounds (AirBrushBlob *b, int *x, int *y, int *width, int *height); + + + + +#endif /* __AIRBRUSHBLOB_H__ */ diff --git a/app/pixmaps2.h b/app/pixmaps2.h index 4b98380979..52bf8ff6a5 100644 --- a/app/pixmaps2.h +++ b/app/pixmaps2.h @@ -972,3 +972,35 @@ static char *measure_bits [] = "....a............a....", "......................" }; +/* GIMP icon image format -- S. Kimball, P. Mattis */ +/* Image name: xinput_airbrush */ + + +#define xinput_airbrush_width 22 +#define xinput_airbrush_height 22 +static char *xinput_airbrush_bits [] = +{ + "......................", + "........aaaaaa........", + ".......adddddda.......", + "......affffffffa......", + ".......dddddddd.......", + "......affffffffa......", + ".......daaadddd.......", + "......affafffffa......", + ".......afaffffa.......", + "..aaaaaaaaaaaaaaaaaa..", + ".aahhhhhhaaaaaaaaaaaa.", + ".aaggggggaaaaaaaaaaaaa", + "..aaaaaaaaaaaaaaaaaaa.", + "......adeagggeda......", + ".....addeagggedda.....", + "....aadeeaaageedaa....", + "....acdegggaggedca....", + "...aaccegaagggeccaa...", + "...aaccegaggggeccaa...", + "...aaccegaaaggeccaa...", + "...aaccegggaggeccaa...", + "......................" +}; + diff --git a/app/tools.c b/app/tools.c index 34ae9a224c..391b3eada6 100644 --- a/app/tools.c +++ b/app/tools.c @@ -53,6 +53,7 @@ #include "rect_select.h" #include "session.h" #include "smudge.h" +#include "xinput_airbrush.h" #include "text_tool.h" #include "threshold.h" #include "tools.h" @@ -488,7 +489,7 @@ ToolInfo tool_info[] = 24, N_("/Tools/Measure"), "", - (char **) measure_bits, + (char **) measure_bits, N_("Measure distances and angles"), "ContextHelp/measure", MEASURE, @@ -497,11 +498,27 @@ ToolInfo tool_info[] = NULL }, + { + NULL, + N_("Xinput Airbrush"), + 25, + N_("/Tools/XinputAirbrush"), + "A", + (char **) xinput_airbrush_bits, + N_("Natural Airbrush"), + "ContextHelp/xinput_airbrush", + XINPUT_AIRBRUSH, + tools_new_xinput_airbrush, + tools_free_xinput_airbrush, + NULL + }, + + /* Non-toolbox tools */ { NULL, N_("By Color Select"), - 25, + 26, N_("/Select/By Color..."), NULL, NULL, @@ -516,7 +533,7 @@ ToolInfo tool_info[] = { NULL, N_("Color Balance"), - 26, + 27, N_("/Image/Colors/Color Balance"), NULL, NULL, @@ -531,7 +548,7 @@ ToolInfo tool_info[] = { NULL, N_("Brightness-Contrast"), - 27, + 28, N_("/Image/Colors/Brightness-Contrast"), NULL, NULL, @@ -546,7 +563,7 @@ ToolInfo tool_info[] = { NULL, N_("Hue-Saturation"), - 28, + 29, N_("/Image/Colors/Hue-Saturation"), NULL, NULL, @@ -561,7 +578,7 @@ ToolInfo tool_info[] = { NULL, N_("Posterize"), - 29, + 30, N_("/Image/Colors/Posterize"), NULL, NULL, @@ -576,7 +593,7 @@ ToolInfo tool_info[] = { NULL, N_("Threshold"), - 30, + 31, N_("/Image/Colors/Threshold"), NULL, NULL, @@ -591,7 +608,7 @@ ToolInfo tool_info[] = { NULL, N_("Curves"), - 31, + 32, N_("/Image/Colors/Curves"), NULL, NULL, @@ -606,7 +623,7 @@ ToolInfo tool_info[] = { NULL, N_("Levels"), - 32, + 33, N_("/Image/Colors/Levels"), NULL, NULL, @@ -621,7 +638,7 @@ ToolInfo tool_info[] = { NULL, N_("Histogram"), - 33, + 34, N_("/Image/Histogram"), NULL, NULL, diff --git a/app/tools/airbrush_blob.c b/app/tools/airbrush_blob.c new file mode 100644 index 0000000000..bcc7e106e4 --- /dev/null +++ b/app/tools/airbrush_blob.c @@ -0,0 +1,1797 @@ +/* airbrush_blob.c: routines for manipulating scan converted convex + * polygons. + * + * Copyright 1998-1999, Owen Taylor + * + * > Please contact the above author before modifying the copy < + * > of this file in the GIMP distribution. Thanks. < + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "airbrush_blob.h" +#include + +#include +#include +#include +#include + + +#define ROUND(A) floor((A)+0.5) + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* M_PI */ + +#ifndef M_PI_H +#define M_PI_H 3.14159265358979323846/2 +#endif + +#define SUBSAMPLE 8.0 + + + +static AirBrushBlob * +airbrush_blob_new (int y, int height) +{ + + AirBrushBlob *result; + + result = g_malloc (sizeof (AirBrushBlob) + sizeof(AirBrushBlobSpan) * (height-1)); + result->y = y; + result->height = height; + return result; +} + + +static AirBlob * +airblob_new(int y) +{ + AirBlob *result; + + result = g_malloc (sizeof (AirBlob)); + return result; +} + +static AirLine * +airline_new(int y) +{ + AirLine *result; + + result = g_malloc(sizeof (AirLine)); + return result; +} + + + + + +typedef enum { + EDGE_NONE = 0, + EDGE_LEFT = 1 << 0, + EDGE_RIGHT = 1 << 1 +} EdgeType; + +static void +airbrush_blob_fill (AirBrushBlob *b, EdgeType *present) +{ + + int start; + int x1, x2, i1, i2; + int i; + + /* Mark empty lines at top and bottom as unused */ + + start = 0; + while (!(present[start])) + { + b->data[start].left = 0; + b->data[start].right = -1; + start++; + } + if (present[start] != (EDGE_RIGHT | EDGE_LEFT)) + { + if (present[start] == EDGE_RIGHT) + b->data[start].left = b->data[start].right; + else + b->data[start].right = b->data[start].left; + + present[start] = EDGE_RIGHT | EDGE_LEFT; + } + + for (i=b->height-1;!present[i];i--) + { + b->data[i].left = 0; + b->data[i].right = -1; + } + if (present[i] != (EDGE_RIGHT | EDGE_LEFT)) + { + if (present[i] == EDGE_RIGHT) + b->data[i].left = b->data[i].right; + else + b->data[i].right = b->data[i].left; + + present[i] = EDGE_RIGHT | EDGE_LEFT; + } + + + /* Restore missing edges */ + + /* We fill only interior regions of convex hull, as if we were filling + polygons. But since we draw ellipses with nearest points, not interior + points, maybe it would look better if we did the same here. Probably + not a big deal either way after anti-aliasing */ + + /* left edge */ + for (i1=start; i1height-2; i1++) + { + /* Find empty gaps */ + if (!(present[i1+1] & EDGE_LEFT)) + { + int increment; /* fractional part */ + int denom; /* denominator of fraction */ + int step; /* integral step */ + int frac; /* fractional step */ + int reverse; + + /* find bottom of gap */ + i2 = i1+2; + while (!(present[i2] & EDGE_LEFT) && i2 < b->height) i2++; + + if (i2 < b->height) + { + denom = i2-i1; + x1 = b->data[i1].left; + x2 = b->data[i2].left; + step = (x2-x1)/denom; + frac = x2-x1 - step*denom; + if (frac < 0) + { + frac = -frac; + reverse = 1; + } + else + reverse = 0; + + increment = 0; + for (i=i1+1; i= denom) + { + increment -= denom; + x1 += reverse ? -1 : 1; + } + if (increment == 0 || reverse) + b->data[i].left = x1; + else + b->data[i].left = x1 + 1; + } + } + i1 = i2-1; /* advance to next possibility */ + } + } + + /* right edge */ + for (i1=start; i1height-2; i1++) + { + /* Find empty gaps */ + if (!(present[i1+1] & EDGE_RIGHT)) + { + int increment; /* fractional part */ + int denom; /* denominator of fraction */ + int step; /* integral step */ + int frac; /* fractional step */ + int reverse; + + /* find bottom of gap */ + i2 = i1+2; + while (!(present[i2] & EDGE_RIGHT) && i2 < b->height) i2++; + + if (i2 < b->height) + { + denom = i2-i1; + x1 = b->data[i1].right; + x2 = b->data[i2].right; + step = (x2-x1)/denom; + frac = x2-x1 - step*denom; + if (frac < 0) + { + frac = -frac; + reverse = 1; + } + else + reverse = 0; + + increment = 0; + for (i=i1+1; i= denom) + { + increment -= denom; + x1 += reverse ? -1 : 1; + } + if (reverse && increment != 0) + b->data[i].right = x1 - 1; + else + b->data[i].right = x1; + } + } + i1 = i2-1; /* advance to next possibility */ + } + } + +} + +static void +airbrush_blob_make_convex (AirBrushBlob *b, EdgeType *present) +{ + int x1, x2, y1, y2, i1, i2; + int i; + int start; + + /* Walk through edges, deleting points that aren't on convex hull */ + + start = 0; + while (!(present[start])) start++; + + /* left edge */ + + i1 = start-1; + i2 = start; + x1 = b->data[start].left - b->data[start].right; + y1 = 0; + + for (i=start+1;iheight;i++) + { + if (!(present[i] & EDGE_LEFT)) + continue; + + x2 = b->data[i].left - b->data[i2].left; + y2 = i-i2; + + while (x2*y1 - x1*y2 < 0) /* clockwise rotation */ + { + present[i2] &= ~EDGE_LEFT; + i2 = i1; + while (!(present[--i1] & EDGE_LEFT) && i1>=start); + + if (i1data[start].left - b->data[start].right; + y1 = 0; + } + else + { + x1 = b->data[i2].left - b->data[i1].left; + y1 = i2 - i1; + } + x2 = b->data[i].left - b->data[i2].left; + y2 = i - i2; + } + x1 = x2; + y1 = y2; + i1 = i2; + i2 = i; + } + + /* Right edge */ + + i1 = start -1; + i2 = start; + x1 = b->data[start].right - b->data[start].left; + y1 = 0; + + for (i=start+1;iheight;i++) + { + if (!(present[i] & EDGE_RIGHT)) + continue; + + x2 = b->data[i].right - b->data[i2].right; + y2 = i-i2; + + while (x2*y1 - x1*y2 > 0) /* counter-clockwise rotation */ + { + present[i2] &= ~EDGE_RIGHT; + i2 = i1; + while (!(present[--i1] & EDGE_RIGHT) && i1>=start); + + if (i1data[start].right - b->data[start].left; + y1 = 0; + } + else + { + x1 = b->data[i2].right - b->data[i1].right; + y1 = i2 - i1; + } + x2 = b->data[i].right - b->data[i2].right; + y2 = i - i2; + } + x1 = x2; + y1 = y2; + i1 = i2; + i2 = i; + } + + airbrush_blob_fill (b, present); +} + +AirBrushBlob * +airbrush_blob_convex_union (AirBrushBlob *b1, AirBrushBlob *b2) +{ + AirBrushBlob *result; + int y; + int i, j; + EdgeType *present; + + + /* Create the storage for the result */ + + y = MIN(b1->y,b2->y); + result = airbrush_blob_new (y, MAX(b1->y+b1->height,b2->y+b2->height)-y); + + if (result->height == 0) + return result; + + present = g_new0 (EdgeType, result->height); + + /* Initialize spans from original objects */ + + for (i=0, j=b1->y-y; iheight; i++,j++) + { + if (b1->data[i].right >= b1->data[i].left) + { + present[j] = EDGE_LEFT | EDGE_RIGHT; + result->data[j].left = b1->data[i].left; + result->data[j].right = b1->data[i].right; + } + } + + for (i=0, j=b2->y-y; iheight; i++,j++) + { + if (b2->data[i].right >= b2->data[i].left) + { + if (present[j]) + { + if (result->data[j].left > b2->data[i].left) + result->data[j].left = b2->data[i].left; + if (result->data[j].right < b2->data[i].right) + result->data[j].right = b2->data[i].right; + } + else + { + present[j] = EDGE_LEFT | EDGE_RIGHT; + result->data[j].left = b2->data[i].left; + result->data[j].right = b2->data[i].right; + } + } + } + + airbrush_blob_make_convex (result, present); + + g_free (present); + return result; +} + +/* You must be able to divide TABLE_SIZE with 4*/ + +#define TABLE_SIZE 256 +#define TABLE_QUARTER TABLE_SIZE/4 + +#define ELLIPSE_SHIFT 2 +#define TABLE_SHIFT 14 +#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) + +static int trig_initialized = 0; +static int trig_table[TABLE_SIZE]; +#define POWFAC 0.999 + + + +/* Scan convert an ellipse specified by _offsets_ of major and + minor axes, and by center into a airbrush_blob */ + +/* Warning UGLY code ahead :-)*/ + +AirBrushBlob * +airbrush_blob_ellipse (double xc, double yc, double xt, double yt, double xr, double yr, double xb, double yb, double xl, double yl) +{ + int i; + AirBrushBlob *rma, *rmi, *rtot; + gint maxyma, minyma, maxymi, minymi; + gint step; + double max_radius; + double ma_ang1, ma_ang2; + double x1, x2, y1, y2; + double xtotma, ytotma; + double xcma, ycma, ytma, xrma, ybma, xlma; + double xcmi, ycmi, ytmi, xrmi, ybmi, xlmi; + double mapoint_i, mapoint; + + gint xcma_shift, ycma_shift; + gint xcmi_shift, ycmi_shift; + gint ytma_shift, ytmi_shift; + gint xrma_shift, xrmi_shift; + gint ybma_shift, ybmi_shift; + gint xlma_shift, xlmi_shift; + + EdgeType *presentma; + EdgeType *presentmi; + + + if ((yt == yb) && (xr == xl) && (yt == xr)) + { + /*Zero*/ + + ytma = ytmi = xrma = xrmi = ybma = ybmi = xlma = xlmi = yt; + ycma = ycmi = yc; + xcma = xcmi = xc; + + } + else if (xr == xl) + { + if (yt > yb) + { + /*South*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = xl; + mapoint_i = (((yt * yt) / yb) - yt); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc; + ycma = yc + mapoint; + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = xl/2; + xcmi = xc; + ycmi = yc - mapoint/2; + + } + else + { + /*North*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = xl; + mapoint_i = (((yb * yb) / yt) - yb); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc; + ycma = yc - mapoint; + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = xl/2; + xcmi = xc; + ycmi = yc + mapoint/2; + + } + } + else if (yt == yb) + { + if (xr > xl) + { + /*East*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = yt; + mapoint_i = (((xr * xr) /xl) -xr); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = mapoint + xc; + ycma = yc; + + /*The Min Circle*/ + ytmi = ybmi = xrmi = xlmi = yt/2; + xcmi = xc - mapoint/2; + ycmi = yc; + + } + else + { + /*West*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = yt; + mapoint_i = (((xl * xl) /xr) - xl); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - mapoint; + ycma = yc; + + + /*The Min Circle*/ + ytmi = ybmi = xrmi = xlmi = yt/2; + xcmi = xc + mapoint/2; + ycmi = yc; + + } + } + else if ((yt > yb) && (xr > xl)) + { + /*SouthEast*/ + + /*The Max circle*/ + ma_ang1 = atan(yt/xr); + x1 = cos(ma_ang1) * xl; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xr; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yt * yt + xr * xr) / hypot(yb, xl)) - hypot(yt, xr)); + mapoint = mapoint_i * pow(POWFAC , mapoint_i); + xcma = xc + (cos(ma_ang1) * mapoint); + ycma = yc + (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yb, xl)/2; + xcmi = xc - (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc - (sin(ma_ang1) * mapoint * 0.5); + + } + else if ((yt > yb) && (xl > xr)) + { + /*SouthWest*/ + + /*The Max circle*/ + ma_ang1 = atan(yt/xl); + x1 = cos(ma_ang1) * xr; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xl; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yt * yt + xl * xl) / hypot(yb, xr)) - hypot(yt, xl)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - (cos(ma_ang1) * mapoint); + ycma = yc + (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yb, xr)/2; + xcmi = xc + (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc - (sin(ma_ang1) * mapoint * 0.5); + + } + else if ((yb > yt) && (xl > xr)) + { + /*NorthWest*/ + + /*The Max circle*/ + ma_ang1 = atan(yb/xl); + x1 = cos(ma_ang1) * xl; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xr; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yb * yb + xl * xl) / hypot(yt, xr)) - hypot(yb, xl)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - (cos(ma_ang1) * mapoint); + ycma = yc - (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yt, xr)/2; + xcmi = xc + (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc + (sin(ma_ang1) * mapoint * 0.5); + + } + else +/*if ((yb > yt) && (xr > xl))*/ + { + /*NorthEast*/ + + /*The Max circle*/ + ma_ang1 = atan(yb/xr); + x1 = cos(ma_ang1) * xr; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xl; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yb * yb + xr * xr) / hypot(yt, xl)) - hypot(yb, xr)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc + (cos(ma_ang1) * mapoint); + ycma = yc - (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yt, xl)/2; + xcmi = xc - (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc + (sin(ma_ang1) * mapoint * 0.5); + + } + if (ytmi <= 0) + { + ytmi = ybmi = xrmi = xlmi = 1; + } + + if (ytma <= 0) + { + ytma = ybma = xrma = xlma = 1; + } + + if (!trig_initialized) + { + trig_initialized = 1; + for (i=0; i<256; i++) + trig_table[i] = 0.5 + sin(i * (M_PI / 128.)) * (1 << TABLE_SHIFT); + } + + +/*Make the Max circle*/ + + maxyma = ceil (ycma + fabs (ytma)); + minyma = floor (ycma - fabs (ybma)); + + + + rma = airbrush_blob_new (minyma, maxyma - minyma + 1); + + + presentma = g_new0 (EdgeType, rma->height); + + max_radius = ytma; + + step = TABLE_SIZE; + + while (step > 1 && (TABLE_SIZE / step < 4*max_radius)) + step >>= 1; + + /* Fill in the edge points */ + + xcma_shift = 0.5 + xcma * (1 << TOTAL_SHIFT); + ycma_shift = 0.5 + ycma * (1 << TOTAL_SHIFT); + ytma_shift = 0.5 + ytma * (1 << ELLIPSE_SHIFT); + xrma_shift = 0.5 + xrma * (1 << ELLIPSE_SHIFT); + ybma_shift = 0.5 + ybma * (1 << ELLIPSE_SHIFT); + xlma_shift = 0.5 + xlma * (1 << ELLIPSE_SHIFT); + + + for (i = 0 ; i < TABLE_SIZE ; i += step) + { + + gint x, y, yi, dydi; + + gint s = trig_table[i]; + gint c = trig_table[(TABLE_SIZE + TABLE_SIZE/4 - i) % TABLE_SIZE]; + + if (i < TABLE_QUARTER ) + { + x = (xcma_shift + c * xrma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycma_shift + s * ytma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = yi - rma->y; + dydi = 1; + + } + else if ( i < (2 * TABLE_QUARTER) ) + { + x = (xcma_shift + c * xlma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycma_shift + s * ytma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + + y = yi - rma->y; + + dydi = -1; + } + else if ( i < (3 * TABLE_QUARTER) ) + { + x = (xcma_shift + c * xlma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycma_shift + s * ybma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = yi - rma->y; + dydi = -1; + + } + else + { + x = (xcma_shift + c * xrma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = ((ycma_shift + s * ybma_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT) - rma->y; + + dydi = 1; + + } + + + + + if (dydi <= 0) /* left edge */ + { + if (presentma[y] & EDGE_LEFT) + { + rma->data[y].left = MIN (rma->data[y].left, x); + } + else + { + presentma[y] |= EDGE_LEFT; + rma->data[y].left = x; + } + } + + if (dydi > 0) /* right edge */ + { + if (presentma[y] & EDGE_RIGHT) + { + rma->data[y].right = MAX (rma->data[y].right, x); + } + else + { + presentma[y] |= EDGE_RIGHT; + rma->data[y].right = x; + } + } + } + + /* Now fill in missing points */ + + airbrush_blob_fill (rma, presentma); + g_free (presentma); + +/*Make the Min circle*/ + + + maxymi = ceil (ycmi + fabs (ytmi)); + minymi = floor (ycmi - fabs (ybmi)); + + + rmi = airbrush_blob_new (minymi, maxymi - minymi + 1); + + + presentmi = g_new0 (EdgeType, rmi->height); + + max_radius = ytmi; + + step = TABLE_SIZE; + + while (step > 1 && (TABLE_SIZE / step < 4*max_radius)) + step >>= 1; + + /* Fill in the edge points */ + + xcmi_shift = 0.5 + xcmi * (1 << TOTAL_SHIFT); + ycmi_shift = 0.5 + ycmi * (1 << TOTAL_SHIFT); + ytmi_shift = 0.5 + ytmi * (1 << ELLIPSE_SHIFT); + xrmi_shift = 0.5 + xrmi * (1 << ELLIPSE_SHIFT); + ybmi_shift = 0.5 + ybmi * (1 << ELLIPSE_SHIFT); + xlmi_shift = 0.5 + xlmi * (1 << ELLIPSE_SHIFT); + + + + for (i = 0 ; i < TABLE_SIZE ; i += step) + { + + gint x, y, yi, dydi; + + gint s = trig_table[i]; + gint c = trig_table[(TABLE_SIZE + TABLE_SIZE/4 - i) % TABLE_SIZE]; + + if (i < TABLE_QUARTER ) + { + x = (xcmi_shift + c * xrmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycmi_shift + s * ytmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = yi - rmi->y; + dydi = 1; + + } + else if ( i < (2 * TABLE_QUARTER) ) + { + x = (xcmi_shift + c * xlmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycmi_shift + s * ytmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + + y = yi - rmi->y; + + dydi = -1; + } + else if ( i < (3 * TABLE_QUARTER) ) + { + x = (xcmi_shift + c * xlmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + yi = (ycmi_shift + s * ybmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = yi - rmi->y; + dydi = -1; + + } + else + { + x = (xcmi_shift + c * xrmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + y = ((ycmi_shift + s * ybmi_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT) - rmi->y; + + dydi = 1; + + } + + + + + if (dydi <= 0) /* left edge */ + { + if (presentmi[y] & EDGE_LEFT) + { + rmi->data[y].left = MIN (rmi->data[y].left, x); + } + else + { + presentmi[y] |= EDGE_LEFT; + rmi->data[y].left = x; + } + } + + if (dydi > 0) /* right edge */ + { + if (presentmi[y] & EDGE_RIGHT) + { + rmi->data[y].right = MAX (rmi->data[y].right, x); + } + else + { + presentmi[y] |= EDGE_RIGHT; + rmi->data[y].right = x; + } + } + } + + /* Now fill in missing points */ + + airbrush_blob_fill (rmi, presentmi); + g_free (presentmi); + + rtot = airbrush_blob_convex_union(rma, rmi); + + g_free (rma); + g_free (rmi); + + return rtot; +} + +void +airbrush_blob_bounds(AirBrushBlob *b, int *x, int *y, int *width, int *height) +{ + int i; + int x0, x1, y0, y1; + + i = 0; + while (iheight && b->data[i].left > b->data[i].right) + i++; + + if (iheight) + { + y0 = b->y + i; + x0 = b->data[i].left; + x1 = b->data[i].right + 1; + while (iheight && b->data[i].left <= b->data[i].right) + { + x0 = MIN(b->data[i].left, x0); + x1 = MAX(b->data[i].right+1, x1); + i++; + } + y1 = b->y + i; + } + else + { + x0 = y0 = 0; + x1 = y1 = 0; + } + + *x = x0; + *y = y0; + *width = x1 - x0; + *height = y1 - y0; +} + +void +airbrush_blob_dump(AirBrushBlob *b) { + + int i,j; + + + for (i=0; iheight; i++) + { + for (j=0;jdata[i].left;j++) + putchar(' '); + for (j=b->data[i].left;j<=b->data[i].right;j++) + putchar('*'); + putchar('\n'); + } +} + +/* This is just a first try to see how it works i.e ugly code :-) */ + + +AirBlob * +create_air_blob (double xc, + double yc, + double xt, + double yt, + double xr, + double yr, + double xb, + double yb, + double xl, + double yl, + double direction_abs, + double direction) +{ + AirBlob *air_blob; + double ma_ang1, ma_ang2; + double x1, x2, y1, y2; + double xtotma, ytotma; + double xcma, ycma, ytma, xrma, ybma, xlma; + double xcmi, ycmi, ytmi, xrmi, ybmi, xlmi; + double mapoint_i, mapoint; + + air_blob = airblob_new(1); + + air_blob->direction_abs = direction_abs; + air_blob->direction = direction; + air_blob->ycenter = yc; + air_blob->xcenter = xc; + + + + + if ((yt == yb) && (xr == xl) && (yt == xr)) + { + /*Zero*/ + + ytma = ytmi = xrma = xrmi = ybma = ybmi = xlma = xlmi = yt; + ycma = ycmi = yc; + xcma = xcmi = xc; + + air_blob->main_line.size = yt; + air_blob->minor_line.size = yt; + + air_blob->maincross_line.size = yt * 2; + air_blob->maincross_line.dist = 0.0; + + air_blob->minorcross_line.size = yt * 2; + air_blob->minorcross_line.dist = 0.0; + + air_blob->direction = M_PI_H; + + } + else if (xr == xl) + { + if (yt > yb) + { + /*South*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = xl; + mapoint_i = (((yt * yt) / yb) - yt); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc; + ycma = yc + mapoint; + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = xl/2; + xcmi = xc; + ycmi = yc - mapoint/2; + + air_blob->main_line.size = mapoint + xl; + air_blob->minor_line.size = mapoint/2 + xl/2; + + air_blob->maincross_line.size = xl * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xl/2; + air_blob->minorcross_line.dist = mapoint/2; + + + + } + else + { + /*North*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = xl; + mapoint_i = (((yb * yb) / yt) - yb); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc; + ycma = yc - mapoint; + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = xl/2; + xcmi = xc; + ycmi = yc + mapoint/2; + + + air_blob->main_line.size = mapoint + xl; + air_blob->minor_line.size = mapoint/2 + xl/2; + + air_blob->maincross_line.size = xl * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xl/2; + air_blob->minorcross_line.dist = mapoint/2; + + + } + } + else if (yt == yb) + { + if (xr > xl) + { + /*East*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = yt; + mapoint_i = (((xr * xr) /xl) -xr); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = mapoint + xc; + ycma = yc; + + /*The Min Circle*/ + ytmi = ybmi = xrmi = xlmi = yt/2; + xcmi = xc - mapoint/2; + ycmi = yc; + + air_blob->main_line.size = mapoint + yt; + air_blob->minor_line.size = mapoint/2 + yt/2; + + air_blob->maincross_line.size = xl * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xl/2; + air_blob->minorcross_line.dist = mapoint/2; + + + } + else + { + /*West*/ + + /*The Max circle*/ + ytma = ybma = xrma = xlma = yt; + mapoint_i = (((xl * xl) /xr) - xl); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - mapoint; + ycma = yc; + + + /*The Min Circle*/ + ytmi = ybmi = xrmi = xlmi = yt/2; + xcmi = xc + mapoint/2; + ycmi = yc; + + air_blob->main_line.size = mapoint + yt; + air_blob->minor_line.size = mapoint/2 + yt/2; + + air_blob->maincross_line.size = xl * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xl/2; + air_blob->minorcross_line.dist = mapoint/2; + + + } + } + else if ((yt > yb) && (xr > xl)) + { + /*SouthEast*/ + + /*The Max circle*/ + ma_ang1 = atan(yt/xr); + x1 = cos(ma_ang1) * xl; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xr; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yt * yt + xr * xr) / hypot(yb, xl)) - hypot(yt, xr)); + mapoint = mapoint_i * pow(POWFAC , mapoint_i); + xcma = xc + (cos(ma_ang1) * mapoint); + ycma = yc + (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yb, xl)/2; + xcmi = xc - (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc - (sin(ma_ang1) * mapoint * 0.5); + + + air_blob->main_line.size = mapoint + xlma; + air_blob->minor_line.size = mapoint/2 + xlmi; + + + air_blob->maincross_line.size = xlma * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xlmi; + air_blob->minorcross_line.dist = mapoint/2; + + + + + } + else if ((yt > yb) && (xl > xr)) + { + /*SouthWest*/ + + /*The Max circle*/ + ma_ang1 = atan(yt/xl); + x1 = cos(ma_ang1) * xr; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xl; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yt * yt + xl * xl) / hypot(yb, xr)) - hypot(yt, xl)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - (cos(ma_ang1) * mapoint); + ycma = yc + (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yb, xr)/2; + xcmi = xc + (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc - (sin(ma_ang1) * mapoint * 0.5); + + air_blob->main_line.size = mapoint + xlma; + air_blob->minor_line.size = mapoint/2 + xlmi; + + air_blob->maincross_line.size = xlma * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xlmi; + air_blob->minorcross_line.dist = mapoint/2; + + } + else if ((yb > yt) && (xl > xr)) + { + /*NorthWest*/ + + /*The Max circle*/ + ma_ang1 = atan(yb/xl); + x1 = cos(ma_ang1) * xl; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xr; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yb * yb + xl * xl) / hypot(yt, xr)) - hypot(yb, xl)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc - (cos(ma_ang1) * mapoint); + ycma = yc - (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yt, xr)/2; + xcmi = xc + (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc + (sin(ma_ang1) * mapoint * 0.5); + + air_blob->main_line.size = mapoint + xlma; + air_blob->minor_line.size = mapoint/2 + xlmi; + + air_blob->maincross_line.size = xlma * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xlmi; + air_blob->minorcross_line.dist = mapoint/2; + + + + + } + else +/*if ((yb > yt) && (xr > xl))*/ + { + /*NorthEast*/ + + /*The Max circle*/ + ma_ang1 = atan(yb/xr); + x1 = cos(ma_ang1) * xr; + y1 = sin(ma_ang1) * yt; + ma_ang2 = M_PI/2 - ma_ang1; + x2 = cos(ma_ang2) * xl; + y2 = sin(ma_ang2) * yb; + xtotma = x1 + x2; + ytotma = y1 + y2; + ytma = ybma = xrma = xlma = hypot(ytotma, xtotma)/2; + mapoint_i = (((yb * yb + xr * xr) / hypot(yt, xl)) - hypot(yb, xr)); + mapoint = mapoint_i * pow(POWFAC, mapoint_i); + xcma = xc + (cos(ma_ang1) * mapoint); + ycma = yc - (sin(ma_ang1) * mapoint); + + /*The Min Circle*/ + ytmi = xrmi = ybmi = xlmi = hypot(yt, xl)/2; + xcmi = xc - (cos(ma_ang1) * mapoint * 0.5); + ycmi = yc + (sin(ma_ang1) * mapoint * 0.5); + + air_blob->main_line.size = mapoint + xlma; + air_blob->minor_line.size = mapoint/2 + xlmi; + + + air_blob->maincross_line.size = xlma * 2; + air_blob->maincross_line.dist = mapoint; + + air_blob->minorcross_line.size = xlmi; + air_blob->minorcross_line.dist = mapoint/2; + + + } + + return air_blob; +} + +AirLine * +create_air_line(AirBlob *airblob) +{ + + int i; + double xcenter, ycenter; + + double direction; + + double masupport, misupport; + double ma_angsupport, mi_angsupport, iang; + + int min_x, max_x; + int min_y, max_y; + + AirLine *airline; + + /* Yes I know I can do a cal of number of lines, but it is for + the moment much easier to just set a side mem for 16 lines + */ + + airline = airline_new(1); + + xcenter = airblob->xcenter; + ycenter = airblob->ycenter; + + airline->xcenter = xcenter/SUBSAMPLE; + airline->ycenter = ycenter/SUBSAMPLE; + + direction = airblob->direction_abs; + + if(direction == 0.0) + + { + + airline->line[0].x = (xcenter + airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[0].y = (ycenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[1].x = (xcenter - airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter + airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter)/SUBSAMPLE; + airline->line[3].x = (xcenter - airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter)/SUBSAMPLE; + + airline->line[4].x = (xcenter + airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter - airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->nlines = 6; + + + } + + else if(direction == M_PI_H) + + { + + airline->line[0].x = (xcenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[0].y = (ycenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[1].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter)/SUBSAMPLE; + airline->line[2].y = (ycenter - airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter)/SUBSAMPLE; + airline->line[3].y = (ycenter + airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter + airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter - airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->nlines = 6; + + } + + + else if(direction == M_PI) + + { + airline->line[0].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[0].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[1].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter - airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter)/SUBSAMPLE; + airline->line[3].x = (xcenter + airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter)/SUBSAMPLE; + + airline->line[4].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->nlines = 6; + + } + + else if(direction == -M_PI_H) + + { + airline->line[0].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[0].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[1].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter)/SUBSAMPLE; + airline->line[2].y = (ycenter + airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter)/SUBSAMPLE; + airline->line[3].y = (ycenter - airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter + airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter - airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + } + + + else if(direction == -M_PI) + + { + airline->line[0].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[0].y = (ycenter + airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[1].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[1].y = (ycenter - airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->line[2].x = (xcenter - airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter)/SUBSAMPLE; + airline->line[3].x = (xcenter + airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter)/SUBSAMPLE; + + airline->line[4].x = (xcenter - airblob->maincross_line.dist)/SUBSAMPLE; + airline->line[4].y = (ycenter - airblob->maincross_line.size/2)/SUBSAMPLE; + airline->line[5].x = (xcenter + airblob->minorcross_line.dist)/SUBSAMPLE; + airline->line[5].y = (ycenter + airblob->minorcross_line.size/2)/SUBSAMPLE; + + airline->nlines = 6; + + } + + + else if ((direction < M_PI) && (direction > M_PI_H)) + + { + + + masupport = hypot(airblob->maincross_line.dist, airblob->maincross_line.size/2); + misupport = hypot(airblob->minorcross_line.dist,airblob->minorcross_line.size/2); + + ma_angsupport = atan(airblob->maincross_line.size/2/airblob->maincross_line.dist); + mi_angsupport = atan(airblob->minorcross_line.size/2/airblob->minorcross_line.dist); + + iang = airblob->direction_abs - M_PI_H; + + airline->line[0].x = (xcenter - sin(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[0].y = (ycenter - cos(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[1].x = (xcenter + sin(iang + mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[1].y = (ycenter + cos(iang + mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[2].x = (xcenter - sin(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter - cos(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter + sin(iang) * airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter + cos(iang) * airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter - sin(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[4].y = (ycenter - cos(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[5].x = (xcenter + sin(iang - mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[5].y = (ycenter + cos(iang - mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[6].x = (xcenter - sin(iang + ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[6].y = (ycenter - cos(iang + ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[7].x = (xcenter - sin(iang - ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[7].y = (ycenter - cos(iang - ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + + + + airline->nlines = 8; + + } + + else if ((direction < M_PI_H) && (direction > 0.0)) + + { + + + masupport = hypot(airblob->maincross_line.dist, airblob->maincross_line.size/2); + misupport = hypot(airblob->minorcross_line.dist,airblob->minorcross_line.size/2); + + ma_angsupport = atan(airblob->maincross_line.size/2/airblob->maincross_line.dist); + mi_angsupport = atan(airblob->minorcross_line.size/2/airblob->minorcross_line.dist); + + iang = airblob->direction_abs; + + airline->line[0].x = (xcenter + cos(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[0].y = (ycenter - sin(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[1].x = (xcenter - cos(iang + mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[1].y = (ycenter + sin(iang + mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[2].x = (xcenter + cos(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter - sin(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter - cos(iang) * airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter + sin(iang) * airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter + cos(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[4].y = (ycenter - sin(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[5].x = (xcenter - cos(iang - mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[5].y = (ycenter + sin(iang - mi_angsupport) * misupport)/SUBSAMPLE; + + + airline->line[6].x = (xcenter + cos(iang + ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[6].y = (ycenter - sin(iang + ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[7].x = (xcenter + cos(iang - ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + airline->line[7].y = (ycenter - sin(iang - ma_angsupport/2.) * (masupport + (airblob->main_line.size - masupport)/2.0))/SUBSAMPLE; + + + + + airline->nlines = 8; + + } + + + else if ((direction < 0.0) && (direction > -M_PI_H)) + + { + + + masupport = hypot(airblob->maincross_line.dist, airblob->maincross_line.size/2); + misupport = hypot(airblob->minorcross_line.dist,airblob->minorcross_line.size/2); + + ma_angsupport = atan(airblob->maincross_line.size/2/airblob->maincross_line.dist); + mi_angsupport = atan(airblob->minorcross_line.size/2/airblob->minorcross_line.dist); + + iang = fabs(airblob->direction_abs); + + airline->line[0].x = (xcenter + cos(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[0].y = (ycenter + sin(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[1].x = (xcenter - cos(iang + mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[1].y = (ycenter - sin(iang + mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[2].x = (xcenter + cos(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter + sin(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter - cos(iang) * airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter - sin(iang) * airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter + cos(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[4].y = (ycenter + sin(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[5].x = (xcenter - cos(iang - mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[5].y = (ycenter - sin(iang - mi_angsupport) * misupport)/SUBSAMPLE; + + airline->nlines = 6; + + } + + + else if ((direction < -M_PI_H) && (direction > -M_PI)) + + { + + + masupport = hypot(airblob->maincross_line.dist, airblob->maincross_line.size/2); + misupport = hypot(airblob->minorcross_line.dist,airblob->minorcross_line.size/2); + + ma_angsupport = atan(airblob->maincross_line.size/2/airblob->maincross_line.dist); + mi_angsupport = atan(airblob->minorcross_line.size/2/airblob->minorcross_line.dist); + + iang = fabs(airblob->direction_abs) - M_PI_H; + + airline->line[0].x = (xcenter - sin(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[0].y = (ycenter + cos(iang + ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[1].x = (xcenter + sin(iang + mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[1].y = (ycenter - cos(iang + mi_angsupport) * misupport)/SUBSAMPLE; + + airline->line[2].x = (xcenter - sin(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[2].y = (ycenter + cos(iang) * airblob->main_line.size)/SUBSAMPLE; + airline->line[3].x = (xcenter + sin(iang) * airblob->minor_line.size)/SUBSAMPLE; + airline->line[3].y = (ycenter - cos(iang) * airblob->minor_line.size)/SUBSAMPLE; + + airline->line[4].x = (xcenter - sin(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[4].y = (ycenter + cos(iang - ma_angsupport) * masupport)/SUBSAMPLE; + airline->line[5].x = (xcenter + sin(iang - mi_angsupport) * misupport)/SUBSAMPLE; + airline->line[5].y = (ycenter - cos(iang - mi_angsupport) * misupport)/SUBSAMPLE; + + airline->nlines = 6; + + } + else + { + printf("Hmm a bug in the create_air_line"); + } + + + min_x = max_x = airline->xcenter; + min_y = max_y = airline->ycenter; + + + + for (i=0; i < airline->nlines ; i++) + { + min_x = MIN(airline->line[i].x, min_x); + max_x = MAX(airline->line[i].x, max_x); + min_y = MIN(airline->line[i].y, min_y); + max_y = MAX(airline->line[i].y, max_y); + } + + airline->width = max_x - min_x + 1; + airline->height = max_y - min_y + 1; + + airline->min_x = min_x; + airline->min_y = min_y; + airline->max_x = max_x; + airline->max_y = max_y; + + return airline; + +} + +AirBlob * +trans_air_blob(AirBlob *airblob_last, AirBlob *airblob_present, double dist, int xcen, int ycen) +{ + + AirBlob *trans_airblob; + + double direction_last_abs, direction_present_abs; + double idirection_abs; + + double direction_last, direction_present; + double idirection; + + double main_line_present, main_line_last; + + + double minor_line_present, minor_line_last; + + + double maincross_line_dist_present, maincross_line_dist_last; + + double minorcross_line_dist_present, minorcross_line_dist_last; + + + double maincross_line_size_present, maincross_line_size_last; + + double minorcross_line_size_present, minorcross_line_size_last; + + + + trans_airblob = airblob_new(1); + + direction_last_abs = airblob_last->direction_abs + M_PI; + direction_present_abs = airblob_present->direction_abs + M_PI; + + idirection_abs = direction_present_abs - direction_last_abs; + + direction_last = airblob_last->direction + M_PI_H; + direction_present = airblob_present->direction + M_PI_H; + + idirection = direction_present - direction_last; + + main_line_present = airblob_present->main_line.size; + main_line_last = airblob_last->main_line.size; + + minor_line_present = airblob_present->minor_line.size; + minor_line_last = airblob_last->minor_line.size; + + maincross_line_dist_present = airblob_present->maincross_line.dist; + maincross_line_size_present = airblob_present->maincross_line.size; + minorcross_line_dist_present = airblob_present->minorcross_line.dist; + minorcross_line_size_present = airblob_present->minorcross_line.size; + + maincross_line_dist_last = airblob_last->maincross_line.dist; + maincross_line_size_last = airblob_last->maincross_line.size; + minorcross_line_dist_last = airblob_last->minorcross_line.dist; + minorcross_line_size_last = airblob_last->minorcross_line.size; + + + + + /* + Now we have to guess a little :-). Why? + Well we can't know if the users is painting + up/down or if she is painting a circle at high speed. + As you may notice it be so that the last airblob has + a direction more or less M_PI rad differernt from the + present airblob. But we can't know if she tured the + airbrush quickly (so that there was no mouse capture + during the turn) or if she paints just up and down the + same direction. + + There for we guess that we want to pait up and down + when the diff in direction is bigger than 171 deg and + smaller than 189 deg. + + */ + + if ((fabs(idirection_abs) > (M_PI - 0.1571)) && (fabs(idirection_abs) < (M_PI + 0.1571))) + { + /* We asume that the artist meant to paint in a "strait line" by just tilting the airbrush*/ + + idirection_abs = idirection_abs - M_PI; + + if ((idirection_abs * dist) > (idirection_abs/2)) + { + if ((direction_present_abs - idirection_abs * dist) > 2 * M_PI) + { + trans_airblob->direction_abs = direction_present_abs - idirection_abs * dist - 2 * M_PI - M_PI; + } + else if ((direction_present_abs - idirection_abs * dist) < 0.0) + { + trans_airblob->direction_abs = direction_present_abs - idirection_abs * dist + 2 * M_PI - M_PI; + } + else + { + trans_airblob->direction_abs = direction_present_abs - idirection_abs * dist - M_PI; + } + } + else + { + if ((direction_present_abs + idirection_abs * dist) > 2 * M_PI) + { + trans_airblob->direction_abs = direction_present_abs + idirection_abs * dist - 2 * M_PI - M_PI; + } + else if ((direction_present_abs + idirection_abs * dist) < 0.0) + { + trans_airblob->direction_abs = direction_present_abs + idirection_abs * dist + 2 * M_PI - M_PI; + } + else + { + trans_airblob->direction_abs = direction_present_abs + idirection_abs * dist - M_PI; + } + } + + trans_airblob->main_line.size = main_line_last + ((minor_line_present - main_line_last) * dist); + trans_airblob->minor_line.size = minor_line_last + ((main_line_present - minor_line_last) * dist); + + trans_airblob->maincross_line.dist = maincross_line_dist_last + ((minorcross_line_dist_present - maincross_line_dist_last) * dist); + trans_airblob->maincross_line.size = maincross_line_size_last + ((minorcross_line_size_present - maincross_line_size_last) * dist); + + trans_airblob->minorcross_line.dist = minorcross_line_dist_last + ((maincross_line_dist_present - minorcross_line_dist_last) * dist); + trans_airblob->minorcross_line.size = minorcross_line_size_last + ((maincross_line_size_present - minorcross_line_size_last) * dist); + + } + + else if (fabs(idirection_abs) < (M_PI - 0.1571)) + { + if ((direction_last_abs + idirection_abs * dist) > 2*M_PI) + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist - 2 * M_PI - M_PI; + } + else if((direction_last_abs + idirection_abs * dist) < 0.0) + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist + 2 * M_PI - M_PI; + } + else + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist - M_PI; + } + + trans_airblob->main_line.size = main_line_last + ((main_line_present - main_line_last) * dist); + trans_airblob->minor_line.size = minor_line_last + ((minor_line_present - minor_line_last) * dist); + + trans_airblob->maincross_line.dist = maincross_line_dist_last + ((maincross_line_dist_present - maincross_line_dist_last) * dist); + trans_airblob->maincross_line.size = maincross_line_size_last + ((maincross_line_size_present - maincross_line_size_last) * dist); + + trans_airblob->minorcross_line.dist = minorcross_line_dist_last + ((minorcross_line_dist_present - minorcross_line_dist_last) * dist); + trans_airblob->minorcross_line.size = minorcross_line_size_last + ((minorcross_line_size_present - minorcross_line_size_last) * dist); + } + else + { + + /* We asume that the artist always travels the shortest way around the "clock" */ + + idirection_abs = idirection_abs - M_PI; + + if ((direction_last_abs + idirection_abs * dist) > 2*M_PI) + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist - 2 * M_PI - M_PI; + } + else if((direction_last_abs + idirection_abs * dist) < 0.0) + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist + 2 * M_PI - M_PI; + } + else + { + trans_airblob->direction_abs = direction_last_abs + idirection_abs * dist - M_PI; + } + + trans_airblob->main_line.size = main_line_last + ((main_line_present - main_line_last) * dist); + trans_airblob->minor_line.size = minor_line_last + ((minor_line_present - minor_line_last) * dist); + + trans_airblob->maincross_line.dist = maincross_line_dist_last + ((maincross_line_dist_present - maincross_line_dist_last) * dist); + trans_airblob->maincross_line.size = maincross_line_size_last + ((maincross_line_size_present - maincross_line_size_last) * dist); + + trans_airblob->minorcross_line.dist = minorcross_line_dist_last + ((minorcross_line_dist_present - minorcross_line_dist_last) * dist); + trans_airblob->minorcross_line.size = minorcross_line_size_last + ((minorcross_line_size_present - minorcross_line_size_last) * dist); + + } + + + trans_airblob->xcenter = xcen * SUBSAMPLE; + trans_airblob->ycenter = ycen * SUBSAMPLE; + + + return trans_airblob; + +} + +int +number_of_steps(int x0, int y0, int x1, int y1) +{ + + + int dx, dy; + + dx = abs(x0 - x1); + + dy = abs(y0 - y1); + + if (dy > dx) + { + return dy + 1; + } + else + { + return dx + 1; + } +} + + + + + + + + + + + + + + + diff --git a/app/tools/airbrush_blob.h b/app/tools/airbrush_blob.h new file mode 100644 index 0000000000..55c70f4577 --- /dev/null +++ b/app/tools/airbrush_blob.h @@ -0,0 +1,154 @@ +/* airbrush_blob.h: routines for manipulating scan converted convex + * polygons. + * + * Copyright 1998, Owen Taylor + * + * > Please contact the above author before modifying the copy < + * > of this file in the GIMP distribution. Thanks. < + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * +*/ + +#ifndef __AIRBRUSHBLOB_H__ +#define __AIRBRUSHBLOB_H__ + +typedef enum { + CROSS = 0, + CROSS_LEFT = 1, + CROSS_RIGHT = 2, + CROSS_WHOLE_LINE = 3, + CROSS_NORMAL = 4 +} CrossType; + +typedef enum { + RIGHT_LEFT = 0, + LEFT_RIGHT = 1, + TOP_BOT = 2, + BOT_TOP = 3, + NONE = 4 +} MoveType; + +/* The AirBlob, which is a abstract of a real AirBrushBlob */ + +typedef struct _AirBlob AirBlob; +typedef struct _AirPoint AirPoint; +typedef struct _SupportLine SupportLine; + + +struct _AirPoint { + int x; + int y; +}; + +struct _SupportLine { + double size; + double dist; +}; + +struct _AirBlob { + double direction_abs; + double direction; + double ycenter; + double xcenter; + SupportLine main_line; + SupportLine minor_line; + SupportLine maincross_line; + SupportLine minorcross_line; +}; + + +/* The AirLine is a reslut of a AirBlob */ +typedef struct _AirLine AirLine; + +struct _AirLine { + int xcenter; + int ycenter; + AirPoint line[16]; + int min_x, min_y; + int max_x, max_y; + int width, height; + int nlines; +}; + + +typedef struct _AirBrushBlobPoint AirBrushBlobPoint; +typedef struct _AirBrushBlobSpan AirBrushBlobSpan; +typedef struct _AirBrushBlob AirBrushBlob; + +struct _AirBrushBlobPoint { + int x; + int y; +}; + +struct _AirBrushBlobSpan { + int left; + double angle_left; + double angle_left_abs; + double dist_left; + int right; + double angle_right; + double angle_right_abs; + double dist_right; + + CrossType cross_type; + int x_cross; + + int center; + double dist; + +}; + +struct _AirBrushBlob { + int y; + int height; + int width; + int min_x; + int max_x; + MoveType move; + double direction_abs; + double direction; + CrossType cross; + AirBrushBlobSpan data[1]; +}; + + +typedef struct _AirBrush AirBrush; + +struct _AirBrush { + AirBrushBlob airbrush_blob; + AirBlob airblob; +}; + + + + +AirBlob *create_air_blob (double xc, double yc, double xt, double yt, double xr, double yr, double xb, double yb, double xl, double yl, double direction_abs, double direction); +AirBlob *trans_air_blob(AirBlob *airblob_last, AirBlob *airblob_present, double dist, int xc, int yc); +AirLine *create_air_line(AirBlob *airblob); +int number_of_steps(int x0, int y0, int x1, int y1); + + + + + +AirBrushBlob *airbrush_blob_convex_union (AirBrushBlob *b1, AirBrushBlob *b2); +AirBrushBlob *airbrush_blob_ellipse (double xc, double yc, double xt, double yt, double xr, double yr, double xb, double yb, double xl, double yl); +void airbrush_blob_bounds (AirBrushBlob *b, int *x, int *y, int *width, int *height); + + + + +#endif /* __AIRBRUSHBLOB_H__ */ diff --git a/app/tools/tools.c b/app/tools/tools.c index 34ae9a224c..391b3eada6 100644 --- a/app/tools/tools.c +++ b/app/tools/tools.c @@ -53,6 +53,7 @@ #include "rect_select.h" #include "session.h" #include "smudge.h" +#include "xinput_airbrush.h" #include "text_tool.h" #include "threshold.h" #include "tools.h" @@ -488,7 +489,7 @@ ToolInfo tool_info[] = 24, N_("/Tools/Measure"), "", - (char **) measure_bits, + (char **) measure_bits, N_("Measure distances and angles"), "ContextHelp/measure", MEASURE, @@ -497,11 +498,27 @@ ToolInfo tool_info[] = NULL }, + { + NULL, + N_("Xinput Airbrush"), + 25, + N_("/Tools/XinputAirbrush"), + "A", + (char **) xinput_airbrush_bits, + N_("Natural Airbrush"), + "ContextHelp/xinput_airbrush", + XINPUT_AIRBRUSH, + tools_new_xinput_airbrush, + tools_free_xinput_airbrush, + NULL + }, + + /* Non-toolbox tools */ { NULL, N_("By Color Select"), - 25, + 26, N_("/Select/By Color..."), NULL, NULL, @@ -516,7 +533,7 @@ ToolInfo tool_info[] = { NULL, N_("Color Balance"), - 26, + 27, N_("/Image/Colors/Color Balance"), NULL, NULL, @@ -531,7 +548,7 @@ ToolInfo tool_info[] = { NULL, N_("Brightness-Contrast"), - 27, + 28, N_("/Image/Colors/Brightness-Contrast"), NULL, NULL, @@ -546,7 +563,7 @@ ToolInfo tool_info[] = { NULL, N_("Hue-Saturation"), - 28, + 29, N_("/Image/Colors/Hue-Saturation"), NULL, NULL, @@ -561,7 +578,7 @@ ToolInfo tool_info[] = { NULL, N_("Posterize"), - 29, + 30, N_("/Image/Colors/Posterize"), NULL, NULL, @@ -576,7 +593,7 @@ ToolInfo tool_info[] = { NULL, N_("Threshold"), - 30, + 31, N_("/Image/Colors/Threshold"), NULL, NULL, @@ -591,7 +608,7 @@ ToolInfo tool_info[] = { NULL, N_("Curves"), - 31, + 32, N_("/Image/Colors/Curves"), NULL, NULL, @@ -606,7 +623,7 @@ ToolInfo tool_info[] = { NULL, N_("Levels"), - 32, + 33, N_("/Image/Colors/Levels"), NULL, NULL, @@ -621,7 +638,7 @@ ToolInfo tool_info[] = { NULL, N_("Histogram"), - 33, + 34, N_("/Image/Histogram"), NULL, NULL, diff --git a/app/tools/xinput_airbrush.c b/app/tools/xinput_airbrush.c new file mode 100644 index 0000000000..c0838d4509 --- /dev/null +++ b/app/tools/xinput_airbrush.c @@ -0,0 +1,2448 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +Hmm a little + */ +#include +#include +#include +#include "appenv.h" +#include "drawable.h" +#include "draw_core.h" +#include "gimage_mask.h" +#include "gimprc.h" +#include "xinput_airbrush.h" +#include "paint_options.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "undo.h" +#include "airbrush_blob.h" +#include "gdisplay.h" + +#include "libgimp/gimpintl.h" + +#include "tile.h" /* ick. */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* M_PI */ + +#define SUBSAMPLE 8 + +#define DIST_SMOOTHER_BUFFER 10 +#define TIME_SMOOTHER_BUFFER 10 + +/* the XinputAirbrush structures */ + +typedef struct _XinputAirbrushTool XinputAirbrushTool; +struct _XinputAirbrushTool + { + DrawCore * core; /* Core select object */ + + AirBrush * last_airbrush; + AirBrushBlob * last_airbrush_blob; /* airbrush_blob for last cursor position */ + AirBlob * last_airblob; + + + int x1, y1; /* image space coordinate */ + int x2, y2; /* image space coords */ + + /* circular distance history buffer */ + gdouble dt_buffer[DIST_SMOOTHER_BUFFER]; + gint dt_index; + + /* circular timing history buffer */ + guint32 ts_buffer[TIME_SMOOTHER_BUFFER]; + gint ts_index; + + /* Direction and center point */ + double xcenter, ycenter; + double direction_abs; + double direction; + double c_direction; + double c_direction_abs; + + + gdouble last_time; /* previous time of a motion event */ + gdouble lastx, lasty; /* previous position of a motion event */ + gdouble last_lastx, last_lasty; /* The previous previous position of a motion event i.e pos of the blob */ + + gboolean init_velocity; + gboolean init_prepre; + + }; + + +typedef struct _XinputAirbrushOptions XinputAirbrushOptions; +struct _XinputAirbrushOptions + { + PaintOptions paint_options; + + double size; + double size_d; + GtkObject *size_w; + + double sensitivity; + double sensitivity_d; + GtkObject *sensitivity_w; + + double vel_sensitivity; + double vel_sensitivity_d; + GtkObject *vel_sensitivity_w; + + double tilt_sensitivity; + double tilt_sensitivity_d; + GtkObject *tilt_sensitivity_w; + + + double tilt_angle; + double tilt_angle_d; + GtkObject *tilt_angle_w; + + }; + + +/* the xinput_airbrush tool options */ +static XinputAirbrushOptions *xinput_airbrush_options = NULL; + +/* local variables */ + +/* undo blocks variables */ +static TileManager * undo_tiles = NULL; + +/* Tiles used to render the stroke at 1 byte/pp */ +static TileManager * canvas_tiles = NULL; + +/* Flat buffer that is used to used to render the dirty region + * for composition onto the destination drawable + */ +static TempBuf * canvas_buf = NULL; + + +/* local function prototypes */ + +static void xinput_airbrush_button_press (Tool *, GdkEventButton *, gpointer); +static void xinput_airbrush_button_release (Tool *, GdkEventButton *, gpointer); +static void xinput_airbrush_motion (Tool *, GdkEventMotion *, gpointer); +static void xinput_airbrush_cursor_update (Tool *, GdkEventMotion *, gpointer); +static void xinput_airbrush_control (Tool *, ToolAction, gpointer); + +static void time_smoother_add (XinputAirbrushTool* xinput_airbrush_tool, guint32 value); +static gdouble time_smoother_result (XinputAirbrushTool* xinput_airbrush_tool); +static void time_smoother_init (XinputAirbrushTool* xinput_airbrush_tool, guint32 initval); +static void dist_smoother_add (XinputAirbrushTool* xinput_airbrush_tool, gdouble value); +static gdouble dist_smoother_result (XinputAirbrushTool* xinput_airbrush_tool); +static void dist_smoother_init (XinputAirbrushTool* xinput_airbrush_tool, gdouble initval); + +static void xinput_airbrush_init (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + double x, + double y); + +static void xinput_airbrush_finish (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + int tool_id); + +static void xinput_airbrush_cleanup (void); + + +/*Mask functions*/ + +static void calc_angle (AirBrushBlob *airbrush_blob, + double xcenter, + double ycenter); + +static void calc_width (AirBrushBlob *airbrush_blob); + + +static void render_airbrush_line (AirBrushBlob *airbrush_blob, + guchar *dest, + int y, + int width, + XinputAirbrushTool *xinput_airbrush_tool); + + + +static void make_single_mask (AirBrushBlob *airbrush_blob, + XinputAirbrushTool *xinput_airbrush_tool, + MaskBuf *dest); + +static void make_stroke(AirBlob *airblob, + XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + guint last_value, + guint present_value); + + + +static void make_mask(AirLine *airline, + MaskBuf *brush_mask, + guint value); + + + + +static void print_mask(MaskBuf *dest); + + + + +/* Rendering functions */ + +static void xinput_airbrush_set_paint_area (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + int x, + int y, + int width, + int height); + +static void xinput_airbrush_paste (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + MaskBuf *brush_mask, + int x, + int y, + int width, + int height); + +static void xinput_airbrush_to_canvas_tiles (XinputAirbrushTool *xinput_airbrush_tool, + MaskBuf *brush_mask, + int brush_opacity); + +static void xinput_airbrush_canvas_tiles_to_canvas_buf(XinputAirbrushTool *xinput_airbrush_tool); + +static void xinput_airbrush_set_undo_tiles (GimpDrawable *drawable, + int x, + int y, + int w, + int h); + +static void xinput_airbrush_set_canvas_tiles(int x, + int y, + int w, + int h); + + + +/* Start of functions */ + + +static void + xinput_airbrush_options_reset (void) + { + XinputAirbrushOptions *options = xinput_airbrush_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->size_w), + options->size_d); + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->sensitivity_w), + options->sensitivity_d); + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w), + options->tilt_sensitivity_d); + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w), + options->vel_sensitivity_d); + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w), + options->tilt_angle_d); + } + +static XinputAirbrushOptions * + xinput_airbrush_options_new (void) + { + XinputAirbrushOptions *options; + + GtkWidget *table; + GtkWidget *abox; + GtkWidget *label; + GtkWidget *slider; + + /* the new xinput_airbrush tool options structure */ + options = (XinputAirbrushOptions *) g_malloc (sizeof (XinputAirbrushOptions)); + paint_options_init ((PaintOptions *) options, + XINPUT_AIRBRUSH, + xinput_airbrush_options_reset); + options->size = options->size_d = 4.4; + options->sensitivity = options->sensitivity_d = 1.0; + options->vel_sensitivity = options->vel_sensitivity_d = 0.8; + options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4; + options->tilt_angle = options->tilt_angle_d = 0.0; + + /*the main table*/ + + table = gtk_table_new (6, 2, FALSE); + gtk_box_pack_start (GTK_BOX (((ToolOptions *) options)->main_vbox), table, + FALSE, FALSE, 0); + + /*size slider*/ + label = gtk_label_new (_("Size:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (label); + + options->size_w = + gtk_adjustment_new (options->size_d, 0.0, 20.0, 1.0, 5.0, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->size_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_table_attach_defaults (GTK_TABLE (table), slider, 1, 2, 0, 1); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->size_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->size); + gtk_widget_show (slider); + + /*sens slider*/ + label = gtk_label_new (_("Sensitivity:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, + GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (label); + + options->sensitivity_w = + gtk_adjustment_new (options->sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->sensitivity_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_table_attach_defaults (GTK_TABLE (table), slider, 1, 2, 1, 2); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->sensitivity_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->sensitivity); + gtk_widget_show (slider); + + /*tilt sens slider*/ + label = gtk_label_new (_("Tilt")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Sensitivity:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0); + gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 2, 4, + GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (abox); + + options->tilt_sensitivity_w = + gtk_adjustment_new (options->tilt_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_sensitivity_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_container_add (GTK_CONTAINER (abox), slider); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->tilt_sensitivity_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->tilt_sensitivity); + gtk_widget_show (slider); + + + /*velocity sens slider*/ + label = gtk_label_new (_("Speed")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Sensitivity:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0); + gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 4, 6, + GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (abox); + + options->vel_sensitivity_w = + gtk_adjustment_new (options->vel_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->vel_sensitivity_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_container_add (GTK_CONTAINER (abox), slider); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->vel_sensitivity_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->vel_sensitivity); + gtk_widget_show (slider); + + + /*angle adjust slider*/ + label = gtk_label_new (_("Angle")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 6, 7, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Adjust:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 7, 8, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0); + gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 6, 8, + GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (abox); + + options->tilt_angle_w = + gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_container_add (GTK_CONTAINER (abox), slider); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->tilt_angle_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->tilt_angle); + + + + + gtk_widget_show_all (table); + + return options; + } + + + +static AirBlob * +xinput_airbrush_pen_ellipse (XinputAirbrushTool *xinput_airbrush_tool, gdouble x_center, gdouble y_center, + gdouble pressure, gdouble xtiltv, gdouble ytiltv) +{ + + gdouble xtilt, ytilt; + double height; + double tanx, tany; + double tanytop, tanxright, tanybot, tanxleft; + double sprayangle; + double distx, disty; + double ytop, xright, ybot, xleft; + + + + /*Fix av tilt negative when it should be positive*/ + + xtilt = xtiltv * -1.0; + ytilt = ytiltv * -1.0; + + /*Values that should be adjust able*/ + + height = 50.; + sprayangle = M_PI/12; + + /*Tan of x and y tilt plus spray angles x r/l and y t/b tan*/ + + tanx = tan(xtilt * M_PI / 2.0); + tany = tan(ytilt * M_PI / 2.0); + + + tanytop = tan((ytilt * M_PI / 2.0) + (sprayangle/2)); + tanxright = tan((xtilt * M_PI / 2.0) + (sprayangle/2)); + tanybot = tan((ytilt * M_PI / 2.0) - (sprayangle/2)); + tanxleft = tan((xtilt * M_PI / 2.0) - (sprayangle/2)); + + /* Offset from cursor due to tilt in x and y depening on the hight*/ + + distx = tanx * height; + disty = tany * height; + + /* Offset from center of blob in all for axies */ + + ytop = fabs(fabs(tanytop * height) - fabs(disty)); + xright = fabs(fabs(tanxright * height) - fabs(distx)); + ybot = fabs(fabs(tanybot * height) - fabs(disty)); + xleft = fabs(fabs(tanxleft * height) - fabs(distx)); + + x_center = x_center + distx; + y_center = y_center + disty; + + + xinput_airbrush_tool->xcenter=x_center; + xinput_airbrush_tool->ycenter=y_center; + xinput_airbrush_tool->direction_abs=atan2(ytiltv, xtiltv) + M_PI; + xinput_airbrush_tool->direction=atan(ytiltv/xtiltv); + xinput_airbrush_tool->c_direction=atan(ytiltv/xtilt); + xinput_airbrush_tool->c_direction_abs=atan2(ytiltv, xtilt) + M_PI; + + return create_air_blob(x_center * SUBSAMPLE, y_center * SUBSAMPLE, + 0., ytop * SUBSAMPLE, xright * SUBSAMPLE, 0., + 0., ybot * SUBSAMPLE, xleft * SUBSAMPLE, 0., + (xinput_airbrush_tool->c_direction_abs - M_PI), + xinput_airbrush_tool->c_direction); +} + +static void +xinput_airbrush_button_press (Tool *tool, + GdkEventButton *bevent, + gpointer gdisp_ptr) +{ + gdouble x, y; + GDisplay *gdisp; + XinputAirbrushTool *xinput_airbrush_tool; + GimpDrawable *drawable; + AirBlob *airblob; + AirLine *airline; + MaskBuf *brush_mask; + int width, height; + int x_min, y_min; + guint value; + + + + gdisp = (GDisplay *) gdisp_ptr; + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + /* Keep the coordinates of the target */ + gdisplay_untransform_coords_f (gdisp, bevent->x, bevent->y, + &x, &y, TRUE); + drawable = gimage_active_drawable (gdisp->gimage); + + xinput_airbrush_init (xinput_airbrush_tool, drawable, x, y); + + tool->state = ACTIVE; + tool->gdisp_ptr = gdisp_ptr; + tool->paused_count = 0; + + /* pause the current selection and grab the pointer */ + gdisplays_selection_visibility (gdisp->gimage, SelectionPause); + + /* add motion memory if you press mod1 first ^ perfectmouse */ + if (((bevent->state & GDK_MOD1_MASK) != 0) != (perfectmouse != 0)) + gdk_pointer_grab (gdisp->canvas->window, FALSE, + GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, + NULL, NULL, bevent->time); + else + gdk_pointer_grab (gdisp->canvas->window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, bevent->time); + + tool->gdisp_ptr = gdisp_ptr; + tool->state = ACTIVE; + + + /* Get the AirBlob from xinput_airbrush_pen_ellipse */ + + airblob = xinput_airbrush_pen_ellipse (xinput_airbrush_tool, x, y, + bevent->pressure, bevent->xtilt, + bevent->ytilt); + + + /* Make the AirBlob mask */ + + value = 150; + + airline = create_air_line(airblob); + x_min = airline->min_x; + y_min = airline->min_y; + width = airline->width; + height = airline->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(airline, brush_mask, value); + + /*print_mask(brush_mask);*/ + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + + /*Prepare for next stroke*/ + + xinput_airbrush_tool->last_airblob = airblob; + + + time_smoother_init (xinput_airbrush_tool, bevent->time); + xinput_airbrush_tool->last_time = bevent->time; + dist_smoother_init (xinput_airbrush_tool, 0.0); + xinput_airbrush_tool->init_velocity = TRUE; + xinput_airbrush_tool->init_prepre = FALSE; + xinput_airbrush_tool->lastx = xinput_airbrush_tool->xcenter; + xinput_airbrush_tool->lasty = xinput_airbrush_tool->ycenter; + + /*Free the maks_buf and airline*/ + + mask_buf_free(brush_mask); + g_free(airline); + + gdisplay_flush_now (gdisp); + +} + +static void + xinput_airbrush_button_release (Tool *tool, + GdkEventButton *bevent, + gpointer gdisp_ptr) + { + GDisplay * gdisp; + GImage * gimage; + XinputAirbrushTool * xinput_airbrush_tool; + + gdisp = (GDisplay *) gdisp_ptr; + gimage = gdisp->gimage; + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + /* resume the current selection and ungrab the pointer */ + gdisplays_selection_visibility (gdisp->gimage, SelectionResume); + + gdk_pointer_ungrab (bevent->time); + gdk_flush (); + + /* Set tool state to inactive -- no longer painting */ + tool->state = INACTIVE; + + xinput_airbrush_finish (xinput_airbrush_tool, gimage_active_drawable (gdisp->gimage), tool->ID); + gdisplays_flush (); + } + + +static void + dist_smoother_init (XinputAirbrushTool* xinput_airbrush_tool, gdouble initval) + { + gint i; + + xinput_airbrush_tool->dt_index = 0; + + for (i=0; idt_buffer[i] = initval; + } + } + +static gdouble + dist_smoother_result (XinputAirbrushTool* xinput_airbrush_tool) + { + gint i; + gdouble result = 0.0; + + for (i=0; idt_buffer[i]; + } + + return (result / (gdouble)DIST_SMOOTHER_BUFFER); + } + +static void + dist_smoother_add (XinputAirbrushTool* xinput_airbrush_tool, gdouble value) + { + xinput_airbrush_tool->dt_buffer[xinput_airbrush_tool->dt_index] = value; + + if ((++xinput_airbrush_tool->dt_index) == DIST_SMOOTHER_BUFFER) + xinput_airbrush_tool->dt_index = 0; + } + + +static void + time_smoother_init (XinputAirbrushTool* xinput_airbrush_tool, guint32 initval) + { + gint i; + + xinput_airbrush_tool->ts_index = 0; + + for (i=0; its_buffer[i] = initval; + } + } + +static gdouble + time_smoother_result (XinputAirbrushTool* xinput_airbrush_tool) + { + gint i; + guint64 result = 0; + + for (i=0; its_buffer[i]; + } + +#ifdef _MSC_VER + return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER); +#else + return (result / TIME_SMOOTHER_BUFFER); +#endif + } + +static void + time_smoother_add (XinputAirbrushTool* xinput_airbrush_tool, guint32 value) + { + xinput_airbrush_tool->ts_buffer[xinput_airbrush_tool->ts_index] = value; + + if ((++xinput_airbrush_tool->ts_index) == TIME_SMOOTHER_BUFFER) + xinput_airbrush_tool->ts_index = 0; + } + + +static void + xinput_airbrush_motion (Tool *tool, + GdkEventMotion *mevent, + gpointer gdisp_ptr) + { + GDisplay *gdisp; + XinputAirbrushTool *xinput_airbrush_tool; + GimpDrawable *drawable; + AirBlob *airblob; + + double x, y; + double pressure; + double velocity; + double dist; + gdouble lasttime, thistime; + guint last_value, present_value; + double a, b, c, A; + + gboolean turn_around; + + + last_value = present_value = 150; + + gdisp = (GDisplay *) gdisp_ptr; + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE); + drawable = gimage_active_drawable (gdisp->gimage); + + pressure = mevent->pressure; + + airblob = xinput_airbrush_pen_ellipse (xinput_airbrush_tool, x, y, pressure, mevent->xtilt, mevent->ytilt); + + x = xinput_airbrush_tool->xcenter; + y = xinput_airbrush_tool->ycenter; + + lasttime = xinput_airbrush_tool->last_time; + + time_smoother_add (xinput_airbrush_tool, mevent->time); + thistime = xinput_airbrush_tool->last_time = + time_smoother_result(xinput_airbrush_tool); + + /* The time resolution on X-based GDK motion events is + bloody awful, hence the use of the smoothing function. + Sadly this also means that there is always the chance of + having an indeterminite velocity since this event and + the previous several may still appear to issue at the same + instant. -ADM */ + + if (thistime == lasttime) + thistime = lasttime + 1; + + if (xinput_airbrush_tool->init_velocity) + { + dist_smoother_init (xinput_airbrush_tool, dist = sqrt((xinput_airbrush_tool->lastx-x)*(xinput_airbrush_tool->lastx-x) + + (xinput_airbrush_tool->lasty-y)*(xinput_airbrush_tool->lasty-y))); + xinput_airbrush_tool->init_velocity = FALSE; + } + else + { + dist_smoother_add (xinput_airbrush_tool, sqrt((xinput_airbrush_tool->lastx-x)*(xinput_airbrush_tool->lastx-x) + + (xinput_airbrush_tool->lasty-y)*(xinput_airbrush_tool->lasty-y))); + dist = dist_smoother_result(xinput_airbrush_tool); + } + + turn_around = FALSE; + + if (xinput_airbrush_tool->init_prepre) + { + a = hypot(xinput_airbrush_tool->last_lastx - x, xinput_airbrush_tool->last_lasty - y); + b = hypot(xinput_airbrush_tool->lastx - x, xinput_airbrush_tool->lasty - y); + c = hypot(xinput_airbrush_tool->last_lastx - xinput_airbrush_tool->lastx, xinput_airbrush_tool->last_lasty - xinput_airbrush_tool->lasty); + + /* Maybe fix the fact that a, b or c can be zero i.e that we had a perfectly strait turn around or continue*/ + A = acos((b*b + c*c - a*a)/2*b*c); + /* 1.65806 RAD == 95deg*/ + if ( (A >= 0.0) && (A < 1.65806) ) + { + turn_around = TRUE; + /* printf("TURN_AROUND\n");*/ + } + + } + + velocity = 10.0 * sqrt((dist) / (double)(thistime - lasttime)); + + + + make_stroke(airblob, xinput_airbrush_tool, drawable, last_value, present_value); + + xinput_airbrush_tool->last_lastx = xinput_airbrush_tool->lastx; + xinput_airbrush_tool->last_lasty = xinput_airbrush_tool->lasty; + xinput_airbrush_tool->lastx = x; + xinput_airbrush_tool->lasty = y; + + g_free(xinput_airbrush_tool->last_airblob); + xinput_airbrush_tool->last_airblob = airblob; + + xinput_airbrush_tool->init_velocity = TRUE; + xinput_airbrush_tool->init_prepre = TRUE; + + gdisplay_flush_now (gdisp); + } + +static void + xinput_airbrush_cursor_update (Tool *tool, + GdkEventMotion *mevent, + gpointer gdisp_ptr) + { + GDisplay *gdisp; + Layer *layer; + GdkCursorType ctype = GDK_TOP_LEFT_ARROW; + int x, y; + + gdisp = (GDisplay *) gdisp_ptr; + + gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, FALSE, FALSE); + if ((layer = gimage_get_active_layer (gdisp->gimage))) + { + int off_x, off_y; + drawable_offsets (GIMP_DRAWABLE(layer), &off_x, &off_y); + if (x >= off_x && y >= off_y && + x < (off_x + drawable_width (GIMP_DRAWABLE(layer))) && + y < (off_y + drawable_height (GIMP_DRAWABLE(layer)))) + { + /* One more test--is there a selected region? + * if so, is cursor inside? + */ + if (gimage_mask_is_empty (gdisp->gimage)) + ctype = GDK_PENCIL; + else if (gimage_mask_value (gdisp->gimage, x, y)) + ctype = GDK_PENCIL; + } + } + gdisplay_install_tool_cursor (gdisp, ctype); + } + +static void + xinput_airbrush_control (Tool *tool, + ToolAction action, + gpointer gdisp_ptr) + { + XinputAirbrushTool *xinput_airbrush_tool; + + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + switch (action) + { + case PAUSE: + draw_core_pause (xinput_airbrush_tool->core, tool); + break; + + case RESUME: + draw_core_resume (xinput_airbrush_tool->core, tool); + break; + + case HALT: + xinput_airbrush_cleanup (); + break; + + default: + break; + } + } + +static void + xinput_airbrush_init (XinputAirbrushTool *xinput_airbrush_tool, GimpDrawable *drawable, + double x, double y) + { + /* free the block structures */ + if (undo_tiles) + tile_manager_destroy (undo_tiles); + if (canvas_tiles) + tile_manager_destroy (canvas_tiles); + + /* Allocate the undo structure */ + undo_tiles = tile_manager_new (drawable_width (drawable), + drawable_height (drawable), + drawable_bytes (drawable)); + + /* Allocate the canvas blocks structure */ + canvas_tiles = tile_manager_new (drawable_width (drawable), + drawable_height (drawable), 1); + + /* Get the initial undo extents */ + xinput_airbrush_tool->x1 = xinput_airbrush_tool->x2 = x; + xinput_airbrush_tool->y1 = xinput_airbrush_tool->y2 = y; + } + +static void + xinput_airbrush_finish (XinputAirbrushTool *xinput_airbrush_tool, GimpDrawable *drawable, int tool_id) + { + /* push an undo */ + drawable_apply_image (drawable, xinput_airbrush_tool->x1, xinput_airbrush_tool->y1, + xinput_airbrush_tool->x2, xinput_airbrush_tool->y2, undo_tiles, TRUE); + undo_tiles = NULL; + + /* invalidate the drawable--have to do it here, because + * it is not done during the actual painting. + */ + drawable_invalidate_preview (drawable); + } + +static void + xinput_airbrush_cleanup (void) + { + /* CLEANUP */ + /* If the undo tiles exist, nuke them */ + if (undo_tiles) + { + tile_manager_destroy (undo_tiles); + undo_tiles = NULL; + } + + /* If the canvas blocks exist, nuke them */ + if (canvas_tiles) + { + tile_manager_destroy (canvas_tiles); + canvas_tiles = NULL; + } + + /* Free the temporary buffer if it exist */ + if (canvas_buf) + temp_buf_free (canvas_buf); + canvas_buf = NULL; + } + +/********************************* + * Rendering functions * + *********************************/ + +/* Some of this stuff should probably be combined with the + * code it was copied from in paint_core.c; but I wanted + * to learn this stuff, so I've kept it simple. + */ + + +static void +make_stroke(AirBlob *airblob, + XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + guint last_value, + guint present_value) +{ + + int steps; + int j; + + + int dx, dy; + int x, y; + + + int x0, x1, y0, y1; + + double x0d, x1d, y0d, y1d; + + int x_min, y_min, width, height; + + guint ivalue; + double lv, pv, mv; + + double dist; + + int slopeterm; + + AirBlob *last_airblob; + AirBlob *trans_blob; + AirLine *air_line; + MaskBuf *brush_mask; + + + x0 = xinput_airbrush_tool->lastx; + x1 = xinput_airbrush_tool->xcenter; + y0 = xinput_airbrush_tool->lasty; + y1 = xinput_airbrush_tool->ycenter; + + x0d = xinput_airbrush_tool->lastx; + x1d = xinput_airbrush_tool->xcenter; + y0d = xinput_airbrush_tool->lasty; + y1d = xinput_airbrush_tool->ycenter; + + last_airblob = xinput_airbrush_tool->last_airblob; + + steps = number_of_steps(x0, y0, x1, y1); + + + dx = abs(x0 - x1); + dy = abs(y0 - y1); + + lv = last_value; + pv = present_value; + mv = pv - lv; + + + /* + Yes I know this is bulky code :-), + --> see comment on make_mask. + + But I wanted to keep it simple while I + implemented the tool :-). I will make it + more effective later on. And yes I could + look for the three special cases. + */ + + + if (dy > dx) + { + /* Step in the y direction*/ + if (y0 < y1) + { + /* + Step from y0 to y1 + */ + if(x1 < x0) + { + /* + We have to x-- + */ + + x = x0; + y = y0; + + j = 1; + + slopeterm = dx; + + while(y < y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x--; + y++; + } + else + { + slopeterm += dx; + y++; + } + + j++; + + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + + } + else + { + /* + We have to x++ + */ + + x = x0; + y = y0; + + slopeterm = dx; + + j = 1; + + while(y < y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x++; + y++; + } + else + { + slopeterm += dx; + y++; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + } + else + { + /* Step from y0 to y1 with neg steps */ + if(x1 < x0) + { + /* + We have to x-- + */ + + x = x0; + y = y0; + + slopeterm = dx; + + j = 1; + + while(y > y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x--; + y--; + } + else + { + slopeterm += dx; + y--; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + else + { + /* + We have to x++ + */ + x = x0; + y = y0; + + slopeterm = dx; + + j = 1; + + while(y > y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x++; + y--; + } + else + { + slopeterm += dx; + y--; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + } + + } + else + { + /* Step in the X direction*/ + if (x0 < x1) + { + /* Step from x0 to x1 */ + if(y1 < y0) + { + /* + We have to y-- + */ + x = x0; + y = y0; + + slopeterm = dy; + + j = 1; + + while(x < x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y--; + x++; + } + else + { + slopeterm += dy; + x++; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + + } + else + { + /* + We have to y++ + */ + x = x0; + y = y0; + + slopeterm = dy; + + j = 1; + + while(x < x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y++; + x++; + } + else + { + slopeterm += dy; + x++; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + } + else + { + /* Step from x0 to x1 neg direction */ + if(y1 < y0) + { + /* + We have to y-- + */ + x = x0; + y = y0; + + slopeterm = dy; + + j = 1; + + while(x > x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y--; + x--; + } + else + { + slopeterm += dy; + x--; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + else + { + /* + We have to y++ + */ + x = x0; + y = y0; + + slopeterm = dy; + + j = 1; + + while(x > x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y++; + x--; + } + else + { + slopeterm += dy; + x--; + } + + j++; + + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + } + + } + } + } +} + + + +/************************************************** +*************************************************** +*/ + + +static void +make_mask(AirLine *airline, + MaskBuf *dest, + guint value) +{ + + int steps; + int i; + int j; + + + int dx, dy; + int x, y; + + int x0, x1, y0, y1; + + + unsigned char *s; + guint midvalue; + double ivalue; + + int rowlength; + int slopeterm; + + + rowlength = dest->width * dest->bytes; + + for (i=0; i < airline->nlines ; i++) + { + + steps = number_of_steps(airline->xcenter, airline->ycenter, + airline->line[i].x, airline->line[i].y); + + x0 = airline->xcenter - airline->min_x; + x1 = airline->line[i].x - airline->min_x; + + y0 = airline->ycenter - airline->min_y; + y1 = airline->line[i].y -airline->min_y; + + + dx = abs(x0 - x1); + dy = abs(y0 - y1); + + + + /* + Yes I know this is bulky code :-), + you could insted set the x direction + to 1 or -1, and y to - or + rowside. + Then set the first X and Y and of you + go. + + But I wanted to keep it simple while I + implemented the tool :-). I will make it + more effective later on. And yes I could + look for the three special cases. + */ + + /* if (i >= 3) + { + midvalue = 0; + } + else + {*/ + midvalue = value; + /* }*/ + + + if (dy > dx) + { + /* Step in the y direction*/ + if (y0 < y1) + { + /* + Step from y0 to y1 + */ + if(x1 < x0) + { + /* + We have to x-- + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dx; + + j = 1; + + while(y < y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x--; + y++; + } + else + { + slopeterm += dx; + y++; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + + } + else + { + /* + We have to x++ + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dx; + + j = 1; + + while(y < y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x++; + y++; + } + else + { + slopeterm += dx; + y++; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + } + else + { + /* Step from y0 to y1 with neg steps */ + if(x1 < x0) + { + /* + We have to x-- + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dx; + + j = 1; + + while(y > y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x--; + y--; + } + else + { + slopeterm += dx; + y--; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + else + { + /* + We have to x++ + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dx; + + j = 1; + + while(y > y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x++; + y--; + } + else + { + slopeterm += dx; + y--; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + } + + } + else + { + /* Step in the X direction*/ + if (x0 < x1) + { + /* Step from x0 to x1 */ + if(y1 < y0) + { + /* + We have to y-- + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dy; + + j = 1; + + while(x < x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y--; + x++; + } + else + { + slopeterm += dy; + x++; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + + } + else + { + /* + We have to y++ + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dy; + + j = 1; + + while(x < x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y++; + x++; + } + else + { + slopeterm += dy; + x++; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + } + else + { + /* Step from x0 to x1 neg direction */ + if(y1 < y0) + { + /* + We have to y-- + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dy; + + j = 1; + + while(x > x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y--; + x--; + } + else + { + slopeterm += dy; + x--; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + else + { + /* + We have to y++ + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dy; + + j = 1; + + while(x > x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y++; + x--; + } + else + { + slopeterm += dy; + x--; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + + } + } + } + } + + +} + + + + +static void +xinput_airbrush_set_paint_area (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + int x, int y, int width, int height) +{ + int iwidth, iheight; + int x1, y1, x2, y2; + int bytes; + int dwidth, dheight; + + bytes = drawable_has_alpha (drawable) ? + drawable_bytes (drawable) : drawable_bytes (drawable) + 1; + + dwidth = drawable_width (drawable); + dheight = drawable_height (drawable); + + + x1 = CLAMP (x, 0, dwidth); + y1 = CLAMP (y, 0, dheight); + x2 = CLAMP ((x + width), 0, dwidth); + y2 = CLAMP ((y + height), 0, dheight); + + iwidth = MIN((x2 - x1), width); + iheight = MIN((y2 - y1), height); + + + + /* configure the canvas buffer */ + + if ((x2 - x1) && (y2 - y1)) + canvas_buf = temp_buf_resize (canvas_buf, bytes, x1, y1, + iwidth, iheight); + + + + +} + + +static void +render_airbrush_line (AirBrushBlob *airbrush_blob, guchar *dest, + int y, int width, XinputAirbrushTool *xinput_airbrush_tool) +{ + int i, j, k, l, m, g; + + int left, right; + + int brush_width; + + double x_dest; + double xdist, ydist, dist, angle; + double i_value; + guchar value; + + + + if (airbrush_blob->height <= 6) + { + return; + } + left = 0; + right = 0; + + j = y * SUBSAMPLE; + g = y * SUBSAMPLE; + + for (i=0; i= airbrush_blob->height) + { + if ( i == 0) + { + return; + } + else + { + left = airbrush_blob->data[g].left - airbrush_blob->min_x; + right = airbrush_blob->data[g].right - airbrush_blob->min_x; + break; + } + } + if ( i == SUBSAMPLE/2) + { + left = airbrush_blob->data[j].left - airbrush_blob->min_x; + right = airbrush_blob->data[j].right - airbrush_blob->min_x; + break; + } + + } + + brush_width = right - left; + + if (brush_width <= SUBSAMPLE*2) + { + return; + } + + dest = dest + (left/SUBSAMPLE); + + for (i=4; i <= brush_width; i = i + SUBSAMPLE) + { + x_dest = (left + i)/SUBSAMPLE; + xdist = (airbrush_blob->min_x/SUBSAMPLE) + x_dest - xinput_airbrush_tool->xcenter; + ydist = xinput_airbrush_tool->ycenter - (airbrush_blob->y/SUBSAMPLE) - y; + angle = atan2(ydist, xdist) + M_PI; + dist = hypot(xdist, ydist); + for (j=1; j< (airbrush_blob->height - 1) ; j++) + { + k = j - 1; + l = j + 1; + m = 0; + if ( (fabs(airbrush_blob->data[k].angle_right_abs - airbrush_blob->data[l].angle_right_abs) > + fabs(airbrush_blob->data[k].angle_right_abs - angle)) && + (fabs(airbrush_blob->data[k].angle_right_abs - airbrush_blob->data[l].angle_right_abs) > + fabs(airbrush_blob->data[l].angle_right_abs - angle)) + ) + { + i_value = MIN(1., (dist*SUBSAMPLE)/(airbrush_blob->data[j].dist_right)); + value = 255 * (1 - i_value); + *dest = value; + dest++; + m = 1; + break; + } + + else if ( (fabs(airbrush_blob->data[k].angle_left_abs - airbrush_blob->data[l].angle_left_abs) > + fabs(airbrush_blob->data[k].angle_left_abs - angle)) && + (fabs(airbrush_blob->data[k].angle_left_abs - airbrush_blob->data[l].angle_left_abs) > + fabs(airbrush_blob->data[l].angle_left_abs - angle)) + ) + { + i_value = MIN(1., (dist*SUBSAMPLE)/(airbrush_blob->data[j].dist_left)); + value = 255 * (1 - i_value); + *dest = value; + dest++; + m = 1; + break; + } + else if ((j == (airbrush_blob->height - 2)) && ( m == 0)) + { + *dest = 0; + dest++; + } + } + } + + +} + + + +static void +calc_width (AirBrushBlob *airbrush_blob) +{ + int i; + int min_x, max_x; + + i = (airbrush_blob->height)/2; + + min_x = airbrush_blob->data[i].left; + max_x = airbrush_blob->data[i].right; + + for (i=0; i< (airbrush_blob->height) ; i++) + + { + + if ( airbrush_blob->data[i].left <= airbrush_blob->data[i].right) + { + min_x = MIN(airbrush_blob->data[i].left, min_x); + } + + + if ( airbrush_blob->data[i].left <= airbrush_blob->data[i].right) + { + max_x = MAX(airbrush_blob->data[i].right, max_x ); + } + } + + airbrush_blob->width = max_x - min_x; + airbrush_blob->min_x = min_x; + airbrush_blob->max_x = max_x; +} + + +static void +calc_angle (AirBrushBlob *airbrush_blob, double xcenter, double ycenter) +{ + + + int i; + double y_center, x_center; + + double y_dist, x_dist; + double left_ang, right_ang; + double left_ang_abs, right_ang_abs; + int min_x, max_x; + + + + x_center = xcenter * SUBSAMPLE; + y_center = ycenter * SUBSAMPLE - airbrush_blob->y; + + i = (airbrush_blob->height)/2; + + min_x = airbrush_blob->data[i].left; + max_x = airbrush_blob->data[i].right; + + for (i=0; i< (airbrush_blob->height) ; i++) + + { + + y_dist = y_center - i; + x_dist = airbrush_blob->data[i].left - x_center; + left_ang_abs = atan2(y_dist, x_dist) + M_PI; + left_ang = atan(y_dist/x_dist); + airbrush_blob->data[i].angle_left = left_ang; + airbrush_blob->data[i].angle_left_abs = left_ang_abs; + airbrush_blob->data[i].dist_left = hypot(x_dist, y_dist); + if ( airbrush_blob->data[i].left <= airbrush_blob->data[i].right) + { + min_x = MIN(airbrush_blob->data[i].left, min_x); + } + + x_dist = airbrush_blob->data[i].right - x_center; + right_ang_abs = atan2(y_dist, x_dist) + M_PI; + right_ang = atan(y_dist/x_dist); + airbrush_blob->data[i].angle_right = right_ang; + airbrush_blob->data[i].angle_right_abs = right_ang_abs; + airbrush_blob->data[i].dist_right = hypot(x_dist, y_dist); + if ( airbrush_blob->data[i].left <= airbrush_blob->data[i].right) + { + max_x = MAX(airbrush_blob->data[i].right, max_x ); + } + + + + } + + airbrush_blob->width = max_x - min_x; + airbrush_blob->min_x = min_x; + airbrush_blob->max_x = max_x; + + +} + + + +static void +make_single_mask(AirBrushBlob *airbrush_blob, XinputAirbrushTool *xinput_airbrush_tool, MaskBuf *dest) +{ + unsigned char * s; + int h; + int i; + + h = dest->height; + s = dest->data; + + for (i=0; iwidth, xinput_airbrush_tool); + s += (dest->width * dest->bytes); + } + +} + +static void +print_line (guchar *dest, + int width) +{ + + int i; + + for (i=0; i < width; i++) + { + printf("%d ", *dest); + dest++; + } +} + +static void +print_mask(MaskBuf *dest) +{ + unsigned char * s; + int h; + int i; + + h = dest->height; + s = dest->data; + printf("\nBrush_Mask\n\n"); + for (i=0; iwidth); + s += (dest->width * dest->bytes); + printf("\n"); + } + printf("\n"); +} + + + +static void +xinput_airbrush_paste (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + MaskBuf *brush_mask, + int x, + int y, + int width, + int height) +{ + GImage *gimage; + PixelRegion srcPR; + int offx, offy; + unsigned char col[MAX_CHANNELS]; + + + /*print_mask(brush_mask);*/ + + + if (! (gimage = drawable_gimage (drawable))) + return; + + + /* Get the the buffer */ + xinput_airbrush_set_paint_area (xinput_airbrush_tool, drawable, x, y, width, height); + + /* check to make sure there is actually a canvas to draw on */ + if (!canvas_buf) + return; + + gimage_get_foreground (gimage, drawable, col); + + /* set the alpha channel */ + col[canvas_buf->bytes - 1] = OPAQUE_OPACITY; + + /* color the pixels */ + color_pixels (temp_buf_data (canvas_buf), col, + canvas_buf->width * canvas_buf->height, canvas_buf->bytes); + + /* set undo blocks */ + xinput_airbrush_set_undo_tiles (drawable, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height); + + /* initialize any invalid canvas tiles */ + xinput_airbrush_set_canvas_tiles (canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height); + + /*DON'T FORGETT THE 100 VALUE, I.E. THE BRUSH OPACITY WHICH SHOULD BE 255*/ + + xinput_airbrush_to_canvas_tiles (xinput_airbrush_tool, brush_mask, 100); + + xinput_airbrush_canvas_tiles_to_canvas_buf(xinput_airbrush_tool); + + + /* initialize canvas buf source pixel regions */ + srcPR.bytes = canvas_buf->bytes; + srcPR.x = 0; + srcPR.y = 0; + srcPR.w = canvas_buf->width; + srcPR.h = canvas_buf->height; + srcPR.rowstride = canvas_buf->width * canvas_buf->bytes; + srcPR.data = temp_buf_data (canvas_buf); + + + /* apply the paint area to the gimage */ + gimage_apply_image (gimage, drawable, &srcPR, + FALSE, + (int) (gimp_context_get_opacity (NULL) * 255), + gimp_context_get_paint_mode (NULL), + undo_tiles, /* specify an alternative src1 */ + canvas_buf->x, canvas_buf->y); + + /* Update the undo extents */ + xinput_airbrush_tool->x1 = MIN (xinput_airbrush_tool->x1, canvas_buf->x); + xinput_airbrush_tool->y1 = MIN (xinput_airbrush_tool->y1, canvas_buf->y); + xinput_airbrush_tool->x2 = MAX (xinput_airbrush_tool->x2, (canvas_buf->x + canvas_buf->width)); + xinput_airbrush_tool->y2 = MAX (xinput_airbrush_tool->y2, (canvas_buf->y + canvas_buf->height)); + + /* Update the gimage--it is important to call gdisplays_update_area + * instead of drawable_update because we don't want the drawable + * preview to be constantly invalidated + */ + drawable_offsets (drawable, &offx, &offy); + gdisplays_update_area (gimage, canvas_buf->x + offx, canvas_buf->y + offy, + canvas_buf->width, canvas_buf->height); + +} + + + +static void +xinput_airbrush_to_canvas_tiles (XinputAirbrushTool *xinput_airbrush_tool, + MaskBuf *brush_mask, + int brush_opacity) +{ + PixelRegion srcPR, maskPR; + + /* combine the brush mask and the canvas tiles */ + pixel_region_init (&srcPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, TRUE); + + maskPR.bytes = 1; + maskPR.x = 0; + maskPR.y = 0; + maskPR.w = srcPR.w; + maskPR.h = srcPR.h; + maskPR.rowstride = maskPR.bytes * brush_mask->width; + maskPR.data = mask_buf_data (brush_mask); + + /* combine the mask to the canvas tiles */ + combine_mask_and_region (&srcPR, &maskPR, brush_opacity); +} + + + + +static void +xinput_airbrush_canvas_tiles_to_canvas_buf(XinputAirbrushTool *xinput_airbrush_tool) +{ + PixelRegion srcPR, maskPR; + + /* combine the canvas tiles and the canvas buf */ + srcPR.bytes = canvas_buf->bytes; + srcPR.x = 0; + srcPR.y = 0; + srcPR.w = canvas_buf->width; + srcPR.h = canvas_buf->height; + srcPR.rowstride = canvas_buf->width * canvas_buf->bytes; + srcPR.data = temp_buf_data (canvas_buf); + + pixel_region_init (&maskPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, FALSE); + + /* apply the canvas tiles to the canvas buf */ + apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY); +} + + + +static void +xinput_airbrush_set_undo_tiles (GimpDrawable *drawable, + int x, int y, int w, int h) +{ + int i, j; + Tile *src_tile; + Tile *dest_tile; + + for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT))) + { + for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH))) + { + dest_tile = tile_manager_get_tile (undo_tiles, j, i, FALSE, FALSE); + if (tile_is_valid (dest_tile) == FALSE) + { + src_tile = tile_manager_get_tile (drawable_data (drawable), j, i, TRUE, FALSE); + tile_manager_map_tile (undo_tiles, j, i, src_tile); + tile_release (src_tile, FALSE); + } + } + } +} + + +static void +xinput_airbrush_set_canvas_tiles (int x, int y, int w, int h) +{ + int i, j; + Tile *tile; + + for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT))) + { + for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH))) + { + tile = tile_manager_get_tile (canvas_tiles, j, i, FALSE, FALSE); + if (tile_is_valid (tile) == FALSE) + { + tile = tile_manager_get_tile (canvas_tiles, j, i, TRUE, TRUE); + memset (tile_data_pointer (tile, 0, 0), + 0, + tile_size (tile)); + tile_release (tile, TRUE); + } + } + } +} + +/**************************/ +/* Global xinput_airbrush functions */ +/**************************/ + +void +xinput_airbrush_no_draw (Tool *tool) +{ + return; +} + +Tool * +tools_new_xinput_airbrush (void) +{ + Tool * tool; + XinputAirbrushTool * private; + + /* The tool options */ + if (! xinput_airbrush_options) + { + xinput_airbrush_options = xinput_airbrush_options_new (); + tools_register (XINPUT_AIRBRUSH, (ToolOptions *) xinput_airbrush_options); + + /* press all default buttons */ + xinput_airbrush_options_reset (); + } + + tool = tools_new_tool (XINPUT_AIRBRUSH); + private = g_new (XinputAirbrushTool, 1); + + private->core = draw_core_new (xinput_airbrush_no_draw); + private->last_airbrush_blob = NULL; + + tool->private = private; + + tool->button_press_func = xinput_airbrush_button_press; + tool->button_release_func = xinput_airbrush_button_release; + tool->motion_func = xinput_airbrush_motion; + tool->cursor_update_func = xinput_airbrush_cursor_update; + tool->control_func = xinput_airbrush_control; + + return tool; +} + +void +tools_free_xinput_airbrush(Tool *tool) +{ + XinputAirbrushTool * xinput_airbrush_tool; + + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + /* Make sure the selection core is not visible */ + if (tool->state == ACTIVE && xinput_airbrush_tool->core) + draw_core_stop (xinput_airbrush_tool->core, tool); + + /* Free the selection core */ + if (xinput_airbrush_tool->core) + draw_core_free (xinput_airbrush_tool->core); + + /* Free the last airbrush_blob, if any */ + if (xinput_airbrush_tool->last_airbrush_blob) + g_free (xinput_airbrush_tool->last_airbrush_blob); + + /* Cleanup memory */ + xinput_airbrush_cleanup (); + + /* Free the paint core */ + g_free (xinput_airbrush_tool); +} diff --git a/app/tools/xinput_airbrush.h b/app/tools/xinput_airbrush.h new file mode 100644 index 0000000000..db12ac2ff8 --- /dev/null +++ b/app/tools/xinput_airbrush.h @@ -0,0 +1,30 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef __XINPUTAIRBRUSH_H__ +#define __XINPUTAIRBRUSH_H__ + +#include "tools.h" +#include "procedural_db.h" + +Tool * tools_new_xinput_airbrush (void); +void tools_free_xinput_airbrush (Tool *); + +/* Procedure definition and marshalling function */ +extern ProcRecord xinput_airbrush_proc; + +#endif /* __XINPUTAIRBRUSH_H__ */ diff --git a/app/toolsF.h b/app/toolsF.h index 13c59bb784..cfe3feca50 100644 --- a/app/toolsF.h +++ b/app/toolsF.h @@ -65,7 +65,8 @@ typedef enum DODGEBURN, SMUDGE, MEASURE, - LAST_TOOLBOX_TOOL = MEASURE, + XINPUT_AIRBRUSH, + LAST_TOOLBOX_TOOL = XINPUT_AIRBRUSH, /* Non-toolbox tools */ BY_COLOR_SELECT, diff --git a/app/xinput_airbrush.c b/app/xinput_airbrush.c new file mode 100644 index 0000000000..c0838d4509 --- /dev/null +++ b/app/xinput_airbrush.c @@ -0,0 +1,2448 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +Hmm a little + */ +#include +#include +#include +#include "appenv.h" +#include "drawable.h" +#include "draw_core.h" +#include "gimage_mask.h" +#include "gimprc.h" +#include "xinput_airbrush.h" +#include "paint_options.h" +#include "tool_options_ui.h" +#include "tools.h" +#include "undo.h" +#include "airbrush_blob.h" +#include "gdisplay.h" + +#include "libgimp/gimpintl.h" + +#include "tile.h" /* ick. */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* M_PI */ + +#define SUBSAMPLE 8 + +#define DIST_SMOOTHER_BUFFER 10 +#define TIME_SMOOTHER_BUFFER 10 + +/* the XinputAirbrush structures */ + +typedef struct _XinputAirbrushTool XinputAirbrushTool; +struct _XinputAirbrushTool + { + DrawCore * core; /* Core select object */ + + AirBrush * last_airbrush; + AirBrushBlob * last_airbrush_blob; /* airbrush_blob for last cursor position */ + AirBlob * last_airblob; + + + int x1, y1; /* image space coordinate */ + int x2, y2; /* image space coords */ + + /* circular distance history buffer */ + gdouble dt_buffer[DIST_SMOOTHER_BUFFER]; + gint dt_index; + + /* circular timing history buffer */ + guint32 ts_buffer[TIME_SMOOTHER_BUFFER]; + gint ts_index; + + /* Direction and center point */ + double xcenter, ycenter; + double direction_abs; + double direction; + double c_direction; + double c_direction_abs; + + + gdouble last_time; /* previous time of a motion event */ + gdouble lastx, lasty; /* previous position of a motion event */ + gdouble last_lastx, last_lasty; /* The previous previous position of a motion event i.e pos of the blob */ + + gboolean init_velocity; + gboolean init_prepre; + + }; + + +typedef struct _XinputAirbrushOptions XinputAirbrushOptions; +struct _XinputAirbrushOptions + { + PaintOptions paint_options; + + double size; + double size_d; + GtkObject *size_w; + + double sensitivity; + double sensitivity_d; + GtkObject *sensitivity_w; + + double vel_sensitivity; + double vel_sensitivity_d; + GtkObject *vel_sensitivity_w; + + double tilt_sensitivity; + double tilt_sensitivity_d; + GtkObject *tilt_sensitivity_w; + + + double tilt_angle; + double tilt_angle_d; + GtkObject *tilt_angle_w; + + }; + + +/* the xinput_airbrush tool options */ +static XinputAirbrushOptions *xinput_airbrush_options = NULL; + +/* local variables */ + +/* undo blocks variables */ +static TileManager * undo_tiles = NULL; + +/* Tiles used to render the stroke at 1 byte/pp */ +static TileManager * canvas_tiles = NULL; + +/* Flat buffer that is used to used to render the dirty region + * for composition onto the destination drawable + */ +static TempBuf * canvas_buf = NULL; + + +/* local function prototypes */ + +static void xinput_airbrush_button_press (Tool *, GdkEventButton *, gpointer); +static void xinput_airbrush_button_release (Tool *, GdkEventButton *, gpointer); +static void xinput_airbrush_motion (Tool *, GdkEventMotion *, gpointer); +static void xinput_airbrush_cursor_update (Tool *, GdkEventMotion *, gpointer); +static void xinput_airbrush_control (Tool *, ToolAction, gpointer); + +static void time_smoother_add (XinputAirbrushTool* xinput_airbrush_tool, guint32 value); +static gdouble time_smoother_result (XinputAirbrushTool* xinput_airbrush_tool); +static void time_smoother_init (XinputAirbrushTool* xinput_airbrush_tool, guint32 initval); +static void dist_smoother_add (XinputAirbrushTool* xinput_airbrush_tool, gdouble value); +static gdouble dist_smoother_result (XinputAirbrushTool* xinput_airbrush_tool); +static void dist_smoother_init (XinputAirbrushTool* xinput_airbrush_tool, gdouble initval); + +static void xinput_airbrush_init (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + double x, + double y); + +static void xinput_airbrush_finish (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + int tool_id); + +static void xinput_airbrush_cleanup (void); + + +/*Mask functions*/ + +static void calc_angle (AirBrushBlob *airbrush_blob, + double xcenter, + double ycenter); + +static void calc_width (AirBrushBlob *airbrush_blob); + + +static void render_airbrush_line (AirBrushBlob *airbrush_blob, + guchar *dest, + int y, + int width, + XinputAirbrushTool *xinput_airbrush_tool); + + + +static void make_single_mask (AirBrushBlob *airbrush_blob, + XinputAirbrushTool *xinput_airbrush_tool, + MaskBuf *dest); + +static void make_stroke(AirBlob *airblob, + XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + guint last_value, + guint present_value); + + + +static void make_mask(AirLine *airline, + MaskBuf *brush_mask, + guint value); + + + + +static void print_mask(MaskBuf *dest); + + + + +/* Rendering functions */ + +static void xinput_airbrush_set_paint_area (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + int x, + int y, + int width, + int height); + +static void xinput_airbrush_paste (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + MaskBuf *brush_mask, + int x, + int y, + int width, + int height); + +static void xinput_airbrush_to_canvas_tiles (XinputAirbrushTool *xinput_airbrush_tool, + MaskBuf *brush_mask, + int brush_opacity); + +static void xinput_airbrush_canvas_tiles_to_canvas_buf(XinputAirbrushTool *xinput_airbrush_tool); + +static void xinput_airbrush_set_undo_tiles (GimpDrawable *drawable, + int x, + int y, + int w, + int h); + +static void xinput_airbrush_set_canvas_tiles(int x, + int y, + int w, + int h); + + + +/* Start of functions */ + + +static void + xinput_airbrush_options_reset (void) + { + XinputAirbrushOptions *options = xinput_airbrush_options; + + paint_options_reset ((PaintOptions *) options); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->size_w), + options->size_d); + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->sensitivity_w), + options->sensitivity_d); + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w), + options->tilt_sensitivity_d); + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w), + options->vel_sensitivity_d); + gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w), + options->tilt_angle_d); + } + +static XinputAirbrushOptions * + xinput_airbrush_options_new (void) + { + XinputAirbrushOptions *options; + + GtkWidget *table; + GtkWidget *abox; + GtkWidget *label; + GtkWidget *slider; + + /* the new xinput_airbrush tool options structure */ + options = (XinputAirbrushOptions *) g_malloc (sizeof (XinputAirbrushOptions)); + paint_options_init ((PaintOptions *) options, + XINPUT_AIRBRUSH, + xinput_airbrush_options_reset); + options->size = options->size_d = 4.4; + options->sensitivity = options->sensitivity_d = 1.0; + options->vel_sensitivity = options->vel_sensitivity_d = 0.8; + options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4; + options->tilt_angle = options->tilt_angle_d = 0.0; + + /*the main table*/ + + table = gtk_table_new (6, 2, FALSE); + gtk_box_pack_start (GTK_BOX (((ToolOptions *) options)->main_vbox), table, + FALSE, FALSE, 0); + + /*size slider*/ + label = gtk_label_new (_("Size:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (label); + + options->size_w = + gtk_adjustment_new (options->size_d, 0.0, 20.0, 1.0, 5.0, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->size_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_table_attach_defaults (GTK_TABLE (table), slider, 1, 2, 0, 1); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->size_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->size); + gtk_widget_show (slider); + + /*sens slider*/ + label = gtk_label_new (_("Sensitivity:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, + GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (label); + + options->sensitivity_w = + gtk_adjustment_new (options->sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->sensitivity_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_table_attach_defaults (GTK_TABLE (table), slider, 1, 2, 1, 2); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->sensitivity_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->sensitivity); + gtk_widget_show (slider); + + /*tilt sens slider*/ + label = gtk_label_new (_("Tilt")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Sensitivity:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0); + gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 2, 4, + GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (abox); + + options->tilt_sensitivity_w = + gtk_adjustment_new (options->tilt_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_sensitivity_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_container_add (GTK_CONTAINER (abox), slider); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->tilt_sensitivity_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->tilt_sensitivity); + gtk_widget_show (slider); + + + /*velocity sens slider*/ + label = gtk_label_new (_("Speed")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Sensitivity:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0); + gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 4, 6, + GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (abox); + + options->vel_sensitivity_w = + gtk_adjustment_new (options->vel_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->vel_sensitivity_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_container_add (GTK_CONTAINER (abox), slider); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->vel_sensitivity_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->vel_sensitivity); + gtk_widget_show (slider); + + + /*angle adjust slider*/ + label = gtk_label_new (_("Angle")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 6, 7, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Adjust:")); + gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 7, 8, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0); + gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 6, 8, + GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (abox); + + options->tilt_angle_w = + gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0); + slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w)); + gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); + gtk_container_add (GTK_CONTAINER (abox), slider); + gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); + gtk_signal_connect (GTK_OBJECT (options->tilt_angle_w), "value_changed", + (GtkSignalFunc) tool_options_double_adjustment_update, + &options->tilt_angle); + + + + + gtk_widget_show_all (table); + + return options; + } + + + +static AirBlob * +xinput_airbrush_pen_ellipse (XinputAirbrushTool *xinput_airbrush_tool, gdouble x_center, gdouble y_center, + gdouble pressure, gdouble xtiltv, gdouble ytiltv) +{ + + gdouble xtilt, ytilt; + double height; + double tanx, tany; + double tanytop, tanxright, tanybot, tanxleft; + double sprayangle; + double distx, disty; + double ytop, xright, ybot, xleft; + + + + /*Fix av tilt negative when it should be positive*/ + + xtilt = xtiltv * -1.0; + ytilt = ytiltv * -1.0; + + /*Values that should be adjust able*/ + + height = 50.; + sprayangle = M_PI/12; + + /*Tan of x and y tilt plus spray angles x r/l and y t/b tan*/ + + tanx = tan(xtilt * M_PI / 2.0); + tany = tan(ytilt * M_PI / 2.0); + + + tanytop = tan((ytilt * M_PI / 2.0) + (sprayangle/2)); + tanxright = tan((xtilt * M_PI / 2.0) + (sprayangle/2)); + tanybot = tan((ytilt * M_PI / 2.0) - (sprayangle/2)); + tanxleft = tan((xtilt * M_PI / 2.0) - (sprayangle/2)); + + /* Offset from cursor due to tilt in x and y depening on the hight*/ + + distx = tanx * height; + disty = tany * height; + + /* Offset from center of blob in all for axies */ + + ytop = fabs(fabs(tanytop * height) - fabs(disty)); + xright = fabs(fabs(tanxright * height) - fabs(distx)); + ybot = fabs(fabs(tanybot * height) - fabs(disty)); + xleft = fabs(fabs(tanxleft * height) - fabs(distx)); + + x_center = x_center + distx; + y_center = y_center + disty; + + + xinput_airbrush_tool->xcenter=x_center; + xinput_airbrush_tool->ycenter=y_center; + xinput_airbrush_tool->direction_abs=atan2(ytiltv, xtiltv) + M_PI; + xinput_airbrush_tool->direction=atan(ytiltv/xtiltv); + xinput_airbrush_tool->c_direction=atan(ytiltv/xtilt); + xinput_airbrush_tool->c_direction_abs=atan2(ytiltv, xtilt) + M_PI; + + return create_air_blob(x_center * SUBSAMPLE, y_center * SUBSAMPLE, + 0., ytop * SUBSAMPLE, xright * SUBSAMPLE, 0., + 0., ybot * SUBSAMPLE, xleft * SUBSAMPLE, 0., + (xinput_airbrush_tool->c_direction_abs - M_PI), + xinput_airbrush_tool->c_direction); +} + +static void +xinput_airbrush_button_press (Tool *tool, + GdkEventButton *bevent, + gpointer gdisp_ptr) +{ + gdouble x, y; + GDisplay *gdisp; + XinputAirbrushTool *xinput_airbrush_tool; + GimpDrawable *drawable; + AirBlob *airblob; + AirLine *airline; + MaskBuf *brush_mask; + int width, height; + int x_min, y_min; + guint value; + + + + gdisp = (GDisplay *) gdisp_ptr; + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + /* Keep the coordinates of the target */ + gdisplay_untransform_coords_f (gdisp, bevent->x, bevent->y, + &x, &y, TRUE); + drawable = gimage_active_drawable (gdisp->gimage); + + xinput_airbrush_init (xinput_airbrush_tool, drawable, x, y); + + tool->state = ACTIVE; + tool->gdisp_ptr = gdisp_ptr; + tool->paused_count = 0; + + /* pause the current selection and grab the pointer */ + gdisplays_selection_visibility (gdisp->gimage, SelectionPause); + + /* add motion memory if you press mod1 first ^ perfectmouse */ + if (((bevent->state & GDK_MOD1_MASK) != 0) != (perfectmouse != 0)) + gdk_pointer_grab (gdisp->canvas->window, FALSE, + GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, + NULL, NULL, bevent->time); + else + gdk_pointer_grab (gdisp->canvas->window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, bevent->time); + + tool->gdisp_ptr = gdisp_ptr; + tool->state = ACTIVE; + + + /* Get the AirBlob from xinput_airbrush_pen_ellipse */ + + airblob = xinput_airbrush_pen_ellipse (xinput_airbrush_tool, x, y, + bevent->pressure, bevent->xtilt, + bevent->ytilt); + + + /* Make the AirBlob mask */ + + value = 150; + + airline = create_air_line(airblob); + x_min = airline->min_x; + y_min = airline->min_y; + width = airline->width; + height = airline->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(airline, brush_mask, value); + + /*print_mask(brush_mask);*/ + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + + /*Prepare for next stroke*/ + + xinput_airbrush_tool->last_airblob = airblob; + + + time_smoother_init (xinput_airbrush_tool, bevent->time); + xinput_airbrush_tool->last_time = bevent->time; + dist_smoother_init (xinput_airbrush_tool, 0.0); + xinput_airbrush_tool->init_velocity = TRUE; + xinput_airbrush_tool->init_prepre = FALSE; + xinput_airbrush_tool->lastx = xinput_airbrush_tool->xcenter; + xinput_airbrush_tool->lasty = xinput_airbrush_tool->ycenter; + + /*Free the maks_buf and airline*/ + + mask_buf_free(brush_mask); + g_free(airline); + + gdisplay_flush_now (gdisp); + +} + +static void + xinput_airbrush_button_release (Tool *tool, + GdkEventButton *bevent, + gpointer gdisp_ptr) + { + GDisplay * gdisp; + GImage * gimage; + XinputAirbrushTool * xinput_airbrush_tool; + + gdisp = (GDisplay *) gdisp_ptr; + gimage = gdisp->gimage; + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + /* resume the current selection and ungrab the pointer */ + gdisplays_selection_visibility (gdisp->gimage, SelectionResume); + + gdk_pointer_ungrab (bevent->time); + gdk_flush (); + + /* Set tool state to inactive -- no longer painting */ + tool->state = INACTIVE; + + xinput_airbrush_finish (xinput_airbrush_tool, gimage_active_drawable (gdisp->gimage), tool->ID); + gdisplays_flush (); + } + + +static void + dist_smoother_init (XinputAirbrushTool* xinput_airbrush_tool, gdouble initval) + { + gint i; + + xinput_airbrush_tool->dt_index = 0; + + for (i=0; idt_buffer[i] = initval; + } + } + +static gdouble + dist_smoother_result (XinputAirbrushTool* xinput_airbrush_tool) + { + gint i; + gdouble result = 0.0; + + for (i=0; idt_buffer[i]; + } + + return (result / (gdouble)DIST_SMOOTHER_BUFFER); + } + +static void + dist_smoother_add (XinputAirbrushTool* xinput_airbrush_tool, gdouble value) + { + xinput_airbrush_tool->dt_buffer[xinput_airbrush_tool->dt_index] = value; + + if ((++xinput_airbrush_tool->dt_index) == DIST_SMOOTHER_BUFFER) + xinput_airbrush_tool->dt_index = 0; + } + + +static void + time_smoother_init (XinputAirbrushTool* xinput_airbrush_tool, guint32 initval) + { + gint i; + + xinput_airbrush_tool->ts_index = 0; + + for (i=0; its_buffer[i] = initval; + } + } + +static gdouble + time_smoother_result (XinputAirbrushTool* xinput_airbrush_tool) + { + gint i; + guint64 result = 0; + + for (i=0; its_buffer[i]; + } + +#ifdef _MSC_VER + return (gdouble) (gint64) (result / TIME_SMOOTHER_BUFFER); +#else + return (result / TIME_SMOOTHER_BUFFER); +#endif + } + +static void + time_smoother_add (XinputAirbrushTool* xinput_airbrush_tool, guint32 value) + { + xinput_airbrush_tool->ts_buffer[xinput_airbrush_tool->ts_index] = value; + + if ((++xinput_airbrush_tool->ts_index) == TIME_SMOOTHER_BUFFER) + xinput_airbrush_tool->ts_index = 0; + } + + +static void + xinput_airbrush_motion (Tool *tool, + GdkEventMotion *mevent, + gpointer gdisp_ptr) + { + GDisplay *gdisp; + XinputAirbrushTool *xinput_airbrush_tool; + GimpDrawable *drawable; + AirBlob *airblob; + + double x, y; + double pressure; + double velocity; + double dist; + gdouble lasttime, thistime; + guint last_value, present_value; + double a, b, c, A; + + gboolean turn_around; + + + last_value = present_value = 150; + + gdisp = (GDisplay *) gdisp_ptr; + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE); + drawable = gimage_active_drawable (gdisp->gimage); + + pressure = mevent->pressure; + + airblob = xinput_airbrush_pen_ellipse (xinput_airbrush_tool, x, y, pressure, mevent->xtilt, mevent->ytilt); + + x = xinput_airbrush_tool->xcenter; + y = xinput_airbrush_tool->ycenter; + + lasttime = xinput_airbrush_tool->last_time; + + time_smoother_add (xinput_airbrush_tool, mevent->time); + thistime = xinput_airbrush_tool->last_time = + time_smoother_result(xinput_airbrush_tool); + + /* The time resolution on X-based GDK motion events is + bloody awful, hence the use of the smoothing function. + Sadly this also means that there is always the chance of + having an indeterminite velocity since this event and + the previous several may still appear to issue at the same + instant. -ADM */ + + if (thistime == lasttime) + thistime = lasttime + 1; + + if (xinput_airbrush_tool->init_velocity) + { + dist_smoother_init (xinput_airbrush_tool, dist = sqrt((xinput_airbrush_tool->lastx-x)*(xinput_airbrush_tool->lastx-x) + + (xinput_airbrush_tool->lasty-y)*(xinput_airbrush_tool->lasty-y))); + xinput_airbrush_tool->init_velocity = FALSE; + } + else + { + dist_smoother_add (xinput_airbrush_tool, sqrt((xinput_airbrush_tool->lastx-x)*(xinput_airbrush_tool->lastx-x) + + (xinput_airbrush_tool->lasty-y)*(xinput_airbrush_tool->lasty-y))); + dist = dist_smoother_result(xinput_airbrush_tool); + } + + turn_around = FALSE; + + if (xinput_airbrush_tool->init_prepre) + { + a = hypot(xinput_airbrush_tool->last_lastx - x, xinput_airbrush_tool->last_lasty - y); + b = hypot(xinput_airbrush_tool->lastx - x, xinput_airbrush_tool->lasty - y); + c = hypot(xinput_airbrush_tool->last_lastx - xinput_airbrush_tool->lastx, xinput_airbrush_tool->last_lasty - xinput_airbrush_tool->lasty); + + /* Maybe fix the fact that a, b or c can be zero i.e that we had a perfectly strait turn around or continue*/ + A = acos((b*b + c*c - a*a)/2*b*c); + /* 1.65806 RAD == 95deg*/ + if ( (A >= 0.0) && (A < 1.65806) ) + { + turn_around = TRUE; + /* printf("TURN_AROUND\n");*/ + } + + } + + velocity = 10.0 * sqrt((dist) / (double)(thistime - lasttime)); + + + + make_stroke(airblob, xinput_airbrush_tool, drawable, last_value, present_value); + + xinput_airbrush_tool->last_lastx = xinput_airbrush_tool->lastx; + xinput_airbrush_tool->last_lasty = xinput_airbrush_tool->lasty; + xinput_airbrush_tool->lastx = x; + xinput_airbrush_tool->lasty = y; + + g_free(xinput_airbrush_tool->last_airblob); + xinput_airbrush_tool->last_airblob = airblob; + + xinput_airbrush_tool->init_velocity = TRUE; + xinput_airbrush_tool->init_prepre = TRUE; + + gdisplay_flush_now (gdisp); + } + +static void + xinput_airbrush_cursor_update (Tool *tool, + GdkEventMotion *mevent, + gpointer gdisp_ptr) + { + GDisplay *gdisp; + Layer *layer; + GdkCursorType ctype = GDK_TOP_LEFT_ARROW; + int x, y; + + gdisp = (GDisplay *) gdisp_ptr; + + gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, FALSE, FALSE); + if ((layer = gimage_get_active_layer (gdisp->gimage))) + { + int off_x, off_y; + drawable_offsets (GIMP_DRAWABLE(layer), &off_x, &off_y); + if (x >= off_x && y >= off_y && + x < (off_x + drawable_width (GIMP_DRAWABLE(layer))) && + y < (off_y + drawable_height (GIMP_DRAWABLE(layer)))) + { + /* One more test--is there a selected region? + * if so, is cursor inside? + */ + if (gimage_mask_is_empty (gdisp->gimage)) + ctype = GDK_PENCIL; + else if (gimage_mask_value (gdisp->gimage, x, y)) + ctype = GDK_PENCIL; + } + } + gdisplay_install_tool_cursor (gdisp, ctype); + } + +static void + xinput_airbrush_control (Tool *tool, + ToolAction action, + gpointer gdisp_ptr) + { + XinputAirbrushTool *xinput_airbrush_tool; + + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + switch (action) + { + case PAUSE: + draw_core_pause (xinput_airbrush_tool->core, tool); + break; + + case RESUME: + draw_core_resume (xinput_airbrush_tool->core, tool); + break; + + case HALT: + xinput_airbrush_cleanup (); + break; + + default: + break; + } + } + +static void + xinput_airbrush_init (XinputAirbrushTool *xinput_airbrush_tool, GimpDrawable *drawable, + double x, double y) + { + /* free the block structures */ + if (undo_tiles) + tile_manager_destroy (undo_tiles); + if (canvas_tiles) + tile_manager_destroy (canvas_tiles); + + /* Allocate the undo structure */ + undo_tiles = tile_manager_new (drawable_width (drawable), + drawable_height (drawable), + drawable_bytes (drawable)); + + /* Allocate the canvas blocks structure */ + canvas_tiles = tile_manager_new (drawable_width (drawable), + drawable_height (drawable), 1); + + /* Get the initial undo extents */ + xinput_airbrush_tool->x1 = xinput_airbrush_tool->x2 = x; + xinput_airbrush_tool->y1 = xinput_airbrush_tool->y2 = y; + } + +static void + xinput_airbrush_finish (XinputAirbrushTool *xinput_airbrush_tool, GimpDrawable *drawable, int tool_id) + { + /* push an undo */ + drawable_apply_image (drawable, xinput_airbrush_tool->x1, xinput_airbrush_tool->y1, + xinput_airbrush_tool->x2, xinput_airbrush_tool->y2, undo_tiles, TRUE); + undo_tiles = NULL; + + /* invalidate the drawable--have to do it here, because + * it is not done during the actual painting. + */ + drawable_invalidate_preview (drawable); + } + +static void + xinput_airbrush_cleanup (void) + { + /* CLEANUP */ + /* If the undo tiles exist, nuke them */ + if (undo_tiles) + { + tile_manager_destroy (undo_tiles); + undo_tiles = NULL; + } + + /* If the canvas blocks exist, nuke them */ + if (canvas_tiles) + { + tile_manager_destroy (canvas_tiles); + canvas_tiles = NULL; + } + + /* Free the temporary buffer if it exist */ + if (canvas_buf) + temp_buf_free (canvas_buf); + canvas_buf = NULL; + } + +/********************************* + * Rendering functions * + *********************************/ + +/* Some of this stuff should probably be combined with the + * code it was copied from in paint_core.c; but I wanted + * to learn this stuff, so I've kept it simple. + */ + + +static void +make_stroke(AirBlob *airblob, + XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + guint last_value, + guint present_value) +{ + + int steps; + int j; + + + int dx, dy; + int x, y; + + + int x0, x1, y0, y1; + + double x0d, x1d, y0d, y1d; + + int x_min, y_min, width, height; + + guint ivalue; + double lv, pv, mv; + + double dist; + + int slopeterm; + + AirBlob *last_airblob; + AirBlob *trans_blob; + AirLine *air_line; + MaskBuf *brush_mask; + + + x0 = xinput_airbrush_tool->lastx; + x1 = xinput_airbrush_tool->xcenter; + y0 = xinput_airbrush_tool->lasty; + y1 = xinput_airbrush_tool->ycenter; + + x0d = xinput_airbrush_tool->lastx; + x1d = xinput_airbrush_tool->xcenter; + y0d = xinput_airbrush_tool->lasty; + y1d = xinput_airbrush_tool->ycenter; + + last_airblob = xinput_airbrush_tool->last_airblob; + + steps = number_of_steps(x0, y0, x1, y1); + + + dx = abs(x0 - x1); + dy = abs(y0 - y1); + + lv = last_value; + pv = present_value; + mv = pv - lv; + + + /* + Yes I know this is bulky code :-), + --> see comment on make_mask. + + But I wanted to keep it simple while I + implemented the tool :-). I will make it + more effective later on. And yes I could + look for the three special cases. + */ + + + if (dy > dx) + { + /* Step in the y direction*/ + if (y0 < y1) + { + /* + Step from y0 to y1 + */ + if(x1 < x0) + { + /* + We have to x-- + */ + + x = x0; + y = y0; + + j = 1; + + slopeterm = dx; + + while(y < y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x--; + y++; + } + else + { + slopeterm += dx; + y++; + } + + j++; + + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + + } + else + { + /* + We have to x++ + */ + + x = x0; + y = y0; + + slopeterm = dx; + + j = 1; + + while(y < y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x++; + y++; + } + else + { + slopeterm += dx; + y++; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + } + else + { + /* Step from y0 to y1 with neg steps */ + if(x1 < x0) + { + /* + We have to x-- + */ + + x = x0; + y = y0; + + slopeterm = dx; + + j = 1; + + while(y > y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x--; + y--; + } + else + { + slopeterm += dx; + y--; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + else + { + /* + We have to x++ + */ + x = x0; + y = y0; + + slopeterm = dx; + + j = 1; + + while(y > y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x++; + y--; + } + else + { + slopeterm += dx; + y--; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + } + + } + else + { + /* Step in the X direction*/ + if (x0 < x1) + { + /* Step from x0 to x1 */ + if(y1 < y0) + { + /* + We have to y-- + */ + x = x0; + y = y0; + + slopeterm = dy; + + j = 1; + + while(x < x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y--; + x++; + } + else + { + slopeterm += dy; + x++; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + + } + else + { + /* + We have to y++ + */ + x = x0; + y = y0; + + slopeterm = dy; + + j = 1; + + while(x < x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y++; + x++; + } + else + { + slopeterm += dy; + x++; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + } + else + { + /* Step from x0 to x1 neg direction */ + if(y1 < y0) + { + /* + We have to y-- + */ + x = x0; + y = y0; + + slopeterm = dy; + + j = 1; + + while(x > x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y--; + x--; + } + else + { + slopeterm += dy; + x--; + } + + j++; + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + + } + } + else + { + /* + We have to y++ + */ + x = x0; + y = y0; + + slopeterm = dy; + + j = 1; + + while(x > x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y++; + x--; + } + else + { + slopeterm += dy; + x--; + } + + j++; + + dist = 1.0 - (double)j/(double)steps; + ivalue = lv + mv * dist; + + trans_blob = trans_air_blob(last_airblob, airblob, dist, x, y); + air_line = create_air_line(trans_blob); + + x_min = air_line->min_x; + y_min = air_line->min_y; + width = air_line->width; + height = air_line->height; + + brush_mask = mask_buf_new(width, height); + + make_mask(air_line, brush_mask, ivalue); + + /*Paint the mask on the drawable*/ + + xinput_airbrush_paste (xinput_airbrush_tool, drawable, brush_mask, x_min, y_min, width, height); + mask_buf_free(brush_mask); + g_free(air_line); + g_free(trans_blob); + } + + } + } + } +} + + + +/************************************************** +*************************************************** +*/ + + +static void +make_mask(AirLine *airline, + MaskBuf *dest, + guint value) +{ + + int steps; + int i; + int j; + + + int dx, dy; + int x, y; + + int x0, x1, y0, y1; + + + unsigned char *s; + guint midvalue; + double ivalue; + + int rowlength; + int slopeterm; + + + rowlength = dest->width * dest->bytes; + + for (i=0; i < airline->nlines ; i++) + { + + steps = number_of_steps(airline->xcenter, airline->ycenter, + airline->line[i].x, airline->line[i].y); + + x0 = airline->xcenter - airline->min_x; + x1 = airline->line[i].x - airline->min_x; + + y0 = airline->ycenter - airline->min_y; + y1 = airline->line[i].y -airline->min_y; + + + dx = abs(x0 - x1); + dy = abs(y0 - y1); + + + + /* + Yes I know this is bulky code :-), + you could insted set the x direction + to 1 or -1, and y to - or + rowside. + Then set the first X and Y and of you + go. + + But I wanted to keep it simple while I + implemented the tool :-). I will make it + more effective later on. And yes I could + look for the three special cases. + */ + + /* if (i >= 3) + { + midvalue = 0; + } + else + {*/ + midvalue = value; + /* }*/ + + + if (dy > dx) + { + /* Step in the y direction*/ + if (y0 < y1) + { + /* + Step from y0 to y1 + */ + if(x1 < x0) + { + /* + We have to x-- + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dx; + + j = 1; + + while(y < y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x--; + y++; + } + else + { + slopeterm += dx; + y++; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + + } + else + { + /* + We have to x++ + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dx; + + j = 1; + + while(y < y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x++; + y++; + } + else + { + slopeterm += dx; + y++; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + } + else + { + /* Step from y0 to y1 with neg steps */ + if(x1 < x0) + { + /* + We have to x-- + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dx; + + j = 1; + + while(y > y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x--; + y--; + } + else + { + slopeterm += dx; + y--; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + else + { + /* + We have to x++ + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dx; + + j = 1; + + while(y > y1) + { + if (slopeterm > dy) + { + slopeterm -= dy; + x++; + y--; + } + else + { + slopeterm += dx; + y--; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + } + + } + else + { + /* Step in the X direction*/ + if (x0 < x1) + { + /* Step from x0 to x1 */ + if(y1 < y0) + { + /* + We have to y-- + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dy; + + j = 1; + + while(x < x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y--; + x++; + } + else + { + slopeterm += dy; + x++; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + + } + else + { + /* + We have to y++ + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dy; + + j = 1; + + while(x < x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y++; + x++; + } + else + { + slopeterm += dy; + x++; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + } + else + { + /* Step from x0 to x1 neg direction */ + if(y1 < y0) + { + /* + We have to y-- + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dy; + + j = 1; + + while(x > x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y--; + x--; + } + else + { + slopeterm += dy; + x--; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + } + else + { + /* + We have to y++ + */ + s = dest->data; + + x = x0; + y = y0; + + s += y * rowlength + x; + + *s = midvalue; + + slopeterm = dy; + + j = 1; + + while(x > x1) + { + if (slopeterm > dx) + { + slopeterm -= dx; + y++; + x--; + } + else + { + slopeterm += dy; + x--; + } + + j++; + + s = dest->data; + s += y * rowlength + x; + + ivalue = value * (1.0 - (double)j/(double)steps); + + *s = ivalue; + } + + } + } + } + } + + +} + + + + +static void +xinput_airbrush_set_paint_area (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + int x, int y, int width, int height) +{ + int iwidth, iheight; + int x1, y1, x2, y2; + int bytes; + int dwidth, dheight; + + bytes = drawable_has_alpha (drawable) ? + drawable_bytes (drawable) : drawable_bytes (drawable) + 1; + + dwidth = drawable_width (drawable); + dheight = drawable_height (drawable); + + + x1 = CLAMP (x, 0, dwidth); + y1 = CLAMP (y, 0, dheight); + x2 = CLAMP ((x + width), 0, dwidth); + y2 = CLAMP ((y + height), 0, dheight); + + iwidth = MIN((x2 - x1), width); + iheight = MIN((y2 - y1), height); + + + + /* configure the canvas buffer */ + + if ((x2 - x1) && (y2 - y1)) + canvas_buf = temp_buf_resize (canvas_buf, bytes, x1, y1, + iwidth, iheight); + + + + +} + + +static void +render_airbrush_line (AirBrushBlob *airbrush_blob, guchar *dest, + int y, int width, XinputAirbrushTool *xinput_airbrush_tool) +{ + int i, j, k, l, m, g; + + int left, right; + + int brush_width; + + double x_dest; + double xdist, ydist, dist, angle; + double i_value; + guchar value; + + + + if (airbrush_blob->height <= 6) + { + return; + } + left = 0; + right = 0; + + j = y * SUBSAMPLE; + g = y * SUBSAMPLE; + + for (i=0; i= airbrush_blob->height) + { + if ( i == 0) + { + return; + } + else + { + left = airbrush_blob->data[g].left - airbrush_blob->min_x; + right = airbrush_blob->data[g].right - airbrush_blob->min_x; + break; + } + } + if ( i == SUBSAMPLE/2) + { + left = airbrush_blob->data[j].left - airbrush_blob->min_x; + right = airbrush_blob->data[j].right - airbrush_blob->min_x; + break; + } + + } + + brush_width = right - left; + + if (brush_width <= SUBSAMPLE*2) + { + return; + } + + dest = dest + (left/SUBSAMPLE); + + for (i=4; i <= brush_width; i = i + SUBSAMPLE) + { + x_dest = (left + i)/SUBSAMPLE; + xdist = (airbrush_blob->min_x/SUBSAMPLE) + x_dest - xinput_airbrush_tool->xcenter; + ydist = xinput_airbrush_tool->ycenter - (airbrush_blob->y/SUBSAMPLE) - y; + angle = atan2(ydist, xdist) + M_PI; + dist = hypot(xdist, ydist); + for (j=1; j< (airbrush_blob->height - 1) ; j++) + { + k = j - 1; + l = j + 1; + m = 0; + if ( (fabs(airbrush_blob->data[k].angle_right_abs - airbrush_blob->data[l].angle_right_abs) > + fabs(airbrush_blob->data[k].angle_right_abs - angle)) && + (fabs(airbrush_blob->data[k].angle_right_abs - airbrush_blob->data[l].angle_right_abs) > + fabs(airbrush_blob->data[l].angle_right_abs - angle)) + ) + { + i_value = MIN(1., (dist*SUBSAMPLE)/(airbrush_blob->data[j].dist_right)); + value = 255 * (1 - i_value); + *dest = value; + dest++; + m = 1; + break; + } + + else if ( (fabs(airbrush_blob->data[k].angle_left_abs - airbrush_blob->data[l].angle_left_abs) > + fabs(airbrush_blob->data[k].angle_left_abs - angle)) && + (fabs(airbrush_blob->data[k].angle_left_abs - airbrush_blob->data[l].angle_left_abs) > + fabs(airbrush_blob->data[l].angle_left_abs - angle)) + ) + { + i_value = MIN(1., (dist*SUBSAMPLE)/(airbrush_blob->data[j].dist_left)); + value = 255 * (1 - i_value); + *dest = value; + dest++; + m = 1; + break; + } + else if ((j == (airbrush_blob->height - 2)) && ( m == 0)) + { + *dest = 0; + dest++; + } + } + } + + +} + + + +static void +calc_width (AirBrushBlob *airbrush_blob) +{ + int i; + int min_x, max_x; + + i = (airbrush_blob->height)/2; + + min_x = airbrush_blob->data[i].left; + max_x = airbrush_blob->data[i].right; + + for (i=0; i< (airbrush_blob->height) ; i++) + + { + + if ( airbrush_blob->data[i].left <= airbrush_blob->data[i].right) + { + min_x = MIN(airbrush_blob->data[i].left, min_x); + } + + + if ( airbrush_blob->data[i].left <= airbrush_blob->data[i].right) + { + max_x = MAX(airbrush_blob->data[i].right, max_x ); + } + } + + airbrush_blob->width = max_x - min_x; + airbrush_blob->min_x = min_x; + airbrush_blob->max_x = max_x; +} + + +static void +calc_angle (AirBrushBlob *airbrush_blob, double xcenter, double ycenter) +{ + + + int i; + double y_center, x_center; + + double y_dist, x_dist; + double left_ang, right_ang; + double left_ang_abs, right_ang_abs; + int min_x, max_x; + + + + x_center = xcenter * SUBSAMPLE; + y_center = ycenter * SUBSAMPLE - airbrush_blob->y; + + i = (airbrush_blob->height)/2; + + min_x = airbrush_blob->data[i].left; + max_x = airbrush_blob->data[i].right; + + for (i=0; i< (airbrush_blob->height) ; i++) + + { + + y_dist = y_center - i; + x_dist = airbrush_blob->data[i].left - x_center; + left_ang_abs = atan2(y_dist, x_dist) + M_PI; + left_ang = atan(y_dist/x_dist); + airbrush_blob->data[i].angle_left = left_ang; + airbrush_blob->data[i].angle_left_abs = left_ang_abs; + airbrush_blob->data[i].dist_left = hypot(x_dist, y_dist); + if ( airbrush_blob->data[i].left <= airbrush_blob->data[i].right) + { + min_x = MIN(airbrush_blob->data[i].left, min_x); + } + + x_dist = airbrush_blob->data[i].right - x_center; + right_ang_abs = atan2(y_dist, x_dist) + M_PI; + right_ang = atan(y_dist/x_dist); + airbrush_blob->data[i].angle_right = right_ang; + airbrush_blob->data[i].angle_right_abs = right_ang_abs; + airbrush_blob->data[i].dist_right = hypot(x_dist, y_dist); + if ( airbrush_blob->data[i].left <= airbrush_blob->data[i].right) + { + max_x = MAX(airbrush_blob->data[i].right, max_x ); + } + + + + } + + airbrush_blob->width = max_x - min_x; + airbrush_blob->min_x = min_x; + airbrush_blob->max_x = max_x; + + +} + + + +static void +make_single_mask(AirBrushBlob *airbrush_blob, XinputAirbrushTool *xinput_airbrush_tool, MaskBuf *dest) +{ + unsigned char * s; + int h; + int i; + + h = dest->height; + s = dest->data; + + for (i=0; iwidth, xinput_airbrush_tool); + s += (dest->width * dest->bytes); + } + +} + +static void +print_line (guchar *dest, + int width) +{ + + int i; + + for (i=0; i < width; i++) + { + printf("%d ", *dest); + dest++; + } +} + +static void +print_mask(MaskBuf *dest) +{ + unsigned char * s; + int h; + int i; + + h = dest->height; + s = dest->data; + printf("\nBrush_Mask\n\n"); + for (i=0; iwidth); + s += (dest->width * dest->bytes); + printf("\n"); + } + printf("\n"); +} + + + +static void +xinput_airbrush_paste (XinputAirbrushTool *xinput_airbrush_tool, + GimpDrawable *drawable, + MaskBuf *brush_mask, + int x, + int y, + int width, + int height) +{ + GImage *gimage; + PixelRegion srcPR; + int offx, offy; + unsigned char col[MAX_CHANNELS]; + + + /*print_mask(brush_mask);*/ + + + if (! (gimage = drawable_gimage (drawable))) + return; + + + /* Get the the buffer */ + xinput_airbrush_set_paint_area (xinput_airbrush_tool, drawable, x, y, width, height); + + /* check to make sure there is actually a canvas to draw on */ + if (!canvas_buf) + return; + + gimage_get_foreground (gimage, drawable, col); + + /* set the alpha channel */ + col[canvas_buf->bytes - 1] = OPAQUE_OPACITY; + + /* color the pixels */ + color_pixels (temp_buf_data (canvas_buf), col, + canvas_buf->width * canvas_buf->height, canvas_buf->bytes); + + /* set undo blocks */ + xinput_airbrush_set_undo_tiles (drawable, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height); + + /* initialize any invalid canvas tiles */ + xinput_airbrush_set_canvas_tiles (canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height); + + /*DON'T FORGETT THE 100 VALUE, I.E. THE BRUSH OPACITY WHICH SHOULD BE 255*/ + + xinput_airbrush_to_canvas_tiles (xinput_airbrush_tool, brush_mask, 100); + + xinput_airbrush_canvas_tiles_to_canvas_buf(xinput_airbrush_tool); + + + /* initialize canvas buf source pixel regions */ + srcPR.bytes = canvas_buf->bytes; + srcPR.x = 0; + srcPR.y = 0; + srcPR.w = canvas_buf->width; + srcPR.h = canvas_buf->height; + srcPR.rowstride = canvas_buf->width * canvas_buf->bytes; + srcPR.data = temp_buf_data (canvas_buf); + + + /* apply the paint area to the gimage */ + gimage_apply_image (gimage, drawable, &srcPR, + FALSE, + (int) (gimp_context_get_opacity (NULL) * 255), + gimp_context_get_paint_mode (NULL), + undo_tiles, /* specify an alternative src1 */ + canvas_buf->x, canvas_buf->y); + + /* Update the undo extents */ + xinput_airbrush_tool->x1 = MIN (xinput_airbrush_tool->x1, canvas_buf->x); + xinput_airbrush_tool->y1 = MIN (xinput_airbrush_tool->y1, canvas_buf->y); + xinput_airbrush_tool->x2 = MAX (xinput_airbrush_tool->x2, (canvas_buf->x + canvas_buf->width)); + xinput_airbrush_tool->y2 = MAX (xinput_airbrush_tool->y2, (canvas_buf->y + canvas_buf->height)); + + /* Update the gimage--it is important to call gdisplays_update_area + * instead of drawable_update because we don't want the drawable + * preview to be constantly invalidated + */ + drawable_offsets (drawable, &offx, &offy); + gdisplays_update_area (gimage, canvas_buf->x + offx, canvas_buf->y + offy, + canvas_buf->width, canvas_buf->height); + +} + + + +static void +xinput_airbrush_to_canvas_tiles (XinputAirbrushTool *xinput_airbrush_tool, + MaskBuf *brush_mask, + int brush_opacity) +{ + PixelRegion srcPR, maskPR; + + /* combine the brush mask and the canvas tiles */ + pixel_region_init (&srcPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, TRUE); + + maskPR.bytes = 1; + maskPR.x = 0; + maskPR.y = 0; + maskPR.w = srcPR.w; + maskPR.h = srcPR.h; + maskPR.rowstride = maskPR.bytes * brush_mask->width; + maskPR.data = mask_buf_data (brush_mask); + + /* combine the mask to the canvas tiles */ + combine_mask_and_region (&srcPR, &maskPR, brush_opacity); +} + + + + +static void +xinput_airbrush_canvas_tiles_to_canvas_buf(XinputAirbrushTool *xinput_airbrush_tool) +{ + PixelRegion srcPR, maskPR; + + /* combine the canvas tiles and the canvas buf */ + srcPR.bytes = canvas_buf->bytes; + srcPR.x = 0; + srcPR.y = 0; + srcPR.w = canvas_buf->width; + srcPR.h = canvas_buf->height; + srcPR.rowstride = canvas_buf->width * canvas_buf->bytes; + srcPR.data = temp_buf_data (canvas_buf); + + pixel_region_init (&maskPR, canvas_tiles, + canvas_buf->x, canvas_buf->y, + canvas_buf->width, canvas_buf->height, FALSE); + + /* apply the canvas tiles to the canvas buf */ + apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY); +} + + + +static void +xinput_airbrush_set_undo_tiles (GimpDrawable *drawable, + int x, int y, int w, int h) +{ + int i, j; + Tile *src_tile; + Tile *dest_tile; + + for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT))) + { + for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH))) + { + dest_tile = tile_manager_get_tile (undo_tiles, j, i, FALSE, FALSE); + if (tile_is_valid (dest_tile) == FALSE) + { + src_tile = tile_manager_get_tile (drawable_data (drawable), j, i, TRUE, FALSE); + tile_manager_map_tile (undo_tiles, j, i, src_tile); + tile_release (src_tile, FALSE); + } + } + } +} + + +static void +xinput_airbrush_set_canvas_tiles (int x, int y, int w, int h) +{ + int i, j; + Tile *tile; + + for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT))) + { + for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH))) + { + tile = tile_manager_get_tile (canvas_tiles, j, i, FALSE, FALSE); + if (tile_is_valid (tile) == FALSE) + { + tile = tile_manager_get_tile (canvas_tiles, j, i, TRUE, TRUE); + memset (tile_data_pointer (tile, 0, 0), + 0, + tile_size (tile)); + tile_release (tile, TRUE); + } + } + } +} + +/**************************/ +/* Global xinput_airbrush functions */ +/**************************/ + +void +xinput_airbrush_no_draw (Tool *tool) +{ + return; +} + +Tool * +tools_new_xinput_airbrush (void) +{ + Tool * tool; + XinputAirbrushTool * private; + + /* The tool options */ + if (! xinput_airbrush_options) + { + xinput_airbrush_options = xinput_airbrush_options_new (); + tools_register (XINPUT_AIRBRUSH, (ToolOptions *) xinput_airbrush_options); + + /* press all default buttons */ + xinput_airbrush_options_reset (); + } + + tool = tools_new_tool (XINPUT_AIRBRUSH); + private = g_new (XinputAirbrushTool, 1); + + private->core = draw_core_new (xinput_airbrush_no_draw); + private->last_airbrush_blob = NULL; + + tool->private = private; + + tool->button_press_func = xinput_airbrush_button_press; + tool->button_release_func = xinput_airbrush_button_release; + tool->motion_func = xinput_airbrush_motion; + tool->cursor_update_func = xinput_airbrush_cursor_update; + tool->control_func = xinput_airbrush_control; + + return tool; +} + +void +tools_free_xinput_airbrush(Tool *tool) +{ + XinputAirbrushTool * xinput_airbrush_tool; + + xinput_airbrush_tool = (XinputAirbrushTool *) tool->private; + + /* Make sure the selection core is not visible */ + if (tool->state == ACTIVE && xinput_airbrush_tool->core) + draw_core_stop (xinput_airbrush_tool->core, tool); + + /* Free the selection core */ + if (xinput_airbrush_tool->core) + draw_core_free (xinput_airbrush_tool->core); + + /* Free the last airbrush_blob, if any */ + if (xinput_airbrush_tool->last_airbrush_blob) + g_free (xinput_airbrush_tool->last_airbrush_blob); + + /* Cleanup memory */ + xinput_airbrush_cleanup (); + + /* Free the paint core */ + g_free (xinput_airbrush_tool); +} diff --git a/app/xinput_airbrush.h b/app/xinput_airbrush.h new file mode 100644 index 0000000000..db12ac2ff8 --- /dev/null +++ b/app/xinput_airbrush.h @@ -0,0 +1,30 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef __XINPUTAIRBRUSH_H__ +#define __XINPUTAIRBRUSH_H__ + +#include "tools.h" +#include "procedural_db.h" + +Tool * tools_new_xinput_airbrush (void); +void tools_free_xinput_airbrush (Tool *); + +/* Procedure definition and marshalling function */ +extern ProcRecord xinput_airbrush_proc; + +#endif /* __XINPUTAIRBRUSH_H__ */