Laden...

Grafik beim Drucken verzerrt

Erstellt von Sw4t vor einem Jahr Letzter Beitrag vor einem Jahr 1.296 Views
S
Sw4t Themenstarter:in
25 Beiträge seit 2022
vor einem Jahr
Grafik beim Drucken verzerrt

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?

C
2.121 Beiträge seit 2010
vor einem Jahr

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.

4.931 Beiträge seit 2008
vor einem Jahr

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

Aber probiere mal einen besseren InterpolationMode zu setzen.

S
Sw4t Themenstarter:in
25 Beiträge seit 2022
vor einem Jahr

//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?

16.807 Beiträge seit 2008
vor einem Jahr

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.

S
Sw4t Themenstarter:in
25 Beiträge seit 2022
vor einem Jahr

😁 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.

4.931 Beiträge seit 2008
vor einem Jahr

Was ist _grid für ein Control? Entweder dort dessen BackgroundColor ändern oder Graphics.Clear mit Color.Transparent (oder Color.White) aufrufen.

S
Sw4t Themenstarter:in
25 Beiträge seit 2022
vor einem Jahr

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.

D
152 Beiträge seit 2013
vor einem Jahr

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?

S
Sw4t Themenstarter:in
25 Beiträge seit 2022
vor einem Jahr

Wie soll ich sonst die Original-Grafik, die im _grid.Paint() gezeichnet wird, skaliert an den Drucker übergeben?

D
152 Beiträge seit 2013
vor einem Jahr

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)

16.807 Beiträge seit 2008
vor einem Jahr

Zu den Grundlagen/Unterschieden zwischen Drucker DPI und Bildschirm / Grafik "DPI" siehe mein Beitrag in Maßstabsgetreu zeichnen und drucken

S
Sw4t Themenstarter:in
25 Beiträge seit 2022
vor einem Jahr


>
bietet doch die Eigenschaften für die Seitengröße.

Wie könnte mir das Ändern der Seitengröße hier weiterhelfen?

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.

4.931 Beiträge seit 2008
vor einem Jahr

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).

D
152 Beiträge seit 2013
vor einem Jahr

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.

S
Sw4t Themenstarter:in
25 Beiträge seit 2022
vor einem Jahr

Zum Skalieren kann auch einfach vorher Graphics.ScaleTransform aufgerufen werden, s.a.
>
.

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.

S
Sw4t Themenstarter:in
25 Beiträge seit 2022
vor einem Jahr

// 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);
}