Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Grafik beim Drucken verzerrt
Sw4t
myCSharp.de - Member



Dabei seit:
Beiträge: 16

Themenstarter:

Grafik beim Drucken verzerrt

beantworten | zitieren | melden

Ich möchte eine selbstgezeichnete Grafik auf eine A4 Seite anpassen und ausdrucken
Wenn die Grafik über das Maß hinaus geht, soll sie verkleinert werden, ansonsten soll die Größe übernommen werden.
Zum Skalieren der Grafik benutze ich die Graphics.DrawImage() Methode.
Dabei entsteht die unerwünschte Verzehrung, die Linien unterschiedlich dick darstellt, siehe Dateianhang.


// VERZEHRUNG
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    using (Bitmap b = new Bitmap(_grid.Width, _grid.Height))
    {
        using (Graphics g = Graphics.FromImage(b))
        {
            _grid.Paint(g); 
            e.Graphics.DrawImage(b, 0, 0);
            // e.Graphics.DrawImage(b, 0, 0, 400, 400);
            b.Save(_path, System.Drawing.Imaging.ImageFormat.Png);
        }
    }
}

// KEINE VERZEHRUNG
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    _grid.Paint(e.Graphics);
}

Erst dachte ich, es läge am Bitmap-Objekt und habe jene Grafik ohne optische Probleme abgespeichert, siehe Dateianhang.
Im Anschluss kommentierte ich jene DrawImage() Zeile aus und versuchte - ohne die Breite und Höhe zu verändern - die Grafik 1:1 an das Drucker-Ereignis weiterzuleiten, mit entsprechenden Erfolgen, siehe Code.
Meines Erachtens liegt mein Problem an der DrawImage() Methode selbst.
Welche Alternative bzw. Herangehensweise gäbe es dazu?
Attachments
private Nachricht | Beiträge des Benutzers
chilic
myCSharp.de - Experte



Dabei seit:
Beiträge: 2.106

beantworten | zitieren | melden

Der auskommentierte Code zeichnet das Bild mit Höhe und Breite 400, soll das so sein? Falls es nicht quadratisch ist wird es dadurch verzerrt. (Mahlzeit *g* )
Von der Vorgabe "wenn größer soll es verkleinert werden" ist in deinem Code nichts zu sehen.
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von chilic am .
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4.389

beantworten | zitieren | melden

Hast du mal zum (Gegen-)Test DrawImageUnscaled verwendet?

Aber probiere mal einen besseren InterpolationMode zu setzen.
private Nachricht | Beiträge des Benutzers
Sw4t
myCSharp.de - Member



Dabei seit:
Beiträge: 16

Themenstarter:

beantworten | zitieren | melden


//zeigt die dünneren Linien etwas besser an
Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; 
// Beide Methoden führen zu einer Verzehrung
Graphics.DrawImage(bmp, 0, 0);
Graphics.DrawImageUnscaled(bmp, 0, 0);

Als letzten Ausweg sehe ich eine zweite Paint-Methode zu schreiben, die einen Skalierfaktor beinhaltet.
Damit sollte es angemessen erscheinen, siehe Dateianhang.


private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    _grid.Paint(e.Graphics);
}

Wie handhabt Windows einen direkten Druckauftrag eines Bildes, samt der Skalierung und Ausrichtung? Mit der Windows Imaging Component?
Attachments
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.928

beantworten | zitieren | melden

GDI+ war nie konzipiert, dass das 1:1 in den Drucker geht, egal ob nun GDI+ via .NET oder C++ oder oder oder...

Der beste Built-In Weg wird wohl immer noch das PrintDocument sein.
Ansonsten muss man schon sagen, dass die Drittanbieter-Angebote, vor allem bei komplexeren Dingen, in diesem Fall schon ihre Daseinsberechtigung haben.

PS: Es heisst verzerren / verzerrt. Verzehren komm von Essen, dass man etwas isst.
Ich habs im Titel korrigiert, sodass man das Thema wenigstens mit der korrekten Schreibweise über die Suche findet.
private Nachricht | Beiträge des Benutzers
Sw4t
myCSharp.de - Member



Dabei seit:
Beiträge: 16

