terug naar artikel
back to article

Soft drawing: Delphi source listing

unit Unit1;
{
  softline drawing project
}
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, shellApi;

type
  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
    clearBtn: TButton;
    linesBtn: TButton;
    PaintBox2: TPaintBox;
    colorBtn: TButton;
    ellipseBtnButton1: TButton;
    HscrollBar: TScrollBar;
    vscrollbar: TScrollBar;
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject);
    procedure clearBtnClick(Sender: TObject);
    procedure linesBtnClick(Sender: TObject);
    procedure colorBtnClick(Sender: TObject);
    procedure ellipseBtnButton1Click(Sender: TObject);
    procedure PaintBox2Paint(Sender: TObject);
    procedure HscrollBarChange(Sender: TObject);
    procedure Image1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type PDW = ^dword;
     TPendirection = (pdHorizontal,pdVertical);

const hpen : array[0..3] of word = ($8888,$4444,$2222,$1111);
      vpen : array[0..3] of word = ($f000,$0f00,$00f0,$000f);
      hmask: array[0..3] of word = ($ffff,$7777,$3333,$1111);
      vmask: array[0..3] of word = ($ffff,$0fff,$00ff,$000f);
      fblend : array[0..16] of single =
               (0.0, 0.0625, 0.125, 0.1875, 0.25, 0.3125, 0.375, 0.4375,
                0.5, 0.5625, 0.625, 0.6875, 0.75, 0.8125, 0.875, 0.9375, 1.0);
      blenderwidth = 601;
      blenderheight = 601;

var bm : Tbitmap;
    line0 : dword;
    LineStep : dword;
    blender : array[0..blenderwidth-1,0..blenderheight-1] of word;
    blendRect : Trect;
    pencolor : dword;
    pendirection : TPendirection;

procedure sort(var a,b : smallInt);
//sort small to large
var h : smallInt;
begin
 if a > b then
  begin
   h := a; a := b; b := h;
  end;
end;

function packrect(a,b,c,d : smallInt) : Trect;
begin
 sort(a,c);
 sort(b,d);
 with result do
  begin
   left := a;
   top := b;
   right := c+1;
   bottom := d+1;
  end;
end;

procedure copybmrect;
begin
 form1.PaintBox1.Canvas.CopyRect(blendRect,bm.Canvas,blendRect);
end;

function popcount(a : word) : byte;
//count nr of 1 bits in a
var i : byte;
begin
 result := 0;
 for i := 0 to 15 do
  begin
   result := result + (a and $1);
   a := a shr 1;
  end;
end;

procedure procblendRect;
//blend pixels in abmRect into bm bitmap
var x,y : word;
    px,py,col : dword;
    r,g,b,pop : byte;
    penR,penG,penB : byte;
    fact,fact1 : single;
begin
 penR := pencolor and $ff;
 penG := (pencolor shr 8) and $ff;
 penB := (pencolor shr 16) and $ff;
 for y := blendRect.top to blendRect.bottom-1 do
  begin
   py := line0 - y*lineStep;
   for x := blendRect.Left to blendRect.Right-1 do
    begin
     px := py + (x shl 2);
     if blender[x,y] > 0 then
      begin
       pop := popcount(blender[x,y]);
       fact := fblend[pop];
       fact1 := 1-fact;
       col := PDW(px)^;
       r := (col shr 16) and $ff;
       g := (col shr 8) and $ff;
       b := col and $ff;
       r := round(fact1*r + fact*penR);
       g := round(fact1*g + fact*penG);
       b := round(fact1*b + fact*penB);
       col := (r shl 16) or (g shl 8) or b;
       PDW(px)^ := col;
       blender[x,y] := 0;
      end;
    end;//x
  end;//y
end;

procedure Qpixel(x,y : single);
//x,y: pixel.quarter
//use array abm for blending
var h : smallInt;
    px,mpx,py,mpy : smallInt;
    pen : word;
