 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Jim Guest
|
Posted: Thu Apr 26, 2007 6:14 pm Post subject: AV in Graphics Routine |
|
|
Can someone who is more versed in graphics then I look at this and tell
me where I went wrong? I have been getting a large number of reports
on the last line shown
LongColor := PDWORD( PMask)^;
I am clearly not accounting for something in this routine.
Thanks,
Jim
exception class : EAccessViolation
exception message : Access violation at address 004F67CC in module
'UltraExplorer.exe'. Read of address 055E8E80.
**********************************************
procedure ConvertBitmapEx(Image32: TBitmap; var OutImage: TBitmap;
const BackGndColor: TColor);
var
I, N: Integer;
PImage, PTarget, PMask: PByte;
LongColor: DWORD;
SourceRed, SourceGreen, SourceBlue, BkGndRed, BkGndGreen, BkGndBlue,
RedTarget, GreenTarget, BlueTarget, Alpha: Byte;
Target, Mask: TBitmap;
PImageDelta, PBitmapDelta, PMaskDelta: Integer;
begin
// Algorithm only works for bitmaps with a height > 1 pixel, should
not be a limitation
// as it would then be a line!
if (Image32.PixelFormat = pf32Bit) and (Image32.Height > 1) then
begin
Target := TBitmap.Create;
Mask := TBitmap.Create;
try
Target.Width := Image32.Width;
Target.Height := Image32.Height;
Target.Assign(Image32);
Mask.Width := Image32.Width;
Mask.Height := Image32.Height;
Mask.Canvas.Brush.Color := BackGndColor;
Mask.Canvas.FillRect(Mask.Canvas.ClipRect);
// initialize the bitmaps
PImage := Image32.ScanLine[0];
PTarget := Target.ScanLine[0];
PMask := Mask.ScanLine[0];
// Calculate the number of bytes in a row to do the direct
calculation
// of the pixel index address, note for a "bottom up" storage
this will
// these will be negative numbers
PImageDelta := Integer( Image32.ScanLine[1]) - Integer(PImage);
PBitmapDelta := Integer( Target.ScanLine[1]) - Integer( PTarget);
PMaskDelta := Integer( Mask.ScanLine[1]) - Integer( PMask);
// If the Deltas are postive then it is a "top down" bitmap and
easier
// to calculate then next rows address, just add one
if PImageDelta > 0 then
PImageDelta := 1;
if PBitmapDelta > 0 then
PBitmapDelta := 1;
if PMaskDelta > 0 then
PMaskDelta := 1;
for I := 0 to Image32.Height - 1 do
begin
for N := 0 to Image32.Width -1 do
begin
// Pixel encoded:
// Source GetColorValues ; Profiled = ~24-30% of time
LongColor := PDWORD( PImage)^;
SourceBlue := LongColor and $000000FF;
SourceGreen := (LongColor and $0000FF00) shr 8;
SourceRed := (LongColor and $00FF0000) shr 16;
Alpha := (LongColor and $FF000000) shr 24;
Inc(PImage, 4);
// Mask GetColorValues ; Profiled = ~24-30% of time
LongColor := PDWORD( PMask)^; <<<<<<< AV Right Here.
......
--
www.mustangpeak.net |
|
| Back to top |
|
 |
David Ninnes Guest
|
Posted: Thu Apr 26, 2007 6:28 pm Post subject: Re: AV in Graphics Routine |
|
|
Jim wrote:
| Quote: |
// Mask GetColorValues ; Profiled = ~24-30% of time
LongColor := PDWORD( PMask)^; <<<<<<< AV Right Here.
......
Target := TBitmap.Create; |
Mask := TBitmap.Create;
These create bitmaps in device format Bitmap.pixelformat := pfdevice;
If so the bitmap is in the screen pixel depth, which may not be 32 bit
and your pointer's overrunning the bitmap, should use
Target.pixelformat := pf32bit;
Mask.pixelformat := pf32bit;
hth,
Dave |
|
| Back to top |
|
 |
