Czytanie bitmapy z rekordu EMR_STRETCHDIBITS MetaFile'a

0

Witajcie:)

Męczę się z tym już drugi dzień i nie mogę sobie poradzić. Otóż chciałbym stworzyć
funkcję, która wyciągnie mi bitmapę zawartą w rekordzie EMR_STRETCHDIBITS metapliku.

Wszystko by było OK, gdyby nie problem z wyciągnięciem tej bitmapy w czystej postaci,
tzn. żeby nie było żadnych przesunięć treści obrazka w stosunku do lewego górnego rogu
canvasa (np. formy) na którym maluję bitmapę oraz bez jej rozciągnięcia lub zwężenia,
żeby wyglądała jak oryginał, co można sprawdzić udostępnionym programem MS Photo Editor.

Metapliki, które chciałbym przekazać Wam do zbadania to testowy obrazek kratki pochodzący
z dokumentów Worda 2000 i Excela 2000 (z pewnych względów musi mi to działać na tak starym
Offic'ie).
Wiadomo, obrazek zaszyty w metafile może mieć dołączone dodatkowe dane "offsetowe",
które służą np. do odpowiedniego spozycjonowania (sformatowania) po przeniesieniu obiektu
z jednego dokumentu do innego. A ja chciałbym te dane zignorować , ponieważ interesuje mnie
tylko sama bitmapa.

No i każdy z obrazków został w Wordzie lub Excelu skopiowany do schowka, a potem
odczytany i zapisany do plików "emf" przy użyciu takiego kodu:

 Var
  HMetaFile: HENHMETAFILE;

 OpenClipboard(0);
 HMetaFile:=GetClipboardData(CF_ENHMETAFILE);
 CopyEnhMetaFile(HMetaFile, PChar('d:\Metaplik.emf')); //zrzut obrazka ze schowka do pliku 'emf'
 DeleteEnhMetaFile(HMetaFile);
 CloseClipboard;

I teraz chciałbym ten obrazek wypakować z tego metapliku, a zrobiłem to jak dotąd tak:

// --- funkcja callback  -------
 Function MyEnhMetaFunc(DisplaySurface: HDC; var MetafileTable: THandleTable; Var MetafileRecord: TEnhMetaRecord; ObjectCount: Integer; var Data: Longint): Integer; stdcall;
   Var
   strukt:PEMRSTRETCHDIBITS; 
begin
  If MetafileRecord.iType = EMR_STRETCHDIBITS then
    begin
      strukt:=PEMRSTRETCHDIBITS(@MetafileRecord); //dostęp do danych struktury     
      PlayEnhMetaFileRecord(DisplaySurface, MetafileTable, MetafileRecord, ObjectCount);     
      Result:=0;
    end;
End;

//--- odczytanie obrazka------------
procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenClipboard(0);
  HMetaFile:=GetEnhMetaFile(PChar('d:\SiatkaWord.emf')); //'d:\SiatkaExcel.emf' 
  SetClipboardData(CF_ENHMETAFILE, HMetaFile); //dodatkowe wklejenie do schowka
  CloseClipboard;
  EnumEnhMetaFile(Canvas.Handle, HMetaFile, @MyEnhMetaFunc, nil, Rect( 0,0, 584, 500));
  DeleteEnhMetaFile(HMetaFile);
end;

Dodatkowo odczytany plik metafile jest kopiowany do schowka, żeby można było wkleić
zawarty w nim obrazek do MS Photo Editor-a i się przekonać, że da się poprawnie odczytać
"na czysto" bitmapkę zawartą w Metafile.

Problem mam też z ustawieniem szerokości i wysokości obrazka na canvasie docelowym,
bo choć w tym przypadku są mi znane rozmiary obrazka (584/500), ale gdybym miał do czynienia
z bitmapą o nieznanych wymiarach, też musiałbym je skądś pobrać. Próbowałem czytać
dane zawarte w rekordzie EMRSTRETCHDIBITS, tzn. xDest i yDest oraz cxSrc i cySrc, ale
to nie zawsze dawało właściwe rozmiary i offset obrazka (zmienna 'strukt')

Struktura EMRSTRETCHDIBITS wyglada tak:

typedef struct tagEMRSTRETCHDIBITS {
    EMR   emr;
    RECTL rclBounds;  
    LONG  xDest; 
    LONG  yDest; 
    LONG  xSrc; 
    LONG  ySrc; 
    LONG  cxSrc; 
    LONG  cySrc;
    DWORD offBmiSrc;
    DWORD cbBmiSrc; 
    DWORD offBitsSrc; 
    DWORD cbBitsSrc; 
    DWORD iUsageSrc; 
    DWORD dwRop; 
    LONG  cxDest; 
    LONG  cyDest; 
} EMRSTRETCHDIBITS, *PEMRSTRETCHDIBITS; 

a w niej struktura:

 typedef struct tagBITMAPINFO {
   BITMAPINFOHEADER bmiHeader; 
   RGBQUAD          bmiColors[1]; 
  } BITMAPINFO; 

a w niej:

tag BITMAPINFOHEADER{ 
   DWORD  biSize; 
   LONG   biWidth; 
   LONG   biHeight; 
   WORD   biPlanes; 
   WORD   biBitCount 
   DWORD  biCompression; 
   DWORD  biSizeImage; 
   LONG   biXPelsPerMeter; 
   LONG   biYPelsPerMeter; 
   DWORD  biClrUsed; 
   DWORD  biClrImportant; 
} BITMAPINFOHEADER; 

Jakoś nie potrafię tego poskładać w jakiś działający uniwersalny kod, żeby uzyskać oryginalną bitmapę
jak w MS Photo Editor.

Z pewnych względów nie chciałbym stosować funkcji PlayEnhMetaFile, która w przypadku użycia jej
do czytania z dokumentów Office 2000 dodaje jakieś cienie do treści bitmapy, a mi zależy żeby
uzyskać oryginał, również przy użyciu tego pakietu, a chyba tylko metoda czytania rekordu
EMRSTRETCHDIBITS może to zapewnić.

Więc tak, tu Wam wrzuciłem wszystko co pomoże Wam skumać w czym problem:

http://www.sendspace.pl/file/NTO59scO/

czyli:

  • dwa pliki emf (z Worda i Excela) do odczytania zawartej w nich oryginalnej bimapy
  • screenshoty mojego programu "Screen1" i Screen2" pokazujące jak niepoprawnie wygląda odczyt
    bitmapy przy użyciu mojego programu (z Worda i Excela)
  • fajny programik "MetafileExplorer" do podglądu rekordów metafile, można zobaczyć jak wygląda
    struktura rekordu EMRSTRETCHDIBITS
  • bitmapę przedstawiającą kratkę będącą oryginalnym obrazkiem, który był wklejony w dokumenty
    Worda2000 i Excela2000 (w Wordzie celowo był zwężony w pionie i rozciągnięty w poziomie,
    co nie powinno przeszkodzić w poprawnym odczycie z metafile, MS Photo Editor daje przecie radę)
  • MS Photo Editor - do testowania prawidłowego ekstraktowania bitmapy z metapliku

Bardzo proszę o wskazówki, a najlepiej o jakiś przykładowy kod.

Pzdr.
Marogo

1

cha masz szczescie, ze lubie sie bawic bitmapami :) i dawno w delphi nie pisalem ...

na szybko (daj Image1 na forme)

Function MyEnhMetaFunc(DisplaySurface: HDC; var MetafileTable: THandleTable; Var MetafileRecord: TEnhMetaRecord; ObjectCount: Integer; var Data: Longint): Integer; stdcall;
Var
   strukt:PEMRSTRETCHDIBITS;
var
  Focus: HWND;
  DC: HDC;
  BitsMem: Pointer;
  BitmapInfo: PBitmapInfo;

begin

  If MetafileRecord.iType = EMR_STRETCHDIBITS then
    begin

      strukt:=PEMRSTRETCHDIBITS(@MetafileRecord); //dostęp do danych struktury




{
  offBmiSrc (4 bytes): A 32-bit unsigned integer that specifies the offset, in bytes from the start of this record to the source bitmap header.
  cbBmiSrc (4 bytes): A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header.
  offBitsSrc (4 bytes): A 32-bit unsigned integer that specifies the offset, in bytes, from the start of this record to the source bitmap bits.
  cbBitsSrc (4 bytes): A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits.
}

      BitmapInfo := PBitmapInfo(integer(@MetafileRecord) + strukt.offBmiSrc);
      BitsMem := Pointer(integer(@MetafileRecord) + strukt.offBitsSrc);


      Focus := GetFocus;
      DC := GetDC(Focus);
      Result := CreateDIBitmap(DC, BitmapInfo^.bmiHeader,  CBM_INIT, BitsMem,  BitmapInfo^, DIB_RGB_COLORS);
      ReleaseDC(Focus, DC);


      //na szybko bez sprawdazania watkow
      Form1.Image1.AutoSize :=  true;
      Form1.Image1.Picture.Bitmap.Handle := result;

      //PlayEnhMetaFileRecord(DisplaySurface, MetafileTable, MetafileRecord, ObjectCount);
      Result:=0;
    end;
End;
0
reichel napisał(a)

cha masz szczescie, ze lubie sie bawic bitmapami :) i dawno w delphi nie pisalem ...

na szybko (daj Image1 na forme)

Function MyEnhMetaFunc(DisplaySurface: HDC; var MetafileTable: THandleTable; Var MetafileRecord: TEnhMetaRecord; ObjectCount: Integer; var Data: Longint): Integer; stdcall;
Var
   strukt:PEMRSTRETCHDIBITS;
var
  Focus: HWND;
  DC: HDC;
  BitsMem: Pointer;
  BitmapInfo: PBitmapInfo;

begin

  If MetafileRecord.iType = EMR_STRETCHDIBITS then
    begin

      strukt:=PEMRSTRETCHDIBITS(@MetafileRecord); //dostęp do danych struktury




{
  offBmiSrc (4 bytes): A 32-bit unsigned integer that specifies the offset, in bytes from the start of this record to the source bitmap header.
  cbBmiSrc (4 bytes): A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header.
  offBitsSrc (4 bytes): A 32-bit unsigned integer that specifies the offset, in bytes, from the start of this record to the source bitmap bits.
  cbBitsSrc (4 bytes): A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits.
}

      BitmapInfo := PBitmapInfo(integer(@MetafileRecord) + strukt.offBmiSrc);
      BitsMem := Pointer(integer(@MetafileRecord) + strukt.offBitsSrc);


      Focus := GetFocus;
      DC := GetDC(Focus);
      Result := CreateDIBitmap(DC, BitmapInfo^.bmiHeader,  CBM_INIT, BitsMem,  BitmapInfo^, DIB_RGB_COLORS);
      ReleaseDC(Focus, DC);


      //na szybko bez sprawdazania watkow
      Form1.Image1.AutoSize :=  true;
      Form1.Image1.Picture.Bitmap.Handle := result;

      //PlayEnhMetaFileRecord(DisplaySurface, MetafileTable, MetafileRecord, ObjectCount);
      Result:=0;
    end;
End;

Przyjacielu, działa dokładnie tak jak chciałem. Jestem Ci niezmiernie wdzięczny za pomoc!:)

Pzdr.
Marogo

0
reichel napisał(a)
      Result := CreateDIBitmap(DC, BitmapInfo^.bmiHeader,  CBM_INIT, BitsMem,  BitmapInfo^, DIB_RGB_COLORS);

Chciałem jeszcze dodać, że wcześniej coś kombinowałem z tym CreateDIBitmap, ale nie zadziałało.

Tak poza tym, to masz bardzo fajne użytkowe programiki na swojej stronie.

Jeszcze raz wielkie dzięki za szybką pomoc [browar]

Pzdr.
Marogo

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