SIGSEGV mimo dostepnej pamięci po uzyciu GetMem

0

Mam taki kod:

type
  TTextureData = record  // Własny typ zawierający wysokość i szerokość tekstury, format kolorów oraz wskaźnik na dane.
    Width,
    Height : Word;
    Format : TColorFormat;
    Data   : Pointer;
  end;

type
  TTGAImageHeader = packed record
    header        : array[0..5] of byte;
    BytesPerPixel : glUInt;
    ImageSize     : glUInt;
    ImageType     : glUInt;
    Height        : glUInt;
    Width         : glUInt;
    Bpp           : glUInt;
  end;

function LoadCompressedTGA(FileName : String) : TTextureData;
var
  TGAFile      : TFileStream;
  ImageHeader  : TTGAImageHeader;
  ImageData    : PByteArray;

  PixelCount   : glUInt;
  CurrentPixel : glUInt;
  CurrentByte  : glUInt;
  ColorBuffer  : PByteArray;
  ChunkHeader  : Byte;

  i            : LongWord;
begin
  TGAFile := TFileStream.Create(Filename, fmOpenRead);
  try
    TGAFile.Seek(12, soFromBeginning);
    TGAFile.ReadBuffer(ImageHeader.Header, SizeOf(ImageHeader.Header));

    ImageHeader.Width := ImageHeader.Header[1] * 256 + ImageHeader.Header[0];
    ImageHeader.Height := ImageHeader.Header[3] * 256 + ImageHeader.Header[2];
    ImageHeader.Bpp := ImageHeader.Header[4];

    if ImageHeader.Bpp = 24 then
      result.Format := RGB;
    if ImageHeader.Bpp = 32 then
      result.Format := RGBA;

    ImageHeader.BytesPerPixel := ImageHeader.Bpp div 8;
    ImageHeader.ImageSize := ImageHeader.BytesPerPixel * ImageHeader.Width * ImageHeader.Height;

    GetMem(ImageData, ImageHeader.ImageSize);
    GetMem(ColorBuffer, ImageHeader.BytesPerPixel);

    PixelCount := ImageHeader.Width * ImageHeader.Height;
    ChunkHeader := 0;
    CurrentPixel := 0;
    CurrentByte := 0;

    while (CurrentPixel < PixelCount) do
    begin
      TGAFile.ReadBuffer(ChunkHeader, SizeOf(ChunkHeader));

      if ChunkHeader < 128 then
      begin
        Inc(ChunkHeader);
        for i := 0 to ChunkHeader - 1 do
        begin
          TGAFile.ReadBuffer(ColorBuffer[0], ImageHeader.BytesPerPixel);

          ImageData[CurrentByte] := ColorBuffer[2];
          ImageData[CurrentByte + 1] := ColorBuffer[1];
          ImageData[CurrentByte + 2] := ColorBuffer[0];
          if ImageHeader.BytesPerPixel = 4 then
            ImageData[CurrentByte + 3] := ColorBuffer[3];

          Inc(CurrentByte, ImageHeader.BytesPerPixel);
          Inc(CurrentPixel);
        end;
      end
      else
      begin
        Dec(ChunkHeader, 127);

        TGAFile.ReadBuffer(ColorBuffer[0], ImageHeader.BytesPerPixel);

        for i := 0 to ChunkHeader - 1 do
        begin
          ImageData[CurrentByte] := ColorBuffer[2];  // <- Tutak się wywala.
          ImageData[CurrentByte + 1] := ColorBuffer[1];
          ImageData[CurrentByte + 2] := ColorBuffer[0];
          if ImageHeader.BytesPerPixel = 4 then
            ImageData[CurrentByte + 3] := ColorBuffer[3];

          Inc(CurrentByte, ImageHeader.BytesPerPixel);
          Inc(CurrentPixel);
        end;
      end;
    end;
  finally
    FreeAndNil(TGAFIle);
  end;

  result.Width := ImageHeader.Width;
  result.Height := ImageHeader.Height;
  result.Data := ImageData;
end;

Jest to funkcja wczytująca skompresowany plik TGA. Próbuję nią wczytać plik o rozdzielczości 128 na 128 pikseli z kanałem alfa. W zaznaczonym miejscu w trzecim obrocie pętli For wywala mi SIGSEGV. Nie mam pojęcia dlaczego. 10 razy sprawdzałem czy getmem alokuje wystarczającą ilość pamięci oracz czy nie przekraczam tablicy i wszystko jest ok jednak program się sypie. Ktoś może wie gdzie leży błąd?

Funkcję przetłumaczyłem z c/c++ z tej strony http://aklimx.sppieniezno.pl/nehepl/display.php?id=33 Dlatego może wymagać kosmetycznych poprawek.

0

@babubabu - na razie tutaj widzę kłopot:

TGAFile.ReadBuffer(ImageHeader.Header, SizeOf(ImageHeader.Header));

Znowu nie odwołujesz się do konkretnego elementu pola header; Debuger pokazałby Ci co wyrabiasz :]

Bardziej bym obstawał przy poniższym rozwiązaniu:

TGAFile.ReadBuffer(ImageHeader.Header[0], Length(ImageHeader.Header));

