Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > смещение указателя


Автор: 14SatanA88 7.9.2011, 12:23
Доброго времени суток, уважаемые программеры.

Разбираю BMP по кускам (лаба).

Код

bmpfi:=p+sizeof(BMPFileHeader);

На этой строке компилятор ругает меня: "Operator not applicable to this operand type".

Код

procedure TMainForm.btnBMPClick(Sender: TObject);
type
  {BMP file header}
  BMPFileHeader = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize_l, bfhSize_h: WORD;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset_l, bfhDataOffset_h: WORD;
  end;
  {BMP info header}
  BMPInfoHeader = record
    bihSize_l, bihSize_h: WORD;
    bihWidth_l, bihWidth_h: WORD;
    bihHeight_l, bihHeight_h: WORD;
    bihPlanes: WORD;
    bihBits: WORD;
    bihCompression_l, bihCompression_h: WORD;
    bihSizeImage_l, bihSizeImage_h: WORD;
    bihResX_l, bihResX_h: WORD;
    bihResY_l, bihResY_h: WORD;
    bihColors_l, bihColors_h: WORD;
    bihIColors_l, bihIColors_h: WORD;
  end;
var fBMP: file of byte; // file
    buf: array[1..1024] of byte;
    bmpfh: ^BMPFileHeader;
    bmpfi: ^BMPInfoHeader;
    p: pointer;
begin

  // set filter on dialog
  OpenDialog.Filter:='BMP|*.bmp';

  // opening file
  if OpenDialog.Execute then
  begin
    imgBMP.Picture.LoadFromFile(OpenDialog.FileName);
    editBMP.Text:=OpenDialog.FileName;
    AssignFile(fBMP,OpenDialog.FileName);
    Reset(fBMP);
    BlockRead(fBMP,buf,sizeof(BMPInfoHeader)+sizeof(BMPInfoHeader));
    p:=@buf;
    memoBMP.Clear;
  // output
    // head
    bmpfh:=p;
    memoBMP.Lines.Add('signature = '+bmpfh^.bfhType_l+bmpfh^.bfhType_h);
    memoBMP.Lines.Add('size = '+inttostr(bmpfh^.bfhSize_h*65536+bmpfh^.bfhSize_l));
    memoBMP.Lines.Add('image starting address = '+inttostr(bmpfh^.bfhDataOffset_h*65536+bmpfh^.bfhDataOffset_l));
    // info
    bmpfi:=p+sizeof(BMPFileHeader);
    memoBMP.Lines.Add('header size = '+inttostr(bmpfi^.bihSize_h*65536+bmpfi^.bihSize_l));
    memoBMP.Lines.Add('width = '+inttostr(bmpfi^.bihWidth_h*65536+bmpfi^.bihWidth_l));
    // end
    CloseFile(fBMP);
  end;


Как обойти? К сожалению, мало что соображаю в указателях, не знаю как там что...
Заранее благодарен за помощь.

P.S. Я надеюсь, проблему изложил понятно.

P.P.S. Видел здесь где-то статейку про указатели, но потерял. Дайте ссылку.

Автор: AndreyIQ 7.9.2011, 13:04
Цитата(14SatanA88 @ 7.9.2011,  12:23)
Доброго времени суток, уважаемые программеры.

Разбираю BMP по кускам (лаба).

Код

bmpfi:=p+sizeof(BMPFileHeader);

На этой строке компилятор ругает меня: "Operator not applicable to this operand type".

Код

procedure TMainForm.btnBMPClick(Sender: TObject);
type
  {BMP file header}
  BMPFileHeader = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize_l, bfhSize_h: WORD;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset_l, bfhDataOffset_h: WORD;
  end;
  {BMP info header}
  BMPInfoHeader = record
    bihSize_l, bihSize_h: WORD;
    bihWidth_l, bihWidth_h: WORD;
    bihHeight_l, bihHeight_h: WORD;
    bihPlanes: WORD;
    bihBits: WORD;
    bihCompression_l, bihCompression_h: WORD;
    bihSizeImage_l, bihSizeImage_h: WORD;
    bihResX_l, bihResX_h: WORD;
    bihResY_l, bihResY_h: WORD;
    bihColors_l, bihColors_h: WORD;
    bihIColors_l, bihIColors_h: WORD;
  end;
var fBMP: file of byte; // file
    buf: array[1..1024] of byte;
    bmpfh: ^BMPFileHeader;
    bmpfi: ^BMPInfoHeader;
    p: pointer;
begin

  // set filter on dialog
  OpenDialog.Filter:='BMP|*.bmp';

  // opening file
  if OpenDialog.Execute then
  begin
    imgBMP.Picture.LoadFromFile(OpenDialog.FileName);
    editBMP.Text:=OpenDialog.FileName;
    AssignFile(fBMP,OpenDialog.FileName);
    Reset(fBMP);
    BlockRead(fBMP,buf,sizeof(BMPInfoHeader)+sizeof(BMPInfoHeader));
    p:=@buf;
    memoBMP.Clear;
  // output
    // head
    bmpfh:=p;
    memoBMP.Lines.Add('signature = '+bmpfh^.bfhType_l+bmpfh^.bfhType_h);
    memoBMP.Lines.Add('size = '+inttostr(bmpfh^.bfhSize_h*65536+bmpfh^.bfhSize_l));
    memoBMP.Lines.Add('image starting address = '+inttostr(bmpfh^.bfhDataOffset_h*65536+bmpfh^.bfhDataOffset_l));
    // info
    bmpfi:=p+sizeof(BMPFileHeader);
    memoBMP.Lines.Add('header size = '+inttostr(bmpfi^.bihSize_h*65536+bmpfi^.bihSize_l));
    memoBMP.Lines.Add('width = '+inttostr(bmpfi^.bihWidth_h*65536+bmpfi^.bihWidth_l));
    // end
    CloseFile(fBMP);
  end;


Как обойти? К сожалению, мало что соображаю в указателях, не знаю как там что...
Заранее благодарен за помощь.

P.S. Я надеюсь, проблему изложил понятно.

P.P.S. Видел здесь где-то статейку про указатели, но потерял. Дайте ссылку.

Не уверен, что это правильно, но попробуй так
Код

type
  {BMP file header}
  BMPFileHeader = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize_l, bfhSize_h: WORD;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset_l, bfhDataOffset_h: WORD;
  end;
   PBMPFileHeader = ^BMPFileHeader;
  {BMP info header}
  BMPInfoHeader = record
    bihSize_l, bihSize_h: WORD;
    bihWidth_l, bihWidth_h: WORD;
    bihHeight_l, bihHeight_h: WORD;
    bihPlanes: WORD;
    bihBits: WORD;
    bihCompression_l, bihCompression_h: WORD;
    bihSizeImage_l, bihSizeImage_h: WORD;
    bihResX_l, bihResX_h: WORD;
    bihResY_l, bihResY_h: WORD;
    bihColors_l, bihColors_h: WORD;
    bihIColors_l, bihIColors_h: WORD;
  end;
  PBMPInfoHeader = ^BMPInfoHeader;
var fBMP: file of byte; // file
    buf: array[1..1024] of byte;
    bmpfh: PBMPFileHeader;
    bmpfi: PBMPInfoHeader;
    p: pointer;
begin

  // set filter on dialog
  OpenDialog.Filter:='BMP|*.bmp';

  // opening file
  if OpenDialog.Execute then
  begin
    imgBMP.Picture.LoadFromFile(OpenDialog.FileName);
    editBMP.Text:=OpenDialog.FileName;
    AssignFile(fBMP,OpenDialog.FileName);
    Reset(fBMP);
    BlockRead(fBMP,buf,sizeof(BMPInfoHeader)+sizeof(BMPInfoHeader));
    p:=@buf;
    memoBMP.Clear;
  // output
    // head
    bmpfh:=p;
    memoBMP.Lines.Add('signature = '+bmpfh^.bfhType_l+bmpfh^.bfhType_h);
    memoBMP.Lines.Add('size = '+inttostr(bmpfh^.bfhSize_h*65536+bmpfh^.bfhSize_l));
    memoBMP.Lines.Add('image starting address = '+inttostr(bmpfh^.bfhDataOffset_h*65536+bmpfh^.bfhDataOffset_l));
    // info
    Inc(PBMPFileHeader(p));
    bmpfi:=p;
    memoBMP.Lines.Add('header size = '+inttostr(bmpfi^.bihSize_h*65536+bmpfi^.bihSize_l));
    memoBMP.Lines.Add('width = '+inttostr(bmpfi^.bihWidth_h*65536+bmpfi^.bihWidth_l));
    // end
    CloseFile(fBMP);
  end;

Автор: 14SatanA88 7.9.2011, 13:25
AndreyIQ, к сожалению, не работает.

Автор: rodnover 7.9.2011, 13:35
а читать поблочно, но несколько раз? 
Или так:

Код

procedure TMainForm.btnBMPClick(Sender: TObject);
type
  {BMP file header}
  BMPFileHeader = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize_l, bfhSize_h: WORD;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset_l, bfhDataOffset_h: WORD;
  end;
  {BMP info header}
  BMPInfoHeader = record
    bihSize_l, bihSize_h: WORD;
    bihWidth_l, bihWidth_h: WORD;
    bihHeight_l, bihHeight_h: WORD;
    bihPlanes: WORD;
    bihBits: WORD;
    bihCompression_l, bihCompression_h: WORD;
    bihSizeImage_l, bihSizeImage_h: WORD;
    bihResX_l, bihResX_h: WORD;
    bihResY_l, bihResY_h: WORD;
    bihColors_l, bihColors_h: WORD;
    bihIColors_l, bihIColors_h: WORD;
  end;
var fBMP: file of byte; // file
    buf: array[1..2] of BMPInfoHeader;
    bmpfh: ^BMPFileHeader;
    bmpfi: ^BMPInfoHeader;
    p: pointer;
begin
  // set filter on dialog
  OpenDialog.Filter:='BMP|*.bmp';
  // opening file
  if OpenDialog.Execute then
  begin
    imgBMP.Picture.LoadFromFile(OpenDialog.FileName);
    editBMP.Text:=OpenDialog.FileName;
    AssignFile(fBMP,OpenDialog.FileName);
    Reset(fBMP);
    BlockRead(fBMP,buf,sizeof(BMPInfoHeader)+sizeof(BMPInfoHeader));
//    p:=@buf[0];
    memoBMP.Clear;
  // output
    // head
    bmpfh:=@buf[1];
    memoBMP.Lines.Add('signature = '+bmpfh^.bfhType_l+bmpfh^.bfhType_h);
    memoBMP.Lines.Add('size = '+inttostr(bmpfh^.bfhSize_h*65536+bmpfh^.bfhSize_l));
    memoBMP.Lines.Add('image starting address = '+inttostr(bmpfh^.bfhDataOffset_h*65536+bmpfh^.bfhDataOffset_l));
    // info
    bmpfi:=@buf[2];
    memoBMP.Lines.Add('header size = '+inttostr(bmpfi^.bihSize_h*65536+bmpfi^.bihSize_l));
    memoBMP.Lines.Add('width = '+inttostr(bmpfi^.bihWidth_h*65536+bmpfi^.bihWidth_l));
    // end
    CloseFile(fBMP);
  end;

Автор: 14SatanA88 7.9.2011, 13:42
rodnover, 
Код

buf: array[1..2] of BMPInfoHeader;
 наверное не поможет, поскольку записи разные (BMPInfoHeader и BMPFileHeader), хотя не проверял

вопрос решился тупым преобразованием (как я сразу не догадался...)
Код

bmpfi:=pointer(integer(p)+sizeof(BMPFileHeader));


указатель сместился на нужную позицию.

Автор: rodnover 7.9.2011, 13:45
ой, вот жешь блин. Извинияюсь. на 5 раз перепроверил, а казалось, что это одинаковые типы. Конец рабочего дня...

Автор: 14SatanA88 7.9.2011, 16:53
все теперь нормально, но хотелось бы добить код.
вот у меня запись
Код

  BMPFileHeader = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize_l, bfhSize_h: WORD;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset_l, bfhDataOffset_h: WORD;
  end;

а вот пара WORD, в которых записан размер файла
Код

memoBMP.Lines.Add('size = '+inttostr(bmpfh^.bfhSize_h*65536+bmpfh^.bfhSize_l));

работает четко

если меняю на
Код

  BMPFileHeader = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize: LONGINT; // поменял вот здесь
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset_l, bfhDataOffset_h: WORD;
  end;

и
Код

memoBMP.Lines.Add('size = '+inttostr(bmpfh^.bfhSize));

выводит совсем не то. 

где я накосячил?

(я отклонился от темы, но все же хотел бы упростить код)

Автор: 14SatanA88 8.9.2011, 08:27
up

актуален только последний вопрос


 ! 
bems
Я те апну

Автор: rodnover 8.9.2011, 08:46
Код

procedure TForm2.FormCreate(Sender: TObject);
type
  {BMP file header}
  BMPFileHeader = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize_l, bfhSize_h: WORD;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset_l, bfhDataOffset_h: WORD;
  end;

  BMPFileHeader2 = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize: LONGINT;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset_l, bfhDataOffset_h: WORD;
  end;
var
  bmpfh: BMPFileHeader;
  bmpfh2: BMPFileHeader2;
begin
  bmpfh.bfhSize_l := $0304;
  bmpfh.bfhSize_h := $0102;
  bmpfh2.bfhSize := $01020304;
  ShowMessage(IntToStr(bmpfh.bfhSize_l + bmpfh.bfhSize_h * 65536));
  ShowMessage(IntToStr(bmpfh2.bfhSize));
end;


Работает отлично. 
Тогда возникает вопрос. В каком порядке читаются байты в файле. может там просто порядок напутан и в итоге получается, что 
Код

bmpfh2.bfhSize := $02010403;

И выводит вместо $01020304?

Если приведешь пример файла (именно читаемого куска, чтобы ковыряться меньше) можно будет точнее разъяснить.

Автор: 14SatanA88 8.9.2011, 09:05
Цитата(rodnover @  8.9.2011,  08:46 Найти цитируемый пост)
В каком порядке читаются байты в файле.


по порядку вроде как...
ну вот весь код:
Код

procedure TMainForm.btnBMPClick(Sender: TObject);
type
  {BMP file header}
  BMPFileHeader = record
    bfhType_l, bfhType_h: CHAR;
    bfhSize: DWORD;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset: DWORD;
  end;
  {BMP info header}
  BMPInfoHeader = record
    bihSize_l, bihSize_h: WORD;
    bihWidth_l, bihWidth_h: WORD;
    //bihHeight_l, bihHeight_h: WORD;
    bihHeight: DWORD;
    bihPlanes: WORD;
    bihBits: WORD;
    bihCompression_l, bihCompression_h: WORD;
    bihSizeImage_l, bihSizeImage_h: WORD;
    bihResX_l, bihResX_h: WORD;
    bihResY_l, bihResY_h: WORD;
    bihColors_l, bihColors_h: WORD;
    bihIColors_l, bihIColors_h: WORD;
  end;
var fBMP: file of byte; // file
    buf: array[1..1024] of byte;
    bmpfh: ^BMPFileHeader;
    bmpfi: ^BMPInfoHeader;
    p: pointer;
begin

  // set filter on dialog
  OpenDialog.Filter:='BMP|*.bmp';

  // opening file
  if OpenDialog.Execute then
  begin
    imgBMP.Picture.LoadFromFile(OpenDialog.FileName);
    editBMP.Text:=OpenDialog.FileName;
    AssignFile(fBMP,OpenDialog.FileName);
    Reset(fBMP);
    BlockRead(fBMP,buf,sizeof(BMPFileHeader)+sizeof(BMPInfoHeader));
    p:=@buf;
    memoBMP.Clear;
  // output
    // head
    bmpfh:=p;
    memoBMP.Lines.Add('signature = '+bmpfh^.bfhType_l+bmpfh^.bfhType_h);
    memoBMP.Lines.Add('size = '+inttostr(bmpfh^.bfhSize));
    memoBMP.Lines.Add('image starting address = '+inttostr(bmpfh^.bfhDataOffset));
    // info
    bmpfi:=pointer(integer(p)+sizeof(BMPFileHeader));
    memoBMP.Lines.Add('header size = '+inttostr(bmpfi^.bihSize_h*65536+bmpfi^.bihSize_l));
    memoBMP.Lines.Add('width = '+inttostr(bmpfi^.bihWidth_h*65536+bmpfi^.bihWidth_l));
    memoBMP.Lines.Add('height = '+inttostr(bmpfi^.bihHeight));
    // end
    CloseFile(fBMP);
  end;

end;


Цитата(rodnover @  8.9.2011,  08:46 Найти цитируемый пост)
Если приведешь пример файла

возьми абсолютно любой BMP и попробуй моим кодом. bfhSize: DWORD; должен будет хранить размер изображения.

Автор: rodnover 8.9.2011, 09:28
Походу, косяк из-за того, что Delphi адресует CHAR не так как надо

Код

bfhType_l,  // Адрес у меня $12EF2E
 bfhType_h: CHAR // Адрес $12EF2С (разница 2 (!) байта, а не один).


Добавлено через 2 минуты и 54 секунды
Код

    bfhType_l, bfhType_h: BYTE; // Было CHAR



У тебя Delphi старше 2009?

Автор: 14SatanA88 8.9.2011, 09:44
Delphi 7.
Заменил CHAR на BYTE, косяк тот же

Автор: AndreyIQ 8.9.2011, 09:46
Если верить википедии, то заголовок файла нужно объявить примерно так:
Код

type
  tagBITMAPFILEHEADER = record
    bfType: WORD;
    bfSize: DWORD;
    bfReserved1: WORD;
    bfReserved2: WORD;
    bfOffBits: DWORD;
  end;
  PBITMAPFILEHEADER = ^tagBITMAPFILEHEADER;

Автор: 14SatanA88 8.9.2011, 09:50
Цитата(AndreyIQ @  8.9.2011,  09:46 Найти цитируемый пост)
Если верить википедии, то заголовок файла нужно объявить примерно так

да это значения не имеет. сумма байт сходится. 

bfType я делю на два CHAR, чтобы получить сигнатуру.

а имена полей и записей ни на что не влияют

Автор: rodnover 8.9.2011, 09:54
Код

BMPFileHeader = packed record
    bfhType_l, bfhType_h: BYTE;
    bfhSize: DWORD;
    bfhReserved1, bfhReserved2: WORD;
    bfhDataOffset: DWORD;
  end;


Так попробуй. record выравнивает структуру, а packed впритык делает.

Добавлено @ 09:55
У меня и работало, потому что я уже успел packed поставить и забыл. smile  без него тоже не работает, смещение другое показывает.

Автор: AndreyIQ 8.9.2011, 09:58
Цитата(14SatanA88 @ 8.9.2011,  09:50)
Цитата(AndreyIQ @  8.9.2011,  09:46 Найти цитируемый пост)
Если верить википедии, то заголовок файла нужно объявить примерно так

да это значения не имеет. сумма байт сходится. 

bfType я делю на два CHAR, чтобы получить сигнатуру.

а имена полей и записей ни на что не влияют

Код

bfhSize_l, bfhSize_h: WORD;
bfhSize: LONGINT; // поменял вот здесь

WORD и DWORD - unsigned 16-bit и unsigned 32-bit соответсвено
LONGINT - signed 32-bit 

Автор: 14SatanA88 8.9.2011, 09:59
rodnover, спасибо, помогло. вопрос решен.
а почему без packed не работало?

Добавлено через 1 минуту и 58 секунд
AndreyIQ, пробовал LONGINT. но дело было не в этом.

Автор: rodnover 8.9.2011, 10:07
Потому что типа того (в скобках слева буду показывать смещение относительно начала структуры)

Код

BMPFileHeader =record
    (0) bfhType_l, bfhType_h: BYTE;
    (4) bfhSize: DWORD;
    (8) bfhReserved1, bfhReserved2: WORD;
    (12) bfhDataOffset: DWORD;
  end;

Так у меня было. а с packed получается так:


Код

BMPFileHeader = packed record
    (0) bfhType_l, bfhType_h: BYTE;
    (2) bfhSize: DWORD;
    (6) bfhReserved1, bfhReserved2: WORD;
    (10) bfhDataOffset: DWORD;
  end;


Это выравнивание по границе двойного слово связано с оптимизацией.

Автор: 14SatanA88 8.9.2011, 11:48
ясно, спасибо

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)