Anders Isaksson Guest
|
Posted: Thu Apr 26, 2007 10:52 pm Post subject: Re: AV in Graphics Routine |
|
|
"Jim" <jimdk (AT) mindspring00 (DOT) com> skrev i meddelandet
news:4630a5bd$1 (AT) newsgroups (DOT) borland.com...
| Quote: | // Calculate the number of bytes in a row to do the direct
calculation
// of the pixel index address, note for a "bottom up" storage
this will
// these will be negative numbers
PImageDelta := Integer( Image32.ScanLine[1]) - Integer(PImage);
PBitmapDelta := Integer( Target.ScanLine[1]) - Integer( PTarget);
PMaskDelta := Integer( Mask.ScanLine[1]) - Integer( PMask);
// If the Deltas are postive then it is a "top down" bitmap and
easier
// to calculate then next rows address, just add one
if PImageDelta > 0 then
PImageDelta := 1;
if PBitmapDelta > 0 then
PBitmapDelta := 1;
if PMaskDelta > 0 then
PMaskDelta := 1;
|
As you don't show the incrementing parts of your loops, this might not be a
problem, but the above code looks very suspicious to me! For a bottom-up
image, you calculate a Delta that is the number of bytes to add to get to
the next scan line, for a top-down image you set the Delta to 1 byte!?
As the Delta is a signed value, calculated as the difference ScanLine[1] -
ScanLine[0] it will work equally well for both kinds of storage.
--
Anders Isaksson, Sweden
BlockCAD: http://web.telia.com/~u16122508/proglego.htm
Gallery: http://web.telia.com/~u16122508/gallery/index.htm |
|
| Back to top |
|
 |
Jim Guest
|
Posted: Fri Apr 27, 2007 6:46 am Post subject: Re: AV in Graphics Routine |
|
|
| Quote: | As you don't show the incrementing parts of your loops, this might
not be a problem, but the above code looks very suspicious to me! For
a bottom-up image, you calculate a Delta that is the number of bytes
to add to get to the next scan line, for a top-down image you set the
Delta to 1 byte!?
|
Trust me I am not proud of these and to be honest I don't recall all
the details of why I did what. Here is the entire thing.
Jim
procedure ConvertBitmapEx(Image32: TBitmap; var OutImage: TBitmap;
const BackGndColor: TColor);
var
I, N: Integer;
PImage, PTarget, PMask: PByte;
LongColor: DWORD;
SourceRed, SourceGreen, SourceBlue, BkGndRed, BkGndGreen, BkGndBlue,
RedTarget, GreenTarget, BlueTarget, Alpha: Byte;
Target, Mask: TBitmap;
PImageDelta, PBitmapDelta, PMaskDelta: Integer;
begin
// Algorithm only works for bitmaps with a height > 1 pixel, should
not be a limitation
// as it would then be a line!
if (Image32.PixelFormat = pf32Bit) and (Image32.Height > 1) then
begin
Target := TBitmap.Create;
Mask := TBitmap.Create;
try
Target.Width := Image32.Width;
Target.Height := Image32.Height;
Target.Assign(Image32);
Mask.Width := Image32.Width;
Mask.Height := Image32.Height;
Mask.Canvas.Brush.Color := BackGndColor;
Mask.Canvas.FillRect(Mask.Canvas.ClipRect);
// initialize the bitmaps
PImage := Image32.ScanLine[0];
PTarget := Target.ScanLine[0];
PMask := Mask.ScanLine[0];
// Calculate the number of bytes in a row to do the direct
calculation
// of the pixel index address, note for a "bottom up" storage
this will
// these will be negative numbers
PImageDelta := Integer( Image32.ScanLine[1]) - Integer(PImage);
PBitmapDelta := Integer( Target.ScanLine[1]) - Integer( PTarget);
PMaskDelta := Integer( Mask.ScanLine[1]) - Integer( PMask);
// If the Deltas are postive then it is a "top down" bitmap and
easier
// to calculate then next rows address, just add one
if PImageDelta > 0 then
PImageDelta := 1;
if PBitmapDelta > 0 then
PBitmapDelta := 1;
if PMaskDelta > 0 then
PMaskDelta := 1;
for I := 0 to Image32.Height - 1 do
begin
for N := 0 to Image32.Width -1 do
begin
// Pixel encoded:
// Source GetColorValues ; Profiled = ~24-30% of time
LongColor := PDWORD( PImage)^;
SourceBlue := LongColor and $000000FF;
SourceGreen := (LongColor and $0000FF00) shr 8;
SourceRed := (LongColor and $00FF0000) shr 16;
Alpha := (LongColor and $FF000000) shr 24;
Inc(PImage, 4);
// Mask GetColorValues ; Profiled = ~24-30% of time
LongColor := PDWORD( PMask)^;
BkGndBlue := LongColor and $000000FF;
BkGndGreen := (LongColor and $0000FF00) shr 8;
BkGndRed := (LongColor and $00FF0000) shr 16;
Inc(PMask, 4);
if Alpha < High(Byte) then
begin
// displayColor = sourceColor?alpha / 255 +
backgroundColor?(255 - alpha) / 255
// Profiled = ~15-24% of time
RedTarget := SourceRed*Alpha shr 8 + BkGndRed*(255-Alpha)
shr 8;
GreenTarget := SourceGreen*Alpha shr 8 +
BkGndGreen*(255-Alpha) shr 8;
BlueTarget := SourceBlue*Alpha shr 8 +
BkGndBlue*(255-Alpha) shr 8;
end else
begin
// skip non-blended pixels
RedTarget := SourceRed;
GreenTarget := SourceGreen;
BlueTarget := SourceBlue;
end;
// Create the RGB DWORD color ; Profiled = ~8%-9%% of time
// Mask out all but the alpha channel then build the
backwards stored RGB preserving the alpha channel bits
PDWORD(PTarget)^ := ((BlueTarget) or (GreenTarget shl 8)or
(RedTarget shl 16));
Inc(PTarget, 4);
end;
Inc(PImage, PImageDelta*2);
Inc(PMask, PMaskDelta*2);
Inc(PTarget, PBitmapDelta*2);
end;
OutImage.Assign(Target);
finally
FreeAndNil(Target);
FreeAndNil(Mask);
end;
end else
OutImage.Assign(Image32)
end;
--
www.mustangpeak.net |
|
| Back to top |
|
 |
