Einzelnen Beitrag anzeigen

berens

Registriert seit: 3. Sep 2004
421 Beiträge
 
Delphi 2010 Professional
 
#3

AW: PNG Vertikal spiegeln mit ScanLine

  Alt 1. Jun 2021, 14:15
Hallo nochmal alle, und Danke an Redeemer für Deinen Beitrag.
Also ich bin mir immer noch unsicher, was das mit den 3 bzw. 4 Bytes pro Pixel angeht.

Ich habe jetzt mal in den QuellCode der pngimage geschaut, und da gibt es zwei verschiedene Größenberechnungen für Bits/Bytes pro Pixel, und *natürlich* sind die mal wieder alle "Private", so dass ich mir die Prozeduren kopieren muss, wenn ich nicht in jedem Modul TPNGImage durch was abgeleitetes ersetzen möchte.

Jetzt kommt zu meiner Freude bei beiden Verfahren wieder zwei verschiedene Zahlen raus: Ein Mal 3 Bytes pro Pixel, ein Mal 4 Bytes pro Pixel, so dass ich nun genauso schlau bin wie vorher auch. Ich nehme an, eine Angabe bezieht sich tatsächlich auf die ByteProPixel für die ScanLine, die Andere auf die ByteProPixel beim Arbeiten mit einem Stream (lesen/schreiben, bzw. Laden/Speichern einer .png-Datei).

Bitte hasst mich nicht dafür, und es soll auch keine "Faulheit" sein, aber ich habe keine Zeit und Nerv, die *komplette* PngImage Unit jetzt solange durchzuarbeiten, bis ich sie ausreichend verstehe. Da mein Programm mit 3 Bytes / Pixel die richtige Ausgabe erzeugt, verwende ich diesen Wert, und hoffe, es wird nichts schlimmes passieren (mit den 4 Bytes / Pixel könnte imo sehr wohl etwas Schlimmes passieren!).
Falls jemand zufälligerweise das notwendige Know-How hat um die Frage zu Beantworten, bin ich natürlich sehr dankbar dafür!

---

Die eine Angabe findet sich in den pngimage Unit in Zeile 2274:
Delphi-Quellcode:
  {Obtain number of bits for each pixel}
  case ColorType of
    COLOR_GRAYSCALE, COLOR_PALETTE, COLOR_GRAYSCALEALPHA:
      case BitDepth of
        {These are supported by windows}
        1, 4, 8: SetInfo(BitDepth, TRUE);
        {2 bits for each pixel is not supported by windows bitmap}
        2 : SetInfo(4, TRUE);
        {Also 16 bits (2 bytes) for each pixel is not supported}
        {and should be transormed into a 8 bit grayscale}
        16 : SetInfo(8, TRUE);
      end;
    {Only 1 byte (8 bits) is supported}
    COLOR_RGB, COLOR_RGBALPHA: SetInfo(24, FALSE);
  end {case ColorType};
daraus habe ich mir gebastelt:
Delphi-Quellcode:
function BitsPerPixel(const ColorType, BitDepth: Byte): Integer;
begin
  Result := 0;

  case ColorType of
    COLOR_GRAYSCALE, COLOR_PALETTE, COLOR_GRAYSCALEALPHA:
      case BitDepth of
        {These are supported by windows}
        1, 4, 8: Result := BitDepth;
        {2 bits for each pixel is not supported by windows bitmap}
        2 : Result := 4;
        {Also 16 bits (2 bytes) for each pixel is not supported}
        {and should be transormed into a 8 bit grayscale}
        16 : Result := 8;
      end;
    {Only 1 byte (8 bits) is supported}
    COLOR_RGB, COLOR_RGBALPHA: Result := 24;
  end;
end;
Ergibt bei RGBA also 24 --> 3 Bytes.

Die andere Stelle steht in Zeile 1073:
Delphi-Quellcode:
{Calculates number of bytes for the number of pixels using the}
{color mode in the paramenter}
function BytesForPixels(const Pixels: Integer; const ColorType,
  BitDepth: Byte): Integer;
