This problem appears in Delphi 3 and 4, but appears to be fixed with the Delphi 4 Update Pack 2 (Build 5.104).  Thanks to Marko Peric for informing me that Delphi 4 Update Pack 2 fixes this problem. [efg, 14 Dec 98] ============================================================================ [Submitted to Inprise 10/16/98. Inprise Bug Reference Number 23532.] Thread: Scanline Enigma. Bug or something simple? (edited for clarity) Author: Earl F. Glynn Date:1998/10/14 Forums:borland.public.delphi.graphics, alt.lang.delphi, borland.public.cppbuilder.graphics, comp.lang.pascal.delphi.misc From time-to-time I get an access violation while working with pf24bit scanlines. I normally use Method A (see below) to assign TRGBTriples in a new bitmap, but when I use Method D (which is the simplest to code) I get an access violation every time. Two other less desirable Methods (B and C) also work. Today I was working on code that I only need Method D, so I was bugged that I couldn't use this simple approach. Am I missing something simple here? Thanks in advance for any help. ------------------------------------------------------------------- CONST MaxPixelCount = 65536; TYPE // For pf24bit Scanlines pRGBTripleArray = ^TRGBTripleArray; TRGBTripleArray = ARRAY[0..MaxPixelCount-1] OF TRGBTriple; // Used by "Method C" below FUNCTION RGBtoRGBTriple(CONST red, green, blue: BYTE): TRGBTriple; BEGIN WITH RESULT DO BEGIN rgbtRed := red; rgbtGreen := green; rgbtBlue := blue END END {RGBTriple}; // Simplified version of code having problem. // This fragment just creates a BitmapB that is a copy of BitmapA PROCEDURE TFormAcetoDiff.ImageUpdate; VAR rowInB : pRGBTripleArray; rowInA : pRGBTripleArray; BitmapA : TBitmap; BitmapB : TBitmap; i,j : INTEGER; RGBTriple: TRGBTriple; BEGIN // Ignore usual TRY .. FINALLY block to show problem here BitmapA := TBitmap.Create; BitmapA.LoadFromFile('N:\Images\Marlow\SeqDO2B\Align.BMP'); ASSERT (BitmapA.PixelFormat = pf24Bit); BitmapB := TBitmap.Create; TRY BitmapB.Width := BitmapA.Width; BitmapB.Height := BitmapA.Height; BitmapB.PixelFormat := pf24bit; FOR j := 0 TO BitmapA.Height-1 DO BEGIN RowInA := BitmapA.ScanLine[j]; RowInB := BitmapB.Scanline[j]; FOR i := 0 TO BitmapA.Width-1 DO BEGIN // Method A -- Always works // RowInB[i].rgbtRed := RowInA[i].rgbtRed; // RowInB[i].rgbtGreen := RowInA[i].rgbtGreen; // RowInB[i].rgbtBlue := RowInA[i].rgbtBlue; // Method B -- Always works // RGBTriple := RowInA[i]; // RowInB[i] := RGBTriple // Method C -- Always works // RowInB[i] := RGBtoRGBTriple(RowInA[i].rgbtRed, // RowInA[i].rgbtGreen, // RowinA[i].rgbtBlue); // Method D -- Why does this fail with an access violation? // In debugger, RowInA is $8352B880 and RowInB is $83657880, i = 0 // Why does access violation occur at 0042DABF? RowInB[i] := RowInA[i]; END END; ImageFalseColor.Picture.Graphic := BitmapB FINALLY BitmapB.Free END END {ImageUpdate}; efg =================================================================== From: Earl F. Glynn Subject: Re: Scanline Enigma. Bug or something simple? Date: Thursday, October 15, 1998 11:32 AM Earl F. Glynn wrote in message <70376b$knc@bgtnsc01.worldnet.att.net>... >From time-to-time I get an access violation while working with >pf24bit scanlines Why does this work (Method E)? RowInB[i] := RGBTripleKludge( RowInA[i] ); but this doesn't (gives access violation): RowInB[i] := RowInA[i]; where FUNCTION RGBTripleKludge(CONST RGBTriple: TRGBTriple): TRGBTriple; BEGIN RESULT := RGBTriple END {RGBTripleKludge}; efg =================================================================== Re: Scanline Enigma. Bug or something simple? Author: ullrich Email:ullrich@math.okstate.edu Date:1998/10/15 Well, it looks fairly enigmatic to me too (yes, I get the same AV). The fact that Method B works seems to rule out the idea that it's some obscure assignment-compatibility thing. Oh: the AV goes away if I turn off optimization. So it's a bug. David C. Ullrich =================================================================== Re: Scanline Enigma. Bug or something simple? more options Author: Earl F. Glynn Thanks MUCH David. Your suggestion worked fine. So, my new question is No Optimization, or Kludge That I Can Live With? I don't think I've ever turned off optimization. I was surprised to learn that you can only turn optimization on or off for the whole module -- I tried to turn it off for the one statement, or the nested loops, but that didn't work. [See Stefan Hoffmeister's post below -- optimization is on a per method/function basis.] So I ran a short timing test copying a 640-by-480 pf24bit bitmap like in the original post. I did this for Methods A, B, C, D and E both With and Without Compiler Optimization. Here are the results (mean +/- s.d.) for a 166 MHz Pentium With and Without Compiler Optimization: Method A: With 151 +/- 7 ms, Without 380 +/- 50 Method B: With 104 +/- 7 ms, Without 163 +/- 12 Method C: With 193 +/- 22ms, Without 260 +/- 28 Method D: With Access Violation, Without 137 +/- 1 Method E: With 140 +/- 28, Without 244 +/- 129 (This would have looked better in an HTML table, but I didn't want to risk the wrath of those that don't like HTML in posts.) So, Method B With Optimization seems the way to go. In summary: VAR rowInB : pRGBTripleArray; rowInA : pRGBTripleArray; i : INTEGER; AvoidOptimizationBug: TRGBTriple; ... // Optimization Bug: RowInB[i] := RowInA[i]; AvoidOptimizationBug := RowInA[i]; // Kludge workaround RowInB[i] := AvoidOptimizationBug; ... I've worked around this "bug" for months, but yesterday I decided I had to figure out why it happened occasionally. Maybe in Delphi 5 Method D will work with optimization. THANKS AGAIN, Dave. efg =================================================================== Re: Scanline Enigma. Bug or something simple? more options Author: Stefan Hoffmeister Date:1998/10/15 >I don't think I've ever turned off optimization. I was surprised to learn that >you can only turn optimization on or off for the whole module -- I tried to >turn it off for the one statement, or the nested loops, but that didn't work. FYI, it works on a per-method/function basis. No need to twiddle with the whole module. -- Stefan Hoffmeister (http://www.econos.de/) =================================================================== Re: Scanline Enigma. Bug or something simple? Author: Tim Roberts Date:1998/10/15 "Earl F. Glynn" wrote: >Am I missing something simple here? Nope, I don't think so. >// Method D -- Why does this fail with an access violation? >// In debugger, RowInA is $8352B880 and RowInB is $83657880, i = 0 >// Why does access violation occur at 0042DABF? > > RowInB[i] := RowInA[i]; It is a compiler optimization bug, pure and simple. I'm using Delphi 3.01; I don't know if it would still occur with Delphi 4. Let's look at the code generated for this last statement. Upon entry, eax has the current value of "i", [esp] has the address of RowInB, and esi has the address of RowInA. lea ebx, [eax+eax*2] Now ebx has 3*i; this is the byte offset of the current pixel within the two scanlines. mov ecx, [esp] Now ecx has the address of RowInB. lea ebx, [ecx+ebx] Now ebx has the address of the current byte within RowInB mov cx, [esi+ebx] HERE is the bug. The compiler is now confused over what is in ebx. in THIS statement, he thinks it has the byte offset value (3*i), which would be a small number. Instead, it has an address, within RowInB. Adding the address of RowInB to the address of RowInA results in the access violation. mov [ebx], cx Here he's trying to use the RowInB pointer it computed in the second lea. mov cl, [esi+ebx+2] mov [ebx+2], cl I have encountered this same problem once before. It also involved manipulation two parallel pointers, and the compiler tried to store two things in one register. I blame Delphi's strange but very strong tendency to always do loops by counting down. In this case, it is keeping both a down counter in edx and an up counter in eax. That is just silly. There is no net gain to what this loop does ("inc eax/dec edx/jnz") over a strictly up-counting loop ("inc eax/cmp eax,[end-value]/jl"), and it uses up one of the very precious x86 registers unnecessarily. --- Why don't you use the "Move" procedure for this? It'd be much quicker than a pixel-by-pixel loop: Move( BitmapA.Scanline[j], BitmapB.Scanline[j], BitmapA.Width * 3 ); - Tim Roberts, timr@probo.com Providenza & Boekelheide, Inc. =================================================================== Re: Scanline Enigma. Bug or something simple? more options Author: Earl F. Glynn Date:1998/10/15 Tim Roberts wrote in message <362861b0.19797937@news.teleport.com>... >"Earl F. Glynn" wrote: >Why don't you use the "Move" procedure for this? It'd be much quicker than >a pixel-by-pixel loop: I just simplified the example for my original post. What I am doing is conditionally assigning false color to some pixels based on the attributes of corresponding pixels in another image. There is an IF that must be executed for each and every pixel, so the Move won't work in this case. While a majority of the pixels are copied, many are not. Thanks for your analysis of what's going on at the assembly level. I started looking at this in D4 and that would take too long (for me). Yes, the problem seems to occur in D3 and D4. Thanks again for your comments. efg