Themenstarter:

beantworten | zitieren | melden

wie lustig, dass mir das "Verzehren" nicht aufgefallen ist. Danke!
Externe Funktionen wie StretchBlt() und TransparentBlt() skalieren optisch ansprechender als Graphics.DrawImage(), ABER ... es mogelt sich ein grauer Hintergrund (RGB[211,211,211]) hinzu, den ich nicht wegbekomme.
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4.389

beantworten | zitieren | melden

Was ist _grid für ein Control? Entweder dort dessen BackgroundColor ändern oder Graphics.Clear mit Color.Transparent (oder Color.White) aufrufen.
private Nachricht | Beiträge des Benutzers
Sw4t
myCSharp.de - Member



Dabei seit:
Beiträge: 16

Themenstarter:

beantworten | zitieren | melden

Die Klasse Grid ist noch kein Control, vielmehr eine Testklasse für ein übergeordnetes Projekt, das auf ein Control hinauslaufen wird.
Es beinhaltet auch eine BackColor-Eigenschaft, die die Hintergrundfarbe des darunterliegenden Parent-Controls setzt.
Also habe ich folgendes versucht:


private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    using (Bitmap b = new Bitmap(_grid.Width, _grid.Height))
    {
        using (Graphics g = Graphics.FromImage(b))
        {
             _grid.Paint(g);

            IntPtr hdcDest = e.Graphics.GetHdc();
            IntPtr hdcSrc = g.GetHdc();
            IntPtr hBitmap = b.GetHbitmap();

            ExternFunctions.SelectObject(hdcSrc, hBitmap);
            ExternFunctions.SetStretchBltMode(hdcDest, ExternFunctions.StretchMode.STRETCH_HALFTONE);
            ExternFunctions.TransparentBlt(hdcDest, 0, 0, b.Width * 20, b.Height * 20, hdcSrc, 0, 0, b.Width, b.Height, (uint)(_grid.BackColor.ToArgb() & 0xFFFFFF));

            g.ReleaseHdc();
            e.Graphics.ReleaseHdc();
        }
    }
}


Und so erscheinen die freien Flächen grau, sogar dann, wenn ich die Hintergrundfarbe (hier Gelb) explizit übergebe.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Sw4t am .
Attachments
private Nachricht | Beiträge des Benutzers
david.m
myCSharp.de - Member



Dabei seit:
Beiträge: 135

beantworten | zitieren | melden

Wenn ich das richtig sehe wird erst eine Bitmap erstellt, welche dann auf das PrintDocument gezeichnet wird.
Warum der Umweg über das Bitmap und nicht direkt auf e.Graphics zeichnen?
private Nachricht | Beiträge des Benutzers
Sw4t
myCSharp.de - Member



Dabei seit:
Beiträge: 16

Themenstarter:

beantworten | zitieren | melden

Wie soll ich sonst die Original-Grafik, die im _grid.Paint() gezeichnet wird, skaliert an den Drucker übergeben?
private Nachricht | Beiträge des Benutzers
david.m
myCSharp.de - Member



Dabei seit:
Beiträge: 135

beantworten | zitieren | melden

PrintPageEventArgs bietet doch die Eigenschaften für die Seitengröße.
Diese musst Du entsprechende verwenden und umrechnen, für das Zeichnen der Linien.

Eine Bitmap hat eine andere DPI z.B. 96, als ein Drucker (z.B. 300 oder 600).

Siehe auch Beispiel PrintDocument.PrintPage Ereignis (System.Drawing.Printing)
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.928

beantworten | zitieren | melden

Zu den Grundlagen/Unterschieden zwischen Drucker DPI und Bildschirm / Grafik "DPI" siehe mein Beitrag in Maßstabsgetreu zeichnen und drucken
private Nachricht | Beiträge des Benutzers
Sw4t
myCSharp.de - Member



Dabei seit:
Beiträge: 16

Themenstarter:

beantworten | zitieren | melden

