Skopiowanie fragmentu obrazka z grafiki PNG

0

Witam,

Potrzebuję skopiować fragment obrazka z grafiki PNG. Wykorzystuję do tego moduł PNGImage. Jest w nim zadeklarowany obiekt TPNGObject z udostępnioną właściwością Canvas.

var
  png:TPNGObject;
  bmp:TBitmap;
  r:TRect;
//...
  png:=TPNGObject.Create;
  png.LoadFromFile('mapa.png');
  bmp:=TBitmap.Create;
  bmp.Width:=256;
  bmp.Height:=256;
  r:=Bounds(0,0,bmp.Width,bmp.Height);
  bmp.Canvas.CopyRect(bmp.Canvas.ClipRect,png.Canvas,r);
//...

Niestety taki sposób kopiowania nie zachowuje oryginalnych kolorów.

Oryginał:
oryginal.png

Skopiowany fragment:
kopia.png

Pytanie brzmi:
Jak to wykonać aby kolory zostały zachowane? Program w Delphi 7.

1

Spróbuj:

procedure CropPNG(Source: TPNGImage; Left, Top, Width, Height: Integer;
  out Target: TPNGImage);
var
  IsAlpha: Boolean;
  Line: Integer;
begin
  if (Source.Width < (Left + Width)) or (Source.Height < (Top + Height)) then
    raise Exception.Create('Niepoprawne wymiary obrazka!');

  Target := TPNGImage.CreateBlank(Source.Header.ColorType,
    Source.Header.BitDepth, Width, Height);
  IsAlpha := Source.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA];
  for Line := 0 to Target.Height - 1 do
  begin
    if IsAlpha then
      CopyMemory(Target.AlphaScanline[Line],
        Ptr(LongInt(Source.AlphaScanline[Line + Top]) + LongInt(Left)),
        Target.Width);
    CopyMemory(Target.Scanline[Line],
      Ptr(LongInt(Source.Scanline[Line + Top]) + LongInt(Left * 3)),
      Target.Width * 3);
  end;
end;
0

Dzięki.
Procedura działa prawidłowo dla grafiki 24bpp.
Ale moje mapy mają ColorType=COLOR_PALETTE i BitDepth=8
Jak to ugryźć?

ps
Mój sposób z grafiką 24-bitową też się sprawdza.

0

Może trzeba ustawić odpowiednią wartość property bitmapy PixelFormat? Może pf8bit?

0

Próbowałem ale to nie pomaga.
Pewnie trzeba jakoś skopiować paletę z png do bmp?

0

Zapisz skopiowany plik PNG i sprawdź, czy ma taką samą paletę, jak oryginał. Na pewno można jakoś odczytać paletę z jednego obrazka i ustawić paletę w drugim obrazku. Dopiero po uzgodnieniu palet kopiuj obrazek. Oczywiście inaczej sprawa wygląda w przypadku obrazkóe 24-bit i greyscale.

0

Możesz zrobić TempBmp.assign(png), aby uzyskać bitmapę i dopiero z niej kopiować to to potrzeba.

0
andrzejlisek napisał(a):

Na pewno można jakoś odczytać paletę z jednego obrazka i ustawić paletę w drugim obrazku.

Na tym właśnie utknąłem.
Próbuję tak ale nie sprawdza się:

var
  png:TPNGObject;
  bmp:TBitmap;
//...
  bmp.PixelFormat:=pf8bit;
  bmp.Palette:=png.Palette;
  bmp.Canvas.CopyRect(bmp.Canvas.ClipRect,png.Canvas,Bounds(0,0,bmp.Width,bmp.Height));
//...
ergo napisał(a):

Możesz zrobić TempBmp.assign(png), aby uzyskać bitmapę i dopiero z niej kopiować to to potrzeba.

Chciałem obejść konieczność tworzenia dodatkowej bitmapy bo to podwaja zapotrzebowanie na pamięć. Pliki png, które będą przetwarzane, mogą być bardzo duże. Przy plikach o wielkości kilkanaście MB (np. grafika 5000x7000) taki sposób jest oczywiście dobry.

0

Spróbuj użyć metody Draw zamiast CopyRect i powinno być ok.

 var
  png: TPNGObject; 
  bmp: TBitmap;
begin
  png := TPNGObject.Create; 
  png.LoadFromFile('oryginal.png');

  bmp:=TBitmap.Create;
  bmp.Width:=256;
  bmp.Height:=256;

  bmp.PixelFormat := pf8bit;

  bmp.Canvas.Draw(0, 0, png);

  bmp.SaveToFile('test.bmp');
end;
0

Chcę skopiować fragment grafiki png a nie całą.

3

Zdaje się, że winą jest bug przy tworzeniu tabeli kolorów bitmapy, i trzeba ją skopiować ręcznie:

var
  rgb : array[0..255] of RGBquad;
  table : array[0..255] of PaletteEntry;

//To wklejasz po skopiowaniu bitmapy (poniżej CopyRect).
  GetPaletteEntries(png.palette,0,256, table);
  for i := 0 to 255 do begin
    rgb[i].rgbBlue := table[i].peBlue;
    rgb[i].rgbGreen := table[i].peGreen;
    rgb[i].rgbRed := table[i].peRed;
  end;
  SetDIBColorTable(bmp.Canvas.Handle, 0, 256, rgb);
0

Wygląda na to, że masz rację. Dzięki!

1 użytkowników online, w tym zalogowanych: 0, gości: 1