2015-05-22 9 views
8

Mamy problem z wyjściem TMetaFileCanvas podczas rysowania obrazu do współrzędnej poza rozdzielczością ekranu. Operacje wektorowe wydają się nie mieć żadnych problemów, ale operacje obrazowe są po prostu "ignorowane". Jeśli narysujemy ten sam obraz do współrzędnych w granicach ekranu, nie ma problemów.Kopiowanie grafiki na TMetaFileCanvas poza wymiarami ekranu

Na przykład. To SSCCE wytworzy 4 pliki wyjściowe. Wariant bitmapy nie ma żadnych problemów i będzie działał zgodnie z oczekiwaniami z czerwonym kwadratem w lewym górnym rogu dla inscreen.bmp i czerwonym kwadratem w prawym dolnym rogu dla outsidescreen.bmp. Plik meta inscreen.emf działa zgodnie z oczekiwaniami z czerwonym kwadratem narysowanym w lewym górnym rogu. outsidescreen.emf nie działa i rysowana jest tylko linia.

program Project6; 

{$APPTYPE CONSOLE} 

{$R *.res} 

uses 
    System.SysUtils, 
    System.Types, 
    Windows, 
    Vcl.Graphics; 

const 
    SIZECONST = 3000; // should be larger than your screen resolution 
    OFFSET = 1500; 

    function GetMyMetafile(const aHDC: HDC): TMetafile; 
    var 
    metcnv: TMetafileCanvas; 
    begin 
    Result := TMetafile.Create; 
    Result.SetSize(500, 500); 

    metcnv := TMetafileCanvas.Create(Result, aHDC); 
    metcnv.Brush.Color := clRed; 
    metcnv.FillRect(Rect(0, 0, 500, 500)); 
    metcnv.Free; 
    end; 

    procedure OutputToMetaFile(const aFilename: string; const aStartOffset, 
     aEndOffset, aMaxSize: Integer; aGraphic: TGraphic; aHDC: HDC); 
    var 
    metafile: TMetafile; 
    metcnv: TMetafileCanvas; 
    begin 
    metafile := TMetafile.Create; 
    try 
     metafile.SetSize(aMaxSize, aMaxSize); 

     metcnv := TMetafileCanvas.Create(metafile, aHDC); 
     try 
     // draw it somewhere offscreen 
     metcnv.StretchDraw(Rect(aStartOffset, aStartOffset, aEndOffset, aEndOffset), aGraphic); 
     metcnv.MoveTo(aStartOffset, aStartOffset); 
     metcnv.LineTo(aEndOffset, aEndOffset); 
     finally 
     metcnv.Free; 
     end; 

     metafile.SaveToFile(aFilename); 
    finally 
     metafile.Free; 
    end; 
    end; 

    procedure OutputToBitmap(const aFilename: string; const aStartOffset, 
     aEndOffset, aMaxSize: Integer; aGraphic: TGraphic); 
    var 
    bmp: TBitmap; 
    begin 
    bmp := TBitmap.Create; 
    try 
     bmp.SetSize(aMaxSize, aMaxSize); 

     bmp.Canvas.StretchDraw(Rect(aStartOffset, aStartOffset, aEndOffset, aEndOffset), aGraphic); 
     bmp.Canvas.MoveTo(aStartOffset, aStartOffset); 
     bmp.Canvas.LineTo(aEndOffset, aEndOffset); 

     bmp.SaveToFile(aFilename); 
    finally 
     bmp.Free; 
    end; 
    end; 

var 
    mygraph: TMetafile; 
    bigBitmap: TBitmap; 
