Skopiowanie fragmentu obrazka z grafiki PNG

Odpowiedz Nowy wątek
2013-09-16 15:57
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.

edytowany 2x, ostatnio: olesio, 2013-09-19 00:09

Pozostało 580 znaków

2013-09-16 17:35
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;

Pozostało 580 znaków

2013-09-16 18:48
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.

edytowany 1x, ostatnio: pelsta, 2013-09-16 18:51

Pozostało 580 znaków

2013-09-16 18:56
0

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

edytowany 1x, ostatnio: marogo, 2013-09-16 18:58

Pozostało 580 znaków

2013-09-16 19:03
0

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

edytowany 1x, ostatnio: pelsta, 2013-09-16 19:03

Pozostało 580 znaków

2013-09-16 20:53
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.

Pozostało 580 znaków

2013-09-18 12:34
0

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

Pozostało 580 znaków

2013-09-18 13:07
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.

Pozostało 580 znaków

2013-09-18 14:19
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;

Pozostało 580 znaków

2013-09-18 14:47
0

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

Pozostało 580 znaków

2013-09-18 17:14

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);
Ważne słowa kluczowe "bug" i "po skopiowaniu bitmapy" - pelsta 2013-09-19 09:49

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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