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

Vector gradient fill

From Allegro Wiki
Jump to: navigation, search

Intro

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

Linear

  • Linear tint with control points:
   Image:linear_cp.png 

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:
   Image:radial_cp.png 

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 | mimg1285.gif | (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:
   Image:int_grad.png 



  • 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>