The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

# Vector gradient fill

## Contents

# Intro

Filled areas in VectorGraphics are more complicated than for rasters. Each discrete filled area is a polygon .

# Linear

- Linear tint with control points:

Notes:

- Linear tints need the same 3 control points that Radial (and textured) fills use, in order to survive being sheared in transformations. A lot of the guff I wrote on this subject became obsolete when I realised this --

# Radial

- Radial tint showing control points:

Notes:

- This fill pattern is commonly called Radial (e.g. in Macromedia Flash) but could more correctly called Circular or Concentric. Paint Shop Pro calls it Sunburst.

# Transforming pixel co-ordinates to a,b space

The vectors **oa** and **ob** are axes in their own 2d space. A vector can be transformed into pixel co-ordinates by multiplying by the following vector

TODO: matmul1.png

| a.x , b.x | | v.a | = | v.x | | a.y , b.y | | v.b | = | v.y |

What we need for rendering though is the other way around, so we want the inverse of the matrix so that

M | v.x | = | v.a | | v.y | = | v.b |

The inverse of a 2x2 matrix | a b | is given by | | (from mathworld.wolfram.com)

Provided we have checked that neither **oa** or **ob** is of zero length, and **o**, **a** and **b** are not otherwise inline, then we can safely assume the determinant of the matrix is non-zero and the matrix is inversible.

So,

1 | b.y , -b.x | | v.x | = | v.a | ----------------------- |-a.y , a.x | | v.y | | v.b | a.x * b.y - b.x * a.y

and thus, the code to get the a,b co-ordinates of the point *(v.x,v.y)* is :-

<highlightSyntax language="c">

v.a = ( b.y * v.x - b.x * v.y ) / (a.x * b.y - b.x * a.y); v.b = (-a.y * v.x + a.x * v.y ) / (a.x * b.y - b.x * a.y);

</highlightSyntax>

Do I divide the determinant like this? -- Main.MattyMatt

# Rendering by scanlines

- The results of the necessary transformation are calculated once before filling starts
- Each pixel is taken from the colourspace of the transformed fill pattern

- An array of ints holds the gradient:

- Each scanline is passed the following params

*** BITMAP * *bmp** bitmap to draw to, this and **y** will be replaced by a single memory pointer in the final tuned version.
*** int *x** left pixel
*** int *y** y position to draw to
*** float *a**,*b* co-ordinates in vector space. Linear tints only need the **a** co-ord.
*** float *da**,*db* delta for each increment in **x**.
*** int * *t** table of colours

<highlightSyntax language="c">
void draw_lineartint_scanline_( BITMAP * bmp, int x, int y, float a, float b, float da, float db, int t);
void draw_radialtint_scanline_( BITMAP * bmp, int x, int y, float a, float b, float da, float db, int t);
void draw_texturetint_scanline_( BITMAP * bmp, int x, int y, float a, float b, float da, float db, int t);

void draw_lineartint_scanline_(BITMAP * bmp, int x1, int y, int x2,

float a, float b, float da, float db, int * t)

{

int d = ftofix(a); int dx = ftofix(da);

for (x = x1; x<x2; x++) { d = pt >> 6; // scale fixed 0~1 to table index. if ( d < 0) d = 0; // clamp min if ( d >= VG_SIZE) d = VG_SIZE-1; // clamp max c = t[d]; putpixel(bmp, x, y, c); d+=dx; }

}

}

rectfill_tint( BITMAP * bmp, int x , int y, int x2, int y2, TINT * tint) {

start_a = tint->oax * x + tint->oay * y; start_b = tint->obx * x + tint->oby * y;

/*delta a & b are oax and oay */

for (y1 =y; y1<= y2; y1++)

draw_lineartint_scanline_(bmp, x, y1, start_a += tint->oay, start_b += tint->oby, tint->oax, tint->obx, tint->colortable );

}

setup_tint( TINT * tint, int x, int y, fixed scale, fixed rotate) {

fixed s = fixsin(rotate);
fixed c = fixcos(rotate);

tint->oox = x + (in->ox * c + in->oy *s) * scale; tint->ooy = y + (in->ox * s - in->oy *c) * scale; tint->oax = x + (in->ax * c + in->ay *s) * scale; tint->oay = y + (in->ax * s - in->ay *c) * scale;

tint->obx = x + (in->bx * c + in->by *s) * scale; tint->oby = y + (in->bx * s - in->by *c) * scale;

}

/* now invert the new oa and ob vectors */

out-> ov.a = ( b.y * v.x - b.x * v.y ) / (a.x * b.y - b.x * a.y); out-> ov.b = (-a.y * v.x + a.x * v.y ) / (a.x * b.y - b.x * a.y);

}

</highlightSyntax>