1

I am using the free JansDraw Components and when the executable runs, it throws AV error. I could locate the error to specifically the loop block of the code (not the initial block of assignment statements).

procedure TjanDrawImage.colorcircle(var bm:TBitmap;center:tpoint;radius,mode:integer);
var p,p0,p1:pbytearray;
    dx,x,y,w,h,i,j,sum,c:integer;
    cm,tm:tbitmap;
    Rs,Rd:trect;
begin
x:=center.x;
y:=center.y;
w:=bm.width;
h:=bm.height;
cm:=tbitmap.create;
cm.width:=2*radius;
cm.height:=2*radius;
cm.PixelFormat :=FPixelFormat;
tm:=tbitmap.create;
tm.width:=2*radius;
tm.height:=2*radius;
tm.PixelFormat :=FPixelFormat;
tm.canvas.brush.color:=clblack;
tm.canvas.Ellipse (0,0,tm.width-1,tm.height-1);
tm.transparent:=true;
tm.TransparentColor :=clblack;
Rd:=rect(0,0,cm.width,cm.height);
Rs:=rect(x-radius,y-radius,x+radius,y+radius);
cm.canvas.CopyRect (Rd,bm.canvas,RS);

    for j:=0 to cm.height-1 do begin
     p:=cm.scanline[j];
     if j>0 then p0:=cm.scanline[j-1];
     if j<(h-1) then p1:=cm.scanline[j+1];
     for i:=0 to cm.width-1 do begin
      case mode of
      0: //blue
       begin
         p[i*3+1]:=0;
         p[i*3+2]:=0;
       end;
      1: //green
       begin
         p[i*3]:=0;
         p[i*3+2]:=0;
       end;
      2: //red
       begin
         p[i*3]:=0;
         p[i*3+1]:=0;
       end;
      3: //not blue
       begin
         p[i*3]:=0;
       end;
      4: //not green
       begin
         p[i*3+1]:=0;
       end;
      5: //not red
       begin
         p[i*3+2]:=0;
       end;
      6: //half blue
       begin
         p[i*3]:=p[i*3]*9 div 10;
       end;
      7: //half green
       begin
         p[i*3+1]:=p[i*3+1]*9 div 10;
       end;
      8: //half red
       begin
         p[i*3+2]:=p[i*3+2]*9 div 10;
       end;
      9:// darker
       begin
         p[i*3]:=round(p[i*3]*10 /11);
         p[i*3+1]:=round(p[i*3+1]*10 / 11);
         p[i*3+2]:=round(p[i*3+2]*10 /11);
       end;
      10:// lighter
       begin
         p[i*3]:=round(p[i*3]*11 / 10);
         p[i*3+1]:=round(p[i*3+1]*11 / 10);
         p[i*3+2]:=round(p[i*3+2]*11 / 10);
       end;
      11:// gray
       begin
         sum:=round((p[i*3]+p[i*3+1]+p[i*3+2])/ 3);
         p[i*3]:=sum;
         p[i*3+1]:=sum;
         p[i*3+2]:=sum;
       end;
      12:// mix
       begin
         c:=p[i*3];
         p[i*3]:=p[i*3+1];
         p[i*3+1]:=p[i*3+2];
         p[i*3+2]:=c;
       end;
      13://smooth
       begin
       if ((j>0) and (j<(h-1))and (i>0)and (i<(w-1))) then begin
         p[i*3]:=round((p[(i-1)*3]+p[(i+1)*3]+p0[i*3]+p1[i*3]) /4);
         p[i*3+1]:=round((p[(i-1)*3+1]+p[(i+1)*3+1]+p0[i*3+1]+p1[i*3+1]) /4);
         p[i*3+2]:=round((p[(i-1)*3+2]+p[(i+1)*3+2]+p0[i*3+2]+p1[i*3+2]) / 4);
         end;
       end;
      end;
      end;
     end;

cm.canvas.Draw (0,0,tm);
cm.transparent:=true;
cm.transparentcolor:=clwhite;
bm.Canvas.draw(x-radius,y-radius,cm);
cm.free;
tm.free;
end;

A linked question which is helpful is this - implementing scan line of bitmap corectly. It suggests to cast the pointers to NativeInt. The OP changed his code after answers, making it difficult to correlate old code with new code. I understand that my problem is due to some hard coded sequential access of pointers but I am really beginner to make sense of scan line or pointers. If you help me port this, these components will continue to be useful to everyone.

update after comment from @Renate Schaaf: all the brush modes of the janDrawImage are working now, except for the below one. I was expecting a bigger problem but that didn't turn out to be the case. So modified the title of the question. @Renate Schaaf Can you please help fix the below one too. I tried but failed.

procedure TjanDrawImage.rimple(src,dst:tbitmap;amount:extended);
var ca,sa,a,dx,dy,r,rx,ry,sr,fr:extended;
    w,h,x,y,cx,cy,i,j,c,ci:NativeInt;
    p1,p2:pbytearray;