begin 
    bigBitmap := TBitmap.Create; 
    try 
    bigBitmap.PixelFormat := pf24bit; 
    Assert(bigBitmap.HandleType = bmDIB, 'Handle Type should be a DIB'); 
    bigBitmap.Width := SIZECONST; 
    bigBitmap.Height := SIZECONST; 
    mygraph := GetMyMetafile(bigBitmap.Canvas.Handle); 
    OutputToMetaFile('inscreen.emf', 0, 1000, SIZECONST, mygraph, bigBitmap.Canvas.Handle); 
    OutputToMetaFile('outsidescreen.emf', OFFSET, SIZECONST-1, SIZECONST, mygraph, bigBitmap.Canvas.Handle); 

    // do the same using bitmap 
    OutputToBitmap('inscreen.bmp', 0, 1000, SIZECONST, mygraph); 
    OutputToBitmap('outsidescreen.bmp', OFFSET, SIZECONST-1, SIZECONST, mygraph); 
    finally 
    bigBitmap.Free; 
    mygraph.Free; 
    end; 
end. 

Czy ktoś może zobaczyć, na czym polega problem lub czy znasz jakieś obejście tego problemu?

Aktualizacja

powinno mam włączone to kiedy pierwotnie zadawane pytanie. Testowaliśmy przy użyciu HDC dla dużej bitmapy i wyświetliliśmy ten sam problem. Zaktualizowałem przykładowy kod, aby to pokazać.

Aktualizacja 2

Niestety rozwiązanie jest wciąż nieuchwytny nawet po dobroci. Każda operacja BitBlt poza rozmiarem ekranu nie jest rysowana.

Oto ekstrakcja operacji MetaFile gdy obraz jest w granicach ekranu koordynuje:

R0001: [001] EMR_HEADER (s=108) {{ Bounds(500,500,18138,18129), Frame(0,0,105000,105000), ver(0x10000), size(688), recs(33), handles(2), pals(0), dev_pix(1080,1920), dev_mil(381,677), pixf_size(0), pixf_ofs(0), openGL(0) }} 
R0002: [033] EMR_SAVEDC (s=8) 
R0003: [115] EMR_SETLAYOUT (s=12) {iMode(0=<default>)} 
R0004: [028] EMR_SETMETARGN (s=8) 
R0005: [037] EMR_SELECTOBJECT (s=12) {Stock object: 0=OBJ_BRUSH.(BS_SOLID)} 
R0006: [037] EMR_SELECTOBJECT (s=12) {Stock object: 7=OBJ_PEN.(PS_SOLID | COSMETIC)} 
R0007: [037] EMR_SELECTOBJECT (s=12) {Stock object: 14=OBJ_FONT} 
R0008: [025] EMR_SETBKCOLOR (s=12) {0x00FFFFFF} 
R0009: [024] EMR_SETTEXTCOLOR (s=12) {0x00000000} 
R0010: [018] EMR_SETBKMODE (s=12) {iMode(2=OPAQUE)} 
R0011: [019] EMR_SETPOLYFILLMODE (s=12) {iMode(1=ALTERNATE)} 
R0012: [020] EMR_SETROP2 (s=12) {iMode(13=R2_COPYPEN)} 
R0013: [021] EMR_SETSTRETCHBLTMODE (s=12) {iMode(1=BLACKONWHITE)} 
R0014: [022] EMR_SETTEXTALIGN (s=12) {iMode(0= TA_LEFT TA_TOP)} 
R0015: [013] EMR_SETBRUSHORGEX (s=16) {ptlOrigin(0,0)} 
R0016: [058] EMR_SETMITERLIMIT (s=12) {Limit:0.000} 
R0017: [027] EMR_MOVETOEX (s=16) { ptl(0,0)} 
R0018: [035] EMR_SETWORLDTRANSFORM (s=32) {xform(eDx:500.000000, eDy:500.000000, eM11:5.039683, eM12:0.000000, eM21:0.000000, eM22:5.037203)} 
R0019: [036] EMR_MODIFYWORLDTRANSFORM (s=36) {iMode(4=MWT_??), xform(eDx:500.000000, eDy:500.000000, eM11:5.039683, eM12:0.000000, eM21:0.000000, eM22:5.037203)} 
R0020: [115] EMR_SETLAYOUT (s=12) {iMode(0=<default>)} 
R0021: [070] EMR_GDICOMMENT (s=40) {GDI.Begin Group} 
R0022: [039] EMR_CREATEBRUSHINDIRECT (s=24) {ihBrush(1), style(0=BS_SOLID, color:0x000000FF)} 
R0023: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)} 
R0024: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)} 
R0025: [076] EMR_BITBLT (s=100) {rclBounds(500,500,18138,18129), Dest[x:0, y:0, cx:3500, cy:3500)], dwRop(0x00F00021), Src[x:0, y:0, xform(eDx:0.000000, eDy:0.000000, eM11:1.000000, eM12:0.000000, eM21:0.000000, eM22:1.000000), BkColor:0x00000000, iUsage:0, offBmi:0, Bmi:0, offBits:0, Bits:0]} 
R0026: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)} 
R0027: [037] EMR_SELECTOBJECT (s=12) {Stock object: 0=OBJ_BRUSH.(BS_SOLID)} 
R0028: [040] EMR_DELETEOBJECT (s=12) {ihObject(1)} 
R0029: [070] EMR_GDICOMMENT (s=20) {GDI.End Group} 
R0030: [034] EMR_RESTOREDC (s=12) {iRelative(-1)} 
R0031: [027] EMR_MOVETOEX (s=16) { ptl(500,500)} 
R0032: [054] EMR_LINETO (s=16) { ptl(1000,1000)} 
R0033: [014] EMR_EOF (s=20) {nPalEntries:0, offPalEntries:16, nSizeLast:20} 