begin
 h := round(x*4);
 px := h shr 2;
 mpx := h and $3;
 h := round(y*4);
 py := h shr 2;
 mpy := h and $3;
 case pendirection of
  pdHorizontal : begin
                  pen := hpen[mpx] and vmask[mpy];
                  blender[px,py] := blender[px,py] or pen;
                  pen := hpen[mpx] and (vmask[mpy] xor $ffff);
                  blender[px,py+1] := blender[px,py+1] or pen;
                 end;
  pdVertical : begin
                pen := vpen[mpy] and hmask[mpx];
                blender[px,py] := blender[px,py] or pen;
                pen := vpen[mpy] and (hmask[mpx] xor $ffff);
                blender[px+1,py] := blender[px+1,py] or pen;
               end;
 end;//case
end;

procedure tradeXY(var x1,y1,x2,y2 : smallInt);
var hx,hy : smallInt;
begin
 hx := x1;
 hy := y1;
 x1 := x2;
 y1 := y2;
 x2 := hx;
 y2 := hy;
end;

procedure tradeAB(var a,b : smallInt);
var h : smallInt;
begin
 h := a;
 a := b;
 b := h;
end;

procedure softLine(x1,y1,x2,y2 : smallInt);
//draw softline in array abm, blend into bitmap bm
//pen width = 1
//color = col
//set mod rect
var hor : boolean;
    steps : smallInt;
    absx,absy : smallInt;
    i : smallInt;
    x,y,dx,dy : single;
begin
 absx := abs(x1-x2);
 absy := abs(y1-y2);
 hor := absx >= absy;
 if hor then
  begin
   if x1 > x2 then tradeXY(x1,y1,x2,y2);
   steps := (absx shl 2) + 3;
   pendirection := pdHorizontal;
  end
  else
   begin
    if y1 > y2 then tradeXY(x1,y1,x2,y2);
    steps := (absy shl 2) + 3;
    pendirection := pdVertical;
   end;
 dx := (x2-x1)/steps;
 dy := (y2-y1)/steps;
 x := x1;
 y := y1;
 for i := 0 to steps do
  begin
   Qpixel(x,y);
   x := x + dx;
   y := y + dy;
  end;
 blendrect := packrect(x1,y1,x2,y2);
 procBlendrect;
end;

procedure SoftEllipse(x1,y1,x2,y2:smallInt);
//paint softline ellipse
var a,b,i,mx,my : smallInt;
    x,y,sx,sy,sqra,sqrb,sqrab,adivb,bdiva : single;
begin
 a := abs(x2-x1) shr 1;
 b := abs(y2-y1) shr 1;
 if (a < 2) or (b < 2) then exit;
//
 sqra := a*a;
 sqrb := b*b;
 mx := (x1 + x2) shr 1;
 my := (y1 + y2) shr 1;
 if x1 > x2 then tradeAB(x1,x2);
 if y1 > y2 then tradeAB(y1,y1);
 sqrab := sqrt(sqra + sqrb);
 sx := sqra / sqrab;
 sy := sqrb / sqrab;
 bdiva := ba;
 adivb := ab;
 pendirection := pdHorizontal;
 x := 0;
 for i := 0 to trunc(sx *4) + 1 do
  begin
   y := bdiva*sqrt(sqra - x*x);
   Qpixel(mx+x,my-y);
   Qpixel(mx-x,my-y);
   Qpixel(mx+x,my+y);
   Qpixel(mx-x,my+y);
   x := x + 0.25;
  end;
 pendirection := pdVertical;
 y := 0;
 for i := 0 to trunc(sy * 4) + 1 do
  begin
   x := adivb*sqrt(sqrb - y*y);
   Qpixel(mx+x,my-y);
   Qpixel(mx-x,my-y);
   Qpixel(mx+x,my+y);
   Qpixel(mx-x,my+y);
   y := y + 0.25;
  end;
 blendRect := packRect(x1,y1,x2,y2);
 procBlendrect;
end;

procedure UpdateBox2;
//expand box1 pixels
var i,j,sx,sy : word;
    color : dword;
    r : Trect;
begin
 sx := form1.hscrollbar.position*40;
 sy := form1.vscrollbar.position*40;
 with form1.PaintBox2.Canvas do
  begin
   brush.Style := bsSolid;
   for j := 0 to 39 do
    for i := 0 to 39 do
     begin
      color := bm.Canvas.pixels[sx+i,sy+j];
      r := rect(i*10+1,j*10+1,i*10+9,j*10+9);
      brush.color := color;
      fillrect(r);
     end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 bm := TBitmap.Create;
 with bm do
  begin
   width := 601;
   height := 601;
   pixelformat := pf32bit;
   line0 := dword(scanline[0]);
   LineStep := line0 - dword(scanline[1]);
  end; 
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 bm.Free;
end;