begin
  case ColorType of
    {Palette and grayscale contains a single value, for palette}
    {an value of size 2^bitdepth pointing to the palette index}
    {and grayscale the value from 0 to 2^bitdepth with color intesity}
    COLOR_GRAYSCALE, COLOR_PALETTE:
      Result := (Pixels * BitDepth + 7) div 8;
    {RGB contains 3 values R, G, B with size 2^bitdepth each}
    COLOR_RGB:
      Result := (Pixels * BitDepth * 3) div 8;
    {Contains one value followed by alpha value booth size 2^bitdepth}
    COLOR_GRAYSCALEALPHA:
      Result := (Pixels * BitDepth * 2) div 8;
    {Contains four values size 2^bitdepth, Red, Green, Blue and alpha}
    COLOR_RGBALPHA:
      Result := (Pixels * BitDepth * 4) div 8;
    else
      Result := 0;
  end {case ColorType}
end;
Diese gibt bei RGBA für einen Pixel den Wert 4 zurück, was -denke ich- in meinem Fall (für die Scanline ohne Alpha) falsch wäre.

Somit ergibt sich für mich der folgende QuellCode:
Delphi-Quellcode:
function BitsPerPixel(const ColorType, BitDepth: Byte): Integer;
begin
  Result := 0;

  case ColorType of
    COLOR_GRAYSCALE, COLOR_PALETTE, COLOR_GRAYSCALEALPHA:
      case BitDepth of
        {These are supported by windows}
        1, 4, 8: Result := BitDepth;
        {2 bits for each pixel is not supported by windows bitmap}
        2 : Result := 4;
        {Also 16 bits (2 bytes) for each pixel is not supported}
        {and should be transormed into a 8 bit grayscale}
        16 : Result := 8;
      end;
    {Only 1 byte (8 bits) is supported}
    COLOR_RGB, COLOR_RGBALPHA: Result := 24;
  end;
end;

// Basierend auf einem Code-Beispiel von Gustavo Daud (Submited on 17 Apr 2006 )
procedure FlipPngVertical(_Source, _Target: TPngImage);
var
  X, Y: Integer;
  IsAlpha: Boolean;
  MemSource, MemTarget: pByteArray;
  PixelSize: Byte;
  BytesPerPixel: integer;
begin
  _Target.Assign(_Source);

  IsAlpha := _Source.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA];
  
  for Y := 0 to _Target.Height - 1 do begin
    if IsAlpha then begin
      CopyMemory(_Target.AlphaScanline[_Target.Height - Y - 1],
        _Source.AlphaScanline[Y], _Target.Width);
    end;

    MemSource := _Source.Scanline[Y];
    MemTarget := _Target.Scanline[_Target.Height - Y - 1];

    // Nein: Es wird nur 1/3 des Bildes kopiert!
    // CopyMemory(MemTarget, MemSource, _Target.Width);

    // Ja! Wahrscheinlich weil das Bild 32-Bit ist?
    BytesPerPixel := BitsPerPixel(_Source.Header.ColorType, _Source.Header.BitDepth) div 8;
    CopyMemory(MemTarget, MemSource, _Source.Width * BytesPerPixel);;

// for X := 0 to _Target.Width - 1 do begin
// _Target.Pixels[X, Y] := _Source.Pixels[X, _Target.Height - Y - 1];
// end;
  end;
end;
Jetzt muss ich mich schon direkt ducken, denn der Nächste wird zurecht anmerken "Was, wenn das PNG in Graustufen ist und du weniger als 8 Bit pro Pixel hast, dann schlägt das Kopieren fehl!". Aber vielleicht hat derjenige ja eine bessere Idee für mich...

Gibt's denn nicht irgendwie SizeOf() oder Length() für die _Source.ScanLine, was ich anwenden kann, um die exakte Größe heraus zu bekommen? Das kann doch eigentlich kein Hexenwerk sein...
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit
  Mit Zitat antworten Zitat