Oto ekstrakcja operacji MetaFile gdy obraz jest poza granicami współrzędnych ekranu :

R0001: [001] EMR_HEADER (s=108) {{ Bounds(1500,1500,2999,2999), Frame(0,0,105000,105000), ver(0x10000), size(588), recs(32), handles(2), pals(0), dev_pix(1080,1920), dev_mil(381,677), pixf_size(0), pixf_ofs(0), openGL(0) }} 
R0002: [033] EMR_SAVEDC (s=8) 
R0003: [115] EMR_SETLAYOUT (s=12) {iMode(0=<default>)} 
R0004: [028] EMR_SETMETARGN (s=8) 
R0005: [037] EMR_SELECTOBJECT (s=12) {Stock object: 0=OBJ_BRUSH.(BS_SOLID)} 
R0006: [037] EMR_SELECTOBJECT (s=12) {Stock object: 7=OBJ_PEN.(PS_SOLID | COSMETIC)} 
R0007: [037] EMR_SELECTOBJECT (s=12) {Stock object: 14=OBJ_FONT} 
R0008: [025] EMR_SETBKCOLOR (s=12) {0x00FFFFFF} 
R0009: [024] EMR_SETTEXTCOLOR (s=12) {0x00000000} 
R0010: [018] EMR_SETBKMODE (s=12) {iMode(2=OPAQUE)} 
R0011: [019] EMR_SETPOLYFILLMODE (s=12) {iMode(1=ALTERNATE)} 
R0012: [020] EMR_SETROP2 (s=12) {iMode(13=R2_COPYPEN)} 
R0013: [021] EMR_SETSTRETCHBLTMODE (s=12) {iMode(1=BLACKONWHITE)} 
R0014: [022] EMR_SETTEXTALIGN (s=12) {iMode(0= TA_LEFT TA_TOP)} 
R0015: [013] EMR_SETBRUSHORGEX (s=16) {ptlOrigin(0,0)} 
R0016: [058] EMR_SETMITERLIMIT (s=12) {Limit:0.000} 
R0017: [027] EMR_MOVETOEX (s=16) { ptl(0,0)} 
R0018: [035] EMR_SETWORLDTRANSFORM (s=32) {xform(eDx:1500.000000, eDy:1500.000000, eM11:15.108969, eM12:0.000000, eM21:0.000000, eM22:15.101533)} 
R0019: [036] EMR_MODIFYWORLDTRANSFORM (s=36) {iMode(4=MWT_??), xform(eDx:1500.000000, eDy:1500.000000, eM11:15.108969, eM12:0.000000, eM21:0.000000, eM22:15.101533)} 
R0020: [115] EMR_SETLAYOUT (s=12) {iMode(0=<default>)} 
R0021: [070] EMR_GDICOMMENT (s=40) {GDI.Begin Group} 
R0022: [039] EMR_CREATEBRUSHINDIRECT (s=24) {ihBrush(1), style(0=BS_SOLID, color:0x000000FF)} 
R0023: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)} 
R0024: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)} 
R0025: [037] EMR_SELECTOBJECT (s=12) {Table object: 1=OBJ_BRUSH.(BS_SOLID)} 
R0026: [037] EMR_SELECTOBJECT (s=12) {Stock object: 0=OBJ_BRUSH.(BS_SOLID)} 
R0027: [040] EMR_DELETEOBJECT (s=12) {ihObject(1)} 
R0028: [070] EMR_GDICOMMENT (s=20) {GDI.End Group} 
R0029: [034] EMR_RESTOREDC (s=12) {iRelative(-1)} 
R0030: [027] EMR_MOVETOEX (s=16) { ptl(1500,1500)} 
R0031: [054] EMR_LINETO (s=16) { ptl(2999,2999)} 
R0032: [014] EMR_EOF (s=20) {nPalEntries:0, offPalEntries:16, nSizeLast:20} 