Ewentualnie zadeklaruj sobie stałą z ilością elementów macierzy header - przyda się i przy deklaracji struktury, i przy wczytywaniu nagłówka;


Druga sprawa:

GetMem(ColorBuffer, ImageHeader.BytesPerPixel);

Masz wyciek pamięci, bo alokujesz pamięć dla bufora, ale ani jej na końcu nie zwalniasz, ani nie przekazujesz do rezultatu funkcji.

0
furious programming napisał(a):

@babubabu - na razie tutaj widzę kłopot:

TGAFile.ReadBuffer(ImageHeader.Header, SizeOf(ImageHeader.Header));

Znowu nie odwołujesz się do konkretnego elementu pola header; Debuger pokazałby Ci co wyrabiasz :]

Bardziej bym obstawał przy poniższym rozwiązaniu:

TGAFile.ReadBuffer(ImageHeader.Header[0], Length(ImageHeader.Header));

Ewentualnie zadeklaruj sobie stałą z ilością elementów macierzy header - przyda się i przy deklaracji struktury, i przy wczytywaniu nagłówka;

Tutaj wszystko działa bo specjalnie pod debugerem sprawdzałem i późniejsze wykorzystanie danych wczytanych do ImageHeader.Header przy obliczeniach daje poprawne wyniki jednak zmienię na to length skoro tak jest poprawnie.


furious programming napisał(a):

Druga sprawa:

GetMem(ColorBuffer, ImageHeader.BytesPerPixel);

Masz wyciek pamięci, bo alokujesz pamięć dla bufora, ale ani jej na końcu nie zwalniasz, ani nie przekazujesz do rezultatu funkcji.

Fakt. Przy testowaniu kodu i próbach znalezienia przyczyny SIGSEGV zapomniałem dodać na końcu FreeMem. Już poprawione.

Jednak wprowadzone poprawki nie pomogły i SIGSEGV nadal występuje.

0

1. Hmmm, tutaj być może nie trzeba się odwoływać do elementu macierzy, ale przy łańcuchach na pewno, więc bezpieczniej jest się przestawić na jeden sposób, aby się w przyszłości nie myliło; Znaleźć później przyczynę błędów może być ciężko;

2. Czyli część rzeczy zrobiona; Gdzieś jednak odwołujesz się do pamięci jakiej nie zaalokowałeś - tam trzeba szukać.

0

Wykrzacza się w tej pętli

for i := 0 to ChunkHeader - 1 do
begin
  ImageData[CurrentByte] := ColorBuffer[2];  // <- Tutaj się wywala.
  ImageData[CurrentByte + 1] := ColorBuffer[1];
  ImageData[CurrentByte + 2] := ColorBuffer[0];
  if ImageHeader.BytesPerPixel = 4 then
    ImageData[CurrentByte + 3] := ColorBuffer[3];
 
  Inc(CurrentByte, ImageHeader.BytesPerPixel);
  Inc(CurrentPixel);
end;

Przy takim stanie zmiennych:

PixelCount = 16384
ChunkHeader = 25
CurrentPixel = 2
CurrentByte = 8
i = 2

Jedyne co w tej pętli jest to przepisywanie wartości z jednej zmiennej do drugiej zaalokowanych przez GetMem i zwiększanie wartości CurrentByte i CurrentPixel. Więc wszystkie zmienne są zaalokowane. Naprawdę nie mam zielonego pojęcia czemu sypie błędem.

2
ImageData[CurrentByte] := ColorBuffer[2];

To się w ogóle kompiluje..? o.O

Wszędzie gdzie chcesz się odwołać do elementu macierzy spod wskaźnika ColorBuffer musisz dodać operator ^:

ImageData^[CurrentByte] := ColorBuffer^[2];

inaczej (przynajmniej u mnie w Lazarusie) nawet się nie skompiluje... To samo przy wszystkich odwołaniach do elementów macierzy ImageData.

0

Przydzielenie pamięci na 4/3 bajty to jakiś wymyślny sposób masturbacji?
Przynajmniej ColorBuffer zrób normalni array[0..3]of Byte;
Natomiast ImageData zamień na array of Byte i przedzielaj pamięć poprzez SetLength.
Jeżeli zadziała to masz przyczynę :)

0

Jestem kretynem -.- Ten sam problem miałem przy wczytywaniu plików BMP jednak sam doszedłem do tego że jak chce się dobrać do wartości na którą wskazuje wskaźnik to muszę użyć ^... Ale tak to jest jak się siedzi 14 godzin na kodem to się nie widzi oczywistych błędów...

Przepraszam za zaspamowanie forum tym tematem.

0

@babubabu - to jeszcze nie koniec;

Algorytm może i działa, ale jest mało optymalny; Zobacz ile razy powtarzasz te same czynności i ile razy zbędnie wykonywany jest warunek sprawdzający ilość bajtów na piksel; Pozbądź się i tego warunku, i dublowanych części kodu, a na pewno przyspieszysz proces odczytu i skrócisz kod.

0

Jak pisałem wczesniej Algorytm jest tłumaczony z c++ i najpierw chciałem żeby w ogóle działał optymalizacją właśnie się zajmuję ;)

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