Working (resizing, padding, splitting) with 16 bit png and 8bit png images? lazarus / 39-Graphics ---------------------------------------------------------------------- FiftyTifty · 2023-01-08 11:54 I've been pulling my hair out with trying to get a tool made to work with 16 bit greyscale png, as well as 24 bit colour png. I've given up on doing it with Python, as the libraries that work with 16bit png files are all sorts of wonky and have incorrect documentation. After doing some research, it seems that only Vampyre Imaging Library works with 16 bit greyscale png files, so I'm using that for now. If there's a better one, let me know. What I have to do, is take a 4096x4096 image (image divisible by 256), and pad out the bottom & right sides of the image with black until it is 4112x4112 (divisible by 257). Then, split the image into 257x257 images by x&y coordinates. How do I pad the image from being 4096x4096, into 4112x4112? My main idea was to create two images, fill them with black, and append them to the original. Don't see a way to do that though. Here is my current code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TformWindow.buttonHeightmapClick(Sender: TObject);var   imageCurrent, imageResized, imageFillX, imageFillY: TSingleImage;   iSizeX, iSizeY, iAddX, iAddY: Integer;   strImageToSave: string;   infoCurrent: TImageFormatInfo;begin   if dialogOpen.Execute then begin     imageCurrent := TSingleImage.CreateFromFile(dialogOpen.FileName);    strImageToSave := ExtractFileNameWithoutExt(dialogOpen.FileName);     imageResized := TSingleImage.CreateFromImage(imageCurrent);    iSizeX := imageCurrent.Height;    iSizeY := imageCurrent.Width;     GetImageFormatInfo(imageCurrent.Format, infoCurrent);     //We want: 'png' = DetermineFileFormat(dialogOpen.FileName)    //16 bit png: infoCurrent.BytesPerPixel = 2;    //Greyscale 1 ChannelAlpha: infoCurrent.ChannelCount = 1;     ShowMessage(DetermineFileFormat(dialogOpen.FileName) + ' '      + inttostr(infoCurrent.BytesPerPixel * 8) + 'bit '      + inttostr(infoCurrent.ChannelCount) + ' channel'    );     iAddX := 257 - (iSizeX mod 257);    iAddY := 257 - (iSizeY mod 257);     imageFillX := TSingleImage.CreateFromParams(257, iAddY, imageCurrent.Format);    imageFillY := TSingleImage.CreateFromParams(iAddX, 1, imageCurrent.Format);     //Somehow pad out imageCurrent until it is 4112x4112   end;  end; ---------------------------------------------------------------------- circular · 2023-01-08 14:21 Hello FiftyTifty, You could do that thing you want to do by creating an image for each target square of the grid and put the content of the source image in it with the appropriate offset. I don't know how to do it with Vampyre Imaging Library, though I can tell you how to do it with BGRABitmap. It provides a TExpandedBitmap class that has 16bit per channel. It is not grayscale but RGBA, so it will used 4x more memory. But it can read and write 16-bit ("word- sized") grayscale PNG images. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses ExpandedBitmap, BGRABitmapTypes, BGRAWritePNG; procedure GetSquares(ASourceFile: string; ADestFilePrefix: string; ATileSize: integer = 257);var  source, dest: TExpandedBitmap;  ofsX, ofsY, nbX, nbY, ix, iy: integer;  writer: TBGRAWriterPNG;begin  source := TExpandedBitmap.Create;  source.LoadFromFile(ASourceFile);  nbX := (source.Width+ATileSize-1) div ATileSize;  nbY := (source.Width+ATileSize-1) div ATileSize;  ofsX := (nbX*ATileSize - source.Width) div 2;  ofsY := (nbY*ATileSize - source.Height) div 2;  writer := TBGRAWriterPNG.Create;  writer.GrayScale := true;  writer.WordSized := true;  writer.UseAlpha := false;  for iy := 0 to nbY-1 do  for ix := 0 to nbX-1 do  begin    dest := TExpandedBitmap.Create(ATileSize, ATileSize, BGRABlack);    dest.PutImage(ofsX - ix*ATileSize, ofsY - iy*ATileSize, source, dmSet);    dest.SaveToFile(ChangeFileExt(ADestFilePrefix, '') +      IntToStr(ix) + '-' + IntToStr(iy) + '.png', writer);    dest.Free;  end;  writer.Free;  source.Free;end; Regards ---------------------------------------------------------------------- marcov · 2023-01-08 14:31 Afaik fcl-image that comes with FPC can do 16-bit grayscale ? If you get stuck somewhere with fcl-image, please talk about it here, or file a bug. ---------------------------------------------------------------------- FiftyTifty · 2023-01-08 19:38 Quote from: marcov on January 08, 2023, 03:31:29 pmAfaik fcl-image that comes with FPC can do 16-bit grayscale ? If you get stuck somewhere with fcl-image, please talk about it here, or file a bug. Thanks for the info you two. I'll try with FCLImage first, as it appears to have the widest file format support. And actual examples on using it. The first issue I've found is pen.FPColor for setting the colour of the black rectangle. There is no example in the wiki of setting a colour. How do we do that? In this case, I want it to be pure black (0). Here's the code so far: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   if dialogOpen.Execute then begin     imageCurrent := TFPMemoryImage.LoadFromFile(dialogOpen.FileName);    canvasCurrent := TFPImageCanvas.create(imageCurrent);    writerCurrent := TFPWriterPNG.Create;     //Get size of the heightmap    iSizeX := imageCurrent.Height;    iSizeY := imageCurrent.Width;     //Get number of pixels needed to make image divisible by 257    iAddX := 257 - (iSizeX mod 257);    iAddY := 257 - (iSizeY mod 257);     //Set canvas to the size we want    canvasCurrent.Width := iSizeX + iAddX;    canvasCurrent.Height := iSizeY + iAddY;     //Fill added rows with black    with canvas do begin       pen.mode := pmBlack;      pen.style := psSolid;      pen.width := 1;      pen.FPColor := ;     end;     canvasCurrent.FillRect(iSizeX + 1, iSizeY + 1,                         canvasCurrent.Width, canvasCurrent.Height                         );   end; Right, did some more work. Now I got it to output an image, but it's a total mess. Mostly black with a couple strips of the source image coming through. I'll attach the project and the heightmap I'm using. Here's the resulting heightmap: (https://i.imgur.com/ydZO386.png) Project: https://mega.nz/file/XwNCmayZ#0MuwF4X- t75jXlccRxVL5N3ZCqKn984R3yr0HON1uf0 Code: Code: [Select]procedure TformWindow.buttonHeightmapClick(Sender: TObject); var    imageCurrent: TFPCustomImage;    canvasCurrent: TFPCustomCanvas;    readerCurrent: TFPCustomImageReader;    writerCurrent: TFPCustomImageWriter;    iSizeX, iSizeY, iAddX, iAddY: Integer;    strImageCurrent, strImageToSave: string; begin   if dialogOpen.Execute then begin     strImageCurrent := dialogOpen.FileName;     imageCurrent := TFPMemoryImage.Create(0, 0);     imageCurrent.LoadFromFile(strImageCurrent);     canvasCurrent := TFPImageCanvas.create(imageCurrent);     writerCurrent := TFPWriterPNG.Create;     //Get size of the heightmap     iSizeX := imageCurrent.Height;     iSizeY := imageCurrent.Width;     //Get number of pixels needed to make image divisible by 257     iAddX := 257 - (iSizeX mod 257);     iAddY := 257 - (iSizeY mod 257);     //Set canvas to the size we want     canvasCurrent.Width := iSizeX + iAddX;     canvasCurrent.Height := iSizeY + iAddY;     //Fill added rows with black     with canvas do begin       pen.mode := pmBlack;       pen.style := psSolid;       pen.width := 1;       pen.FPColor := TColorToFPColor(RGBToColor(0,0,0));     end;     ShowMessage('Canvas set');     canvasCurrent.FillRect(iSizeX + 1, iSizeY + 1,                            canvasCurrent.Width, canvasCurrent.Height                            );     imageCurrent.SaveToFile('Test.png', writerCurrent);   end;   writerCurrent.Free;   readerCurrent.Free;   canvasCurrent.Free;   imageCurrent.Free;   ShowMessage('Free finished!'); end;    Edit2: Right, it turns out that resizing the canvas via: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     canvasCurrent.Width := iSizeX + iAddX;    canvasCurrent.Height := iSizeY + iAddY; Is what corrupts the image. Not the rectangle being drawn. Edit3: Same happens with: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     imageCurrent.SetSize(iSizeX + iAddX, iSizeY + iAddY); ---------------------------------------------------------------------- marcov · 2023-01-09 09:01 I don't use canvas for image operations.  Whatever library I use, I get dimensions, bpp, rowpitch (*) and some format enumerator. Then I get line pointers, and simply use fillword/char etc. (*) rowpitch is the jump between lines. which usually is (pixelsx*bpp) rounded up to 32-bit, but can be negative for bottom-up images. ---------------------------------------------------------------------- wp · 2023-01-09 11:27 Quote from: FiftyTifty on January 08, 2023, 08:38:26 pmHere's the resulting heightmap: (https://i.imgur.com/ydZO386.png) Project: https://mega.nz/file/XwNCmayZ#0MuwF4X- t75jXlccRxVL5N3ZCqKn984R3yr0HON1uf0 Please don't store attachments on external servers where they will be gone sooner or later. Then this thread will become useless to anybody. And I don't want to register at the mega.nz site just to have a look at your project. Pack the project files (*.pas, *.lfm, *.lpi, *.lpr plus required data files) into a single zip which you can upload under "Attachments and other options". There is a maximum upload limit which may become problematic with large projects, but you should be aware that the chance for somebody to look at your project decreases drastically with the size of your project. The image may be an exception to this rule since it is very large and will probably exceed the upload limit. I downloaded the image from the imgur server, but noticed that it is a 24bit image - I thought you are talking of 16-bit grayscale. ---------------------------------------------------------------------- FiftyTifty · 2023-01-09 17:01 Quote from: marcov on January 09, 2023, 10:01:23 amI don't use canvas for image operations.  Whatever library I use, I get dimensions, bpp, rowpitch (*) and some format enumerator. Then I get line pointers, and simply use fillword/char etc. (*) rowpitch is the jump between lines. which usually is (pixelsx*bpp) rounded up to 32-bit, but can be negative for bottom-up images. That sounds pretty complex. Are there any tutorials on doing that with Lazarus? Quote from: wp on January 09, 2023, 12:27:15 pm-snip- Yeah the image is converted to 8 bit RGB by imgur, and any image host for that matter. The source image is 16 bit greyscale, which you'd see if you downloaded the archive which contains the image. And it's too large to zip and upload, as the image is 11MB, and the project around 30MB.