Zitat von david.m
PrintPageEventArgs bietet doch die Eigenschaften für die Seitengröße.
Wie könnte mir das Ändern der Seitengröße hier weiterhelfen?
Zitat von Abt
Zu den Grundlagen/Unterschieden zwischen Drucker DPI und Bildschirm / Grafik "DPI" siehe mein Beitrag in Maßstabsgetreu zeichnen und drucken
Mit dem Umrechnen von DPI in Zentimeter oder umgekehrt komm ich durchaus auf ein vernünftiges Druckergebnis.
Das gilt sowohl für den virtuellen XPS- als auch für meine HP-Drucker.
Attachments
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4.389

beantworten | zitieren | melden

Zum Skalieren kann auch einfach vorher Graphics.ScaleTransform aufgerufen werden, s.a. Verwenden der globalen Transformation.

Und danach dann _grid.Paint(e.Graphics) aufrufen (wenn du die Bitmap nicht benötigst, ansonsten die Bitmap entsprechend vorher vergrößern).
private Nachricht | Beiträge des Benutzers
david.m
myCSharp.de - Member



Dabei seit:
Beiträge: 135

beantworten | zitieren | melden

Ist ja schon ein Unterschied ob A4, A5, A3 oder andere Papiergrößen im Drucker liegen.

Ich habe mit Drucken ewig nichts mehr zu tun gehabt. aber die Einheiten sind wohl andere mm oder 100/inch sind dort üblich.

Wie Th69 geschrieben hat kann man die Transformation angeben um die Logik für das Zeichnen nur einer Einheit machen muss.
private Nachricht | Beiträge des Benutzers
Sw4t
myCSharp.de - Member



Dabei seit:
Beiträge: 16

Themenstarter:

beantworten | zitieren | melden

Zitat von Th69
Zum Skalieren kann auch einfach vorher Graphics.ScaleTransform aufgerufen werden, s.a. Verwenden der globalen Transformation.

Und danach dann _grid.Paint(e.Graphics) aufrufen (wenn du die Bitmap nicht benötigst, ansonsten die Bitmap entsprechend vorher vergrößern).

♪♫ Das ist die perfekte Lösung, das ist der perfekte Hinweis.
Mehr als Graphics.ScaleTransform() hat es nicht gebraucht.
private Nachricht | Beiträge des Benutzers
Sw4t
myCSharp.de - Member



Dabei seit:
Beiträge: 16

Themenstarter:

beantworten | zitieren | melden


// GRID Klasse
public void Print(Graphics g, int marginX, int marginY, int dpi)
{
    if (g == null)
        return;

    int pageWidth, pageHeight = 0;
    int maxWidth, maxHeight = 0;
    float f = 0;

    if (Width > Height) // Landscape
    {
        pageWidth = UnitCalc.MillimeterToPixel(297, dpi);
        pageHeight = UnitCalc.MillimeterToPixel(210, dpi);
    }
    else // Portrait
    {
        pageWidth = UnitCalc.MillimeterToPixel(210, dpi);
        pageHeight = UnitCalc.MillimeterToPixel(297, dpi);
    }

    maxWidth = pageWidth - 2 * marginX;
    maxHeight = pageHeight - 2 * marginY;

    f = Min(maxWidth / (float)Width, maxHeight / (float)Height);

    maxWidth = (int)(Width * f);
    maxHeight = (int)(Height * f);
    marginX = (int)((pageWidth - maxWidth) / 2);
    marginY = (int)((pageHeight - maxHeight) / 2);

    if (UseBackColor)
        using (SolidBrush br = new SolidBrush(BackColor))
            g.FillRectangle(br, marginX, marginY, maxWidth, maxHeight);

    g.ScaleTransform(f, f);
    DrawCells(g, (int)(marginX / f), (int)(marginY / f));
    DrawGrid(g, (int)(marginX / f), (int)(marginY / f));
}

// FORM Klasse
private void tsmiPrint_Click(object sender, EventArgs e)
{
    printDialog1.Document = printDocument1;

    if (printDialog1.ShowDialog() == DialogResult.OK)
    {
        printDocument1.DefaultPageSettings.Landscape = _grid.Width > _grid.Height;
        printDocument1.Print();
    }
}

private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    _grid.Print(e.Graphics, 30, 30, 100);
}
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Sw4t am .
Attachments
private Nachricht | Beiträge des Benutzers