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.