2015-05-22 6 views
8

Abbiamo riscontrato un problema con l'output di TMetaFileCanvas quando si disegna un'immagine su una coordinata esterna alla risoluzione dello schermo. Le operazioni vettoriali sembrano non avere problemi, ma le operazioni di immagine sono semplicemente "ignorate". Se disegniamo la stessa immagine su una coordinata all'interno dei limiti dello schermo, non ci sono problemi.Copia di un elemento grafico su TMetaFileCanvas all'esterno delle dimensioni dello schermo

Ad esempio. Questo SSCCE produrrà 4 file di output. La variante bitmap non ha problemi e verrà visualizzata come previsto con il quadrato rosso nell'angolo in alto a sinistra per inscreen.bmp e il quadrato rosso nell'angolo in basso a destra per outsidescreen.bmp. Il meta file inscreen.emf funziona come previsto con il quadrato rosso disegnato nell'angolo in alto a sinistra. outsidescreen.emf non funziona e viene disegnata solo la linea.

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. 

Qualcuno può vedere qual è il problema o sai di un lavoro in giro per questo?

Aggiornamento

, avrei compreso questo quando ho inizialmente fatto la domanda. Abbiamo testato utilizzando l'HDC per una bitmap di grandi dimensioni e che presentava lo stesso problema. Ho aggiornato il codice di esempio per dimostrarlo.

Aggiornamento 2

Purtroppo la soluzione è ancora sfuggente, anche dopo la generosità. Qualsiasi operazione BitBlt al di fuori della dimensione dello schermo non viene disegnata.

Ecco un'estrazione delle operazioni Metafile quando l'immagine è nei limiti dello schermo coordinate:

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} 

Ecco un'estrazione delle operazioni Metafile quando l'immagine è fuori dai limiti delle coordinate dello schermo :

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} 

Si può vedere molto chiaramente che manca l'operazione BilBlt (R0025 nella prima).

risposta

6

Si sta creando TMetaFileCanvas con il parametro ReferenceDevice impostato a 0, in modo che sarà impostato ReferenceDevice al HDC da GetDC(0), vale a dire lo schermo. ReferenceDevice viene utilizzato per ottenere la risoluzione e le funzionalità utilizzate durante il disegno EMF. Ad esempio, quando le dimensioni di TMetaFile sono vuote, TMetaFileCanvas utilizza le dimensioni di ReferenceDevice. TMetaFileCanvas crea quindi un HDC per se stesso che ha un rettangolo di delimitazione basato sulle dimensioni del TMetaFile o ReferenceDevice, a seconda di quale fosse valido.

Quindi, per ovviare al problema, fornire un ReferenceDevice di dimensioni sufficienti per gestire il disegno. È possibile eseguire il dimensionamento delle dimensioni TMetaFile alla dimensione massima desiderata prima di creare lo TMetaFileCanvas, ma è probabile che sia necessario creare un valore TBitmap della dimensione massima desiderata e utilizzare lo Canvas.Handle come ReferenceDevice invece di utilizzare lo schermo.

Internamente, TCanvas.StretchDraw() chiama solo TGraphic.Draw(). TMetaFile.Draw() "riproduce" il metafile sulla tela di destinazione HDC.Quando quello HDC è quello creato da TMetaFileCanvas, non è possibile disegnare al di fuori delle dimensioni assegnate a tale TMetaFileCanvas.

+0

Abbiamo provato. Avrei dovuto includerlo nella domanda. Aggiornerò la domanda con un esempio di codice utilizzando una bitmap di grandi dimensioni che presenta lo stesso problema. – Graymatter

+0

Bounty assegnato a questa risposta. Penso che la risposta risieda nel dispositivo di riferimento che deve essere creato o dobbiamo aspettare una correzione di Windows al problema. – 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

Grazie, questa risposta sembra essere corretta. Stiamo solo verificando alcune cose e segnerò la risposta non appena avrò confermato. – Graymatter

+1

Ok, questo non funziona correttamente. Questo sta ancora usando un'operazione vettoriale, quindi il problema rimane. La domanda originale usava un 'FillRect' in' GetMyMetaFile' che genera un'operazione BitBlt nel meta file. Questo codice usa 'Rectangle' in' CreateMyMetafile', che è un'operazione vettoriale. – Graymatter

+0

Le regole della taglia AFAIK, metà della taglia verrà automaticamente assegnata poiché la risposta ha 2 (o più) voti. Ovviamente la risposta è stata sollevata da persone che non si sono accorte di non aver risolto il problema. Il down-voting o la revoca dovrebbero essere il corso naturale dell'azione ... –

Problemi correlati