Widać bardzo wyraźnie, że brakuje operacji BilBlt (R0025 w pierwszym).

Odpowiedz

6

Tworzycie TMetaFileCanvas z parametrem ReferenceDevice ustawiony na 0, więc będzie ustawiony ReferenceDevice do HDC z GetDC(0), czyli ekranu. ReferenceDevice służy do uzyskiwania rozdzielczości i możliwości używanych podczas rysowania pola elektromagnetycznego. Na przykład, gdy wymiary TMetaFile są puste, TMetaFileCanvas używa wymiarów ReferenceDevice. TMetaFileCanvas następnie tworzy dla siebie HDC, który ma prostokąt ograniczający na podstawie wymiarów TMetaFile lub ReferenceDevice, w zależności od tego, który z nich był prawidłowy.

W celu obejścia problemu należy podać numer ReferenceDevice, który jest wystarczająco duży, aby obsłużyć rysunek. Możesz wstępnie dopasować wymiary TMetaFile do wymaganego maksymalnego rozmiaru przed utworzeniem TMetaFileCanvas, ale prawdopodobnie będziesz musiał utworzyć TBitmap o żądanym maksymalnym rozmiarze i użyć jego Canvas.Handle jako ReferenceDevice zamiast używać ekranu.

Wewnętrznie, TCanvas.StretchDraw() po prostu dzwoni TGraphic.Draw(). TMetaFile.Draw() "odtwarza" metaplik na docelowej kanwie HDC.Gdy ten HDC jest utworzony przez TMetaFileCanvas, nie można rysować poza wymiarami przypisanymi do tego TMetaFileCanvas.

+0

Próbowaliśmy tego. Powinienem był to uwzględnić w pytaniu. Zaktualizuję pytanie za pomocą przykładu kodu, używając dużej bitmapy, która wykazuje ten sam problem. – Graymatter

+0

Bounty przyznane za tę odpowiedź. Myślę, że odpowiedź leży albo w urządzeniu referencyjnym, które trzeba utworzyć, albo musimy czekać na naprawę systemu Windows. – Graymatter

2
program Project1; 
{$APPTYPE CONSOLE} 

uses 
SysUtils, Types, Windows, Graphics; 

const 
SIZECONST = 3000; // should be larger than your screen resolution 
OFFSET = 1500; 
var 
//holds millimeter per pixel ratios 
MMPerPixelHorz, 
MMPerPixelVer: Integer; 

procedure CreateMyMetafile(var HmyGraphic: HENHMETAFILE; aHDC: HDC); 
var 
    R: Trect; 
    TheBrush: HBRUSH; 
    OldBrush: HBRUSH; 
    MetafileDC: HDC; 
begin 
    R:= Rect(0, 0, 100*MMPerPixelHorz, 100*MMPerPixelVer); 
    MetafileDC:= CreateEnhMetaFile(aHDC, 'myGraphic.emf', @R, nil); 

    TheBrush:=CreateSolidBrush(RGB(255, 0, 0)); 
    OldBrush:=SelectObject(MetafileDC, TheBrush); 

    Rectangle(MetafileDC, r.Left, r.Top, r.Right, r.Bottom); 

    SelectObject(MetafileDC, OldBrush); 
    DeleteObject(TheBrush); 
    HmyGraphic:=CloseEnhMetaFile(MetafileDC); 