Anders Isaksson Guest
|
Posted: Fri Apr 27, 2007 8:00 pm Post subject: Re: AV in Graphics Routine |
|
|
Jim wrote [slightly edited]:
| Quote: | PImageDelta := Integer( Image32.ScanLine[1]) - Integer(PImage);
PBitmapDelta := Integer( Target.ScanLine[1]) - Integer( PTarget);
PMaskDelta := Integer( Mask.ScanLine[1]) - Integer( PMask);
if PImageDelta > 0 then
PImageDelta := 1;
if PBitmapDelta > 0 then
PBitmapDelta := 1;
if PMaskDelta > 0 then
PMaskDelta := 1;
for I := 0 to Image32.Height - 1 do
begin
for N := 0 to Image32.Width -1 do
begin
work
Inc(PImage, 4);
Inc(PMask, 4);
Inc(PTarget, 4);
end;
Inc(PImage, PImageDelta*2);
Inc(PMask, PMaskDelta*2);
Inc(PTarget, PBitmapDelta*2);
end;
|
So, for bottom-up, you do one line pixelwise forward, and then a double line
leap backwards, seems OK.
But for top-down you do one line pixelwise forward, then skip half a pixel
and think you are at the beginning of the next line... I'd assume the Deltas
should be zero for top-down, as you have Inc'ed your way through the whole
line and are already standing at the beginning of the next.
For both approaches, you have also made the assumption that the memory
layout doesn't contain any non-pixel filler bytes. I'm not sure that's a
safe assumption.
I would have coded it something like (newsreader code, no promises):
LineDelta := ScanLine[1] - ScanLine[0];
if (LineDelta > 0) then PixelDelta := SizeOf(TRGBQuad) else PixelDelta
:= -SizeOf(TRGBQuad);
PLine := ScanLine[0];
for I := 0 to Height - 1 do
begin
PPixel := PLine;
for N := 0 to Width - 1 do
begin
< work with PPixel^ >
Inc(PPixel, PixelDelta);
end;
Inc(PLine, LineDelta);
end;
Also note that if the pointers are instead declared as pointers to the
actual data type, you don't have to fiddle with SizeOf(TRGBQuad), the value
would have been +-1 instead. But the pointer arithmetic lines would need
some more typecasts.
There's also (I believe) a problem zone where the image begins at an address
< PositiveMaxInt and extends into the negative range of an Integer (or vice
versa). Unsigned integers, or longint, would probably be better.
Another beware for the future: Integer will not be able to contain a Pointer
when Delphi for Win64 finally arrives, AFAIK.
--
Anders Isaksson, Sweden
BlockCAD: http://web.telia.com/~u16122508/proglego.htm
Gallery: http://web.telia.com/~u16122508/gallery/index.htm |
|
| Back to top |
|
 |
Jim Guest
|
Posted: Fri Apr 27, 2007 10:55 pm Post subject: Re: AV in Graphics Routine |
|
|
| Quote: | For both approaches, you have also made the assumption that the
memory layout doesn't contain any non-pixel filler bytes. I'm not
sure that's a safe assumption.
I would have coded it something like (newsreader code, no promises):
|
Thanks, much clearer but....
I uploaded a demo to the attachements. If I set the first loop to what
is should be
for I := 0 to Image32.Height - 1 do
it AV when access the last (top row). If I do this:
for I := 0 to Image32.Height - 2 do
Then it works... but it does not run the top line. You can't see the
effect in the demo but in my real app there is a black line across the
top where the algrothim does not touch those pixels.
If you could take a quick look I would appreciate it.
Jim
--
www.mustangpeak.net |
|
| Back to top |
|
 |
Anders Isaksson Guest
|
Posted: Fri Apr 27, 2007 11:16 pm Post subject: Re: AV in Graphics Routine |
|
|
Jim wrote:
| Quote: | If you could take a quick look I would appreciate it.
|
I haven't taken a look yet, but have you given thought to the other thing I
mentioned in the previous post:
| Quote: |
But for top-down you do one line pixelwise forward, then skip half a pixel |
and think you are at the beginning of the next line... I'd assume the Deltas
should be zero for top-down, as you have Inc'ed your way through the whole
line and are already standing at the beginning of the next.
<<<
If I'm understanding your code right, it will Inc the pointers a bit too far
for each line, meaning that it will AV if you try to go through them all (as
it seems you go a bit farther), while it would still 'work' if you skip
enough lines.
Why not test it on a bitmap that is only 1 pixel wide, and with very
distinct color variations for each line?
--
Anders Isaksson, Sweden
BlockCAD: http://web.telia.com/~u16122508/proglego.htm
Gallery: http://web.telia.com/~u16122508/gallery/index.htm |
|
| Back to top |
|
 |
Anders Isaksson Guest
|
Posted: Fri Apr 27, 2007 11:57 pm Post subject: Re: AV in Graphics Routine |
|
|
Jim wrote:
| Quote: | If you could take a quick look I would appreciate it.
|
OK, now I have taken a look. Jim, we are both a little stupid :-)
The [newsreader] code I wrote places the line pointer at the *beginning* of
each line, irrespective of the direction of Delta.
Of course, if PixelDelta is negative, we should start at the *end* of the
line , or (the easier way) we should always go *forward* inside the line -
PixelDelta is always = SizeOf(TRGBQuad). Save a bit on the if:s too :-)
After changing this, it works as expected (at least in D5).
As a side note, I tried to compile with OVERFLOW and RANGE on, but then I
get exceptions on the pointer arthmetic lines. Sometimes I feel like having
these tests on, as they *can* show me problems with the code. To make that
possible here, you'd have to go through some more type casting, probably up
to a longer integer type which would take more time, so I'm not sure which
way I'd go...
--
Anders Isaksson, Sweden
BlockCAD: http://web.telia.com/~u16122508/proglego.htm
Gallery: http://web.telia.com/~u16122508/gallery/index.htm |
|
| Back to top |
|
 |
Anders Isaksson Guest
|
|
| Back to top |
|
 |
Anders Isaksson Guest
|
Posted: Sat Apr 28, 2007 12:25 am Post subject: Re: AV in Graphics Routine |
|
|
Anders Isaksson wrote:
| Quote: | GroupBoxDest.DoubleBuffered := True;
|
GroupBoxDest.ControlStyle := GroupBoxDest.ControlStyle + [csOpaque];
seems to give as good results, but takes less time. That assumes that the
control is redrawing the full canvas though (as this example project is
doing).
--
Anders Isaksson, Sweden
BlockCAD: http://web.telia.com/~u16122508/proglego.htm
Gallery: http://web.telia.com/~u16122508/gallery/index.htm |
|
| Back to top |
|
 |
Jim Guest
|
Posted: Sat Apr 28, 2007 2:08 am Post subject: Re: AV in Graphics Routine |
|
|
| Quote: | OK, now I have taken a look. Jim, we are both a little stupid :-)
The [newsreader] code I wrote places the line pointer at the
beginning of each line, irrespective of the direction of Delta.
|
Uh..... stepped through that at least 50 times and did not even think
about that...... Thanks.
Jim
--
www.mustangpeak.net |
|
| Back to top |
|
 |
Jim Guest
|
Posted: Sat Apr 28, 2007 2:11 am Post subject: Re: AV in Graphics Routine |
|
|
| Quote: | GroupBoxDest.ControlStyle := GroupBoxDest.ControlStyle + [csOpaque];
seems to give as good results, but takes less time. That assumes that
the control is redrawing the full canvas though (as this example
project is doing).
|
This example is not being used for anything. Someone wanted me to do
that type of blending for the images in my EasyListview component so
all that is was a test bed they developed. I did a bit of profiling
and got it about 300% faster with that code. All I use out of it is
the graphics routines in my common library.
Thanks for all your help.
Jim
--
www.mustangpeak.net |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|