begin
w:=src.width;
h:=src.height;
cx:=w div 2;
cy:=h div 2;
if amount<1 then amount:=1;
fr:=cx/amount;
   for y:=0 to h-1 do begin
     p1:=src.ScanLine[y];
     for x:=0 to w-1 do begin
       dx:=x-cx;dy:=-(y-cx);
       r:=sqrt(sqr(dx)+sqr(dy));
       sr:=fr*sin(r/cx*amount*2*pi);
       if (r+sr<cx) and (r+sr>0) then begin
       a:=arctan2(dy,dx);
       sincos(a,sa,ca);
       i:=cx+round((r+sr)*ca);
       j:=cy+round((r+sr)*sa);
       p2:=dst.scanline[j];
       c:=x*3;ci:=i*3;
         p2[ci]:=p1[c];
         p2[ci+1]:=p1[c+1];
         p2[ci+2]:=p1[c+2];
       end;
      end;
     end;
end;
user30478
  • 347
  • 1
  • 3
  • 13
  • 1
    I wouldn't use that code. But at a first glance, it seems to me like it makes assumptions on the pixel format (specifically, it seems to assume it is 24 bpp). – Andreas Rejbrand Oct 16 '20 at 11:59
  • Yes, it uses pf24bit. I tried to update that to pf32bit everywhere but the same problem persisted. – user30478 Oct 16 '20 at 12:29
  • 1
    The culprit is here: if j<(h-1) then p1:=cm.scanline[j+1]; Use cm.height instead of h, which is the height of bm. For pf32bit you just replace the 3s by 4s, with a variable, of course. There's no need to do any typecasting when you change from win32 to win64, bytes stay bytes and it doesn't matter what size their address is. – Renate Schaaf Oct 16 '20 at 12:42
  • @Renate Schaaf, Apologies for extending the question. I was left with one more such scenario. – user30478 Oct 16 '20 at 13:55
  • I think you should post a new question then, as this would be better for people seeking answers. – Renate Schaaf Oct 16 '20 at 14:13
  • Your concern is right @RenateSchaaf but, the update is related to first solution given by you. So it made no sense to post as new question but as an addendum to this one. – user30478 Oct 16 '20 at 14:52

1 Answers1

1

When you do scanline operations, you always need to make sure that your pixel location is within the boundaries of your bitmap, particularly if you make geometric transformations.

So, in the last example you must clamp j to [0,h-1] and i to [0,w-1] using max(min(..)). Also, you should set the size and pixelformat of src and dst to the same at the beginning.

I didn't really bother to find out what this ripple is supposed to do, but when I run an example it doesn't look like it's doing whatever it does right. For geometric transformations you need to work backwards, running through the pixels of the destination and figure out which pixel of the source needs to go there. Otherwise you end up with a destination that has holes, like here.

Edit: Since I'm stuck with my project: I think this is the routine you really want to use. Note that I just switched the roles of src and dst and corrected some errors. It now adds a water ripple effect to the bitmap.

procedure rimple(src, dst: TBitmap; amount: extended);
var
  ca, sa, a, dx, dy, r, sr, fr: extended;
  w, h, x, y, cx, cy, i, j, c, ci: NativeInt;
  p1, p2: pbytearray;
  bits: integer;
begin
  Assert(src.PixelFormat in [pf24bit, pf32bit],
    'Device independent bitmap needed');
  dst.PixelFormat := src.PixelFormat;
  bits := 3;
  if src.PixelFormat = pf32bit then
    bits := 4;
  w := src.width;
  h := src.height;
  dst.SetSize(w, h);
  cx := w div 2;
  cy := h div 2;
  // in case somebody enters a negative amount
  if abs(amount) < 1 then
    amount := 1;
  fr := cx / amount;
  for y := 0 to h - 1 do
  begin
    // switched src and dst
    p1 := dst.scanline[y]; // src.scanline[y];
    for x := 0 to w - 1 do
    begin
      dx := x - cx;
      // Corrected from dy:=-(y-cx)
      dy := (y - cy);
      r := sqrt(sqr(dx) + sqr(dy));
      sr := fr * sin(r / cx * amount * 2 * pi);
      // Omitted the following check
      // if (r + sr < cx) and (r + sr > 0) then
      begin
        a := arctan2(dy, dx);
        sincos(a, sa, ca);
        i := max(min(cx + round((r + sr) * ca), w - 1), 0);
        j := max(min(cy + round((r + sr) * sa), h - 1), 0);
        // switched src and dst
        p2 := src.scanline[j];
        c := x * bits;
        ci := i * bits;
        p1[c] := p2[ci];
        p1[c + 1] := p2[ci + 1];
        p1[c + 2] := p2[ci + 2];
      end;
    end;
  end;
end;
Renate Schaaf
  • 295
  • 1
  • 3
  • 9
  • Followed the answer to clamp down the J index before the problematic scan line access statement like this: `if j>h-1 then j := h-1 else if j< 0 then j := 0;` just before `p2:=dst.scanline[j];` in the second procedure and it works fine. – user30478 Oct 16 '20 at 17:08