1

I have a game program that requires the user to select from 50+ images to place on a 9x9 game board grid with a timage in each position. For the user to place the images, I am providing a tstringgrid which displays various images from a timagelist. The actual images are graphic symbols created in .png format with transparent regions to allow the background color of the image's parent to show through when displayed. The image selected from the tstringgrid displays correctly on the game board timage components, but not in the 'tstringgrid'. The tstringgrid displays an image's transparent areas as black which is unsightly and makes many of the symbols unreadable.

I have used the following code to load the tstringgrid:

procedure TImageForm.FormCreate(Sender: TObject);
var
  r, c, n : Integer;
  img:TImage;
begin
  //assign a value to each cell to connect with imagelist.
  //see StringGrid1DrawCell
  img := timage.Create(nil);
  try
    n := -1;
    with StringGrid1 do begin
      for r := 0 to RowCount - 1 do begin
        for c:= 0 to ColCount - 1 do  begin
          inc(n);
          Cells[c,r] := IntToStr(n);
          ImageList1.GetBitmap(n, img.Picture.Bitmap);
//          ImageList1.AddMasked(Img.Picture.Bitmap, clBlack);
        end;
      end;
    end;
  finally
    img.Free;
  end;
end;

What I need to do is revise the bitmap retrieved from the list before it is displayed.

I am attempting to do this as follows:

procedure TForm1.FillBkgd (bmp : tbitmap;clr : tcolor);
//which is the imagelist index for the image
var
  bmp1  : tbitmap;

begin
  bmp1 := tbitmap.create;

  try
     bmp1.Width := 50;
     bmp1.Height := 50;

     with Bmp1.Canvas do begin
      Brush.Color := clr;  //stringgrid1.color;
      Brush.Style := bsSolid;
      FillRect(rect(0,0,50, 50));
     end;

     bmp1.Canvas.Draw(0,0, bmp);
     bmp:= bmp1;
  finally
    bmp1.free;

  end;
end;

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
{https://www.youtube.com/watch?v=LOEP312NzsE}
var
  bmp : tbitmap;
  s : string;
  aCanvas: TCanvas;
  n : integer;
begin
  inherited;

  if not Assigned (Imagelist1) then   //or (Arow = 0) or (aCol in [0,5])
    exit;
  bmp := tbitmap.create;
  try
    s := (Sender as TStringGrid).Cells [aCol, ARow];
    // Draw ImageX.Picture.Bitmap in all Rows in Col 1
    aCanvas := (Sender as TStringGrid).Canvas;
    // Clear current cell rect
    aCanvas.FillRect(Rect);
    // Draw the image in the cell
    n := strtoInt (s);
    ImageList1.GetBitmap(n, Bmp);

    FillBkgd (bmp,clRed); //stringgrid.color

    aCanvas.StretchDraw(Rect,bmp);

  finally

  end;
end;

My attempts to combine the symbol image with a colored background before placing it in the stringgrid have failed. It is unclear to me whether I am failing to create a solid colored bitmap or am not successfully joining the image to the background.

Ashlar
  • 636
  • 1
  • 10
  • 25
  • Why are you using TStringGrid (which as it's name suggests is designed to display strings) instead of TDrawGrid? – Ken White Apr 25 '20 at 00:58
  • @KenWhite I used `tstringgrid` because I was familiar with it. I will check out `tDrawgrid`, but won't I have the same problem joining the images with a background in the component canvas? – Ashlar Apr 25 '20 at 01:08
  • I don't know. What specific Delphi version are you using? Did you try setting the bitmap's `PixelFormat` to `pf32bit` before drawing to it? The specific reason *not* to use the `TStringGrid` is that it creates `TStringList`s in the background for rows and columns that aren't necessary if you're not using them, which is a waste of resources. – Ken White Apr 25 '20 at 01:21
  • @KenWhite I am using 10.3 community. pf32bit has no affect – Ashlar Apr 25 '20 at 02:26
  • Definitely use `TDrawGrid` instead of `TStringGrid`. Or even `TPaintBox`. But either way, you should use `TImageList.Draw()` to draw the stored images directly on a `Canvas`, instead of using `TImageList.GetBitmap()` + `Canvas.Draw()`. Otherwise, look at putting `TImage` controls in a `TGridPanel` and let the VCL handle all of the drawing for you. – Remy Lebeau Apr 25 '20 at 03:12

1 Answers1

3

Function FillBkgd has big problems with the quality of the code.

     bmp:= bmp1;
  finally
    bmp1.free;    
  end;

All object variables are pointers. bmp and bmp1 objects point to one area of ​​memory that you are freeing. This leads to Access Violation. You are lucky that the pointer is not returned from the function. Function FillBkgd does not work. To get the result, you could use bmp.Assign(bmp1);.

I see a lot of redrawing the picture of ImageList (draw to Bmp, draw to Bmp1, draw ACanvas). After the first transformation transparency information is lost. Therefore at this moment it is necessary to change the background color.

s := (Sender as TStringGrid).Cells [aCol, ARow];
// Draw ImageX.Picture.Bitmap in all Rows in Col 1
aCanvas := (Sender as TStringGrid).Canvas;
// Clear current cell rect
aCanvas.FillRect(Rect);
// Draw the image in the cell
n := strtoInt (s);

//new lines
bmp.Canvas.Brush.Color := clRed;
bmp.Canvas.FillRect(TRect.Create(0, 0, ImageList1.Width, ImageList1.Height));
//new lines
ImageList1.GetBitmap(n, Bmp);

//deleted lines
//FillBkgd (bmp,clRed); //stringgrid.color

aCanvas.StretchDraw(Rect,bmp);

And do not forget to fill out finally to free bmp.

Alex R.
  • 266
  • 2
  • 9
  • Your corrections successfully worked for a bitmap image in the `imagelist` with a transparency color set to the background. The result is problematic since the background if the images varies as the pixels get close to the actual symbol resulting in a poor rendering. – Ashlar Apr 25 '20 at 18:11
  • If the image in the `imagelist` was originally a .png with transparent pixels, the image is still rendered as black and the background color is not displayed. – Ashlar Apr 25 '20 at 18:13
  • " I see a lot of redrawing...After the first transformation information is lost." I'm not sure I understand, could you clarify which step in the code does that occur? – Ashlar Apr 25 '20 at 18:16
  • I checked again with `.png`, it works for me. Background is red. – Alex R. Apr 25 '20 at 18:28
  • The best way would be if you draw a slice on a `TStringGrid`. `ImageList1.Draw(aCanvas, 0, 0, n);`Maybe you will resize the png-images before placing them on the `TImageList`. E.g. https://stackoverflow.com/questions/2437714/resize-png-image – Alex R. Apr 25 '20 at 18:34