procedure paintboxEdges(box : TPaintbox);
//paint edges around painybox on form1 canvas
var x1,y1,x2,y2 : word;
    i : byte;
begin
 with box do
  begin
   x1 := left-2;
   x2 := left+width+1;
   y1 := top-2;
   y2 := top + height+1;
  end;
 with form1.canvas do
  begin
   pen.Width := 1;
   for i := 0 to 1 do
    begin
     pen.color := 0;
     moveto(x2-i,y1+i);
     lineto(x1+i,y1+i);
     lineto(x1+i,y2-i);
     pen.color := $a0a0a0;
     lineto(x2-i,y2-i);
     lineto(x2-i,y1+i);
    end;
  end;
end;

procedure TForm1.FormPaint(Sender: TObject);
//paint edge of paintbox on form canvas
begin
 paintboxEdges(paintbox1);
 paintboxEdges(paintbox2);
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
 paintbox1.canvas.Draw(0,0,bm);
end;

procedure TForm1.clearBtnClick(Sender: TObject);
var i,j : smallInt;
    r : Trect;
begin
 for j := 0 to blenderheight-1 do
  for i := 0 to blenderwidth-1 do blender[i,j] := 0;
 with bm do with canvas do
  begin
   brush.color := $ffffff;
   brush.Style := bsSolid;
   r := rect(0,0,width,height);
   fillrect(r);
  end;
 paintbox1.Canvas.Draw(0,0,bm);
 with paintbox2 do with canvas do
  begin
   brush.Color := $ffffff;
   brush.Style := bsSolid;
   fillrect(rect(0,0,width,height));
  end;
 paintbox2.Invalidate;//repaint raster
 updateBox2; 
end;

procedure TForm1.linesBtnClick(Sender: TObject);
var x1,y1,x2,y2 : smallInt;
    i : word;
begin
 for i := 0 to 20 do
  begin
   x2 := 200 + round(180*cos(0.05*pi*i));
   y2 := 200 + round(180*sin(0.05*pi*i));
   x1 := 400 - x2;
   y1 := 400 - y2;
   softline(x1,y1,x2,y2);
  end;
 paintbox1.Canvas.draw(0,0,bm);
 UpdateBox2;
end;

procedure TForm1.ellipseBtnButton1Click(Sender: TObject);
begin
 softellipse(10,10,50,391);
 softellipse(10,10,390,51);
 softellipse(10,10,391,391);
 softellipse(20,20,381,381);
 blendRect := rect(0,0,400,400);
 paintbox1.Canvas.draw(0,0,bm);
 updateBox2;
end;

procedure TForm1.colorBtnClick(Sender: TObject);
//add color rectangles to bm
var i : byte;
    r: Trect;
begin
 with bm.Canvas do
  for i := 0 to 2 do
   begin
    r := rect(50,50+100*i,350,100+100*i);
    brush.Color := $ff shl (i shl 3);
    brush.Style := bsSolid;
    fillrect(r);
   end;
 paintbox1.Canvas.Draw(0,0,bm);
 updateBox2;
end;

procedure TForm1.PaintBox2Paint(Sender: TObject);
//paint raster 10*10
var i : word;
begin
 with paintbox2 do with canvas do
  begin
   brush.color := $ffffff;
   brush.style := bsSolid;
   fillrect(rect(0,0,width,height));
   pen.width := 1;
   pen.Color := $c0c0c0;
   i := 0;
   while i < width do
    begin
     moveto(i,0); lineto(i,height);
     inc(i,10);
    end;
   i := 0;
   while i < height do
    begin
     moveto(0,i); lineto(width,i);
     inc(i,10);
    end;
  end;
end;

procedure TForm1.HscrollBarChange(Sender: TObject);
begin
 updateBox2;
end;

procedure TForm1.Image1Click(Sender: TObject);
//davdata website elink
begin
 ShellExecute(0,'open','http://www.davdata.nl', nil, nil, SW_SHOWNORMAL);
end;

end.