end; 

procedure OutputToMetaFile(const aFilename: string; const aStartOffset, 
    aEndOffset, aMaxSize: Integer; aHDC: HDC); 
var 
    r: Trect; 

ReferenceRect: TRect; 
MetafileDC: HDC; 
HMetaFile, HMetaMyGraphic: HENHMETAFILE; {EMF file handle} 
begin 
//create our reference rectangle for the metafile 
ReferenceRect:= Rect(0, 0, aMaxSize * MMPerPixelHorz, aMaxSize * MMPerPixelVer); 

//Create First EnhMetaFile 
CreateMyMetafile(HMetaMyGraphic, aHDC); 

MetafileDC:=CreateEnhMetaFile(aHDC, pchar(aFilename),@ReferenceRect, nil); 
//SetMapMode(MetafileDC, MM_ANISOTROPIC); 

try 
    r:= Rect(aStartOffset, aStartOffset, aEndOffset, aEndOffset); 
    PlayEnhMetaFile (MetaFileDC, HMetaMyGraphic, r); 

    MoveToEx(MetafileDC, aStartOffset, aStartOffset, nil); 
    LineTo(MetafileDC, aEndOffset, aEndOffset); 
    HMetaFile:=CloseEnhMetaFile(MetafileDC); 

finally 
    DeleteEnhMetaFile (HMetaFile); 
    DeleteEnhMetaFile (HMetaMyGraphic); 
end; 
end; 

var 
WidthInMM, 
HeightInMM, 
WidthInPixels, 
HeightInPixels: Integer; 

bigBitmap: TBitmap; 
mHDC: HDC; 
begin 
    bigBitmap := TBitmap.Create; 
    try 
    bigBitmap.PixelFormat := pf24bit; 
    Assert(bigBitmap.HandleType = bmDIB, 'Handle Type should be a DIB'); 
    bigBitmap.Width := SIZECONST; 
    bigBitmap.Height := SIZECONST; 
    mHDC:= bigBitmap.Canvas.Handle; 

    //retrieve the size of the screen in millimeters 
    WidthInMM:=GetDeviceCaps(mHDC, HORZSIZE); 
    HeightInMM:=GetDeviceCaps(mHDC, VERTSIZE); 

    //retrieve the size of the screen in pixels 
    WidthInPixels:=GetDeviceCaps(mHDC, HORZRES); 
    HeightInPixels:=GetDeviceCaps(mHDC, VERTRES); 

    MMPerPixelHorz:=(WidthInMM * 100) div WidthInPixels; 
    MMPerPixelVer:=(HeightInMM * 100) div HeightInPixels; 

    OutputToMetaFile('inscreen.emf', 500, 1000, SIZECONST, mHDC); 
    OutputToMetaFile('outsidescreen.emf', OFFSET, SIZECONST-1, SIZECONST, mHDC); 
finally 
    bigBitmap.Free; 
end; 
end. 
+0

Dzięki, ta odpowiedź wygląda na poprawną. Sprawdzamy tylko kilka rzeczy i zaznaczę odpowiedź, gdy tylko potwierdzę. – Graymatter

+1

OK, to nie działa poprawnie. To wciąż używa operacji wektorowej, więc problem pozostaje. Pierwotne pytanie używało 'FillRect' w' GetMyMetaFile', które generuje operację BitBlt w pliku meta. Ten kod używa 'Rectangle' w' CreateMyMetafile', który jest operacją wektorową. – Graymatter

+0

Zasady AFAIK bounty, połowa nagrody zostanie przyznana automatycznie, ponieważ odpowiedź ma 2 (lub więcej) głosów. Oczywiście odpowiedź została przegłosowana przez osoby, które nie zauważyły, że nie rozwiązały problemu. Głosowanie w dół lub odwołanie powinno być naturalnym sposobem działania ... –