Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Resample oder Resize mit GDI+ (https://www.delphipraxis.net/156268-resample-oder-resize-mit-gdi.html)

Schwedenbitter 25. Nov 2010 16:28

Resample oder Resize mit GDI+
 
Hallo,

ich möchte gern größere Bilder mithilfe der Funktionen aus der GDI+ -Bibliothek verkleinern. Leider habe ich keine Ahnung, wie ich das so recht anstellen soll. Auch möchte ich, um Resourcen zu schonen, auf fertige Units verzichten.
Ich bin soweit, dass ich die GDI+ initialisiert, Bitmaps angelegt und deren Speicher wieder freigegeben habe. Ich finde aber nichts genaues, mit welchen Funktionen ich ein Resample erreichen kann.

Aufgrund meiner Recherchen vermute ich, dass ich die Größenänderung mit mit
Delphi-Quellcode:
GdipDrawImageRect()
hinbekommen könnte. Mein Plan sieht so aus:
  1. TBitmap (Source) habe ich schon.
  2. TBitmap (Destination) lege ich an
  3. Mit
    Delphi-Quellcode:
    GdipCreateBitmapFromHBITMAP()
    erzeuge ich ein GDI+konformes Image von Source
  4. Dasselbe mit Destination
  5. Problem 1: Bei welchem der beiden GDI+ Images muss ich jetzt mit
    Delphi-Quellcode:
    GdipSetInterpolationMode()
    den Modus festlegen, damit es etwas wird? Oder muss ich das für beide GDI+ Images machen?
  6. Problem 2: Wie Resample ich?
  7. Problem 3: Bekomme ich das Ergebnis mit
    Delphi-Quellcode:
    GdipCreateHBITMAPFromBitmap()
    wieder zurück?
  8. Beide GDI+ Images mit
    Delphi-Quellcode:
    GdipDisposeImage()
    wieder freigeben

Ich denke auch, dass ich kein zweites TBitmap brauchte. Ich finde aber keine Funktion, mit der ich einfach so ohne Bezug zur (normalen) GDI ein GDI+ Bitmap erstellen kann. Auch insoweit wäre ich für Hilfe dankbar.
Ich möchte das ganze später in einem von TBitmap abgeleiteten Object unterbringen und würde mir gern das Hilfsbitmap sparen.

Gruß, Alex

xaromz 25. Nov 2010 16:40

AW: Resample oder Resize mit GDI+
 
Hallo,

erstens würde ich Dir dringend dazu raten, fertige Klassen für GDI+ zu verwenden, z. B. die von Progdigy. Damit ersparst Du Dir jede Menge Ärger, und die Größe Deines Programms sollte doch heutzutage nicht mehr ausschlaggebend sein, oder?

Zweitens, So musst Du vorgehen:
  • Quellbitmap laden
  • Zielbitmap in der entsprechenden Größe erstellen
  • InterpolationMode des Zielbitmaps setzen
  • Quellbitmap in der entsprechenden Größe auf das Zielbitmap malen
Wie gesagt, mit den entsprechenden Units ist das ein Vierzeiler (Fünfzeiler mit Speichern).

//Edit:
Ach ja, ohne Zweitbitmap geht es nicht, und natürlich kannst Du auch einfach direkt ein GDI+ Bitmap erstellen.

Gruß
xaromz

Bummi 25. Nov 2010 16:43

AW: Resample oder Resize mit GDI+
 
Ich schließe mich meinem Vorredner an:thumb:

Schwedenbitter 25. Nov 2010 17:01

AW: Resample oder Resize mit GDI+
 
Danke. Das bringt mich schon einmal auf Kurs!

Ich möchte keine fertige Klasse verwenden. Und ja, die Größe spielt bei uns eine Rolle, weil mein fertiges Programm aus bestimmten Gründen regelmäßig neu geladen und gestartet werden muss. Bei uns hängen mehrere Leute an einem VPN. Wenn ich die Datei größer mache, gibt es mit denen Ärger.
Außerdem experimentiere ich gern :lol:

Zitat:

Zitat von xaromz (Beitrag 1064037)
Ach ja, ohne Zweitbitmap geht es nicht, und natürlich kannst Du auch einfach direkt ein GDI+ Bitmap erstellen.

Könntest Du das bitte genauer erklären? Dass ich ein zweites GDI+ Bitmap brauche, ist mir klar. Aber wenn ich das richtig verstehe, muss ich nicht zuvor ein zweites TBitmap erstellen und dem eigentlichen GDI+ Ziel-Image zuweisen, oder?
Könntest Du mir bitte die Funktion verraten. Das würde mir sehr helfen. Hier habe ich dazu leider nichts gefunden.

Dann würde ich mich noch freuen, wenn jemand bestätigen oder dementieren könnte, dass ich mit
Delphi-Quellcode:
GdipDrawImageRect()
die Größenänderung machen kann.
Zitat:

Zitat von xaromz (Beitrag 1064037)
Quellbitmap in der entsprechenden Größe auf das Zielbitmap malen

bringt mich im Moment da nicht ganz so weiter.

Danke, Alex

xaromz 25. Nov 2010 17:20

AW: Resample oder Resize mit GDI+
 
Hallo,

ein Bitmap bekommst Du mit
Delphi-Quellcode:
var bitmap: Pointer;
...
bitmap := nil;
lastResult := GdipCreateBitmapFromScan0(width, height, 0, pixelFormat, nil, bitmap);
Danach steckt in bitmap Dein GDI+ Bitmap.

Ein Bitmap malst Du mit
Delphi-Quellcode:
GdipDrawImageRect(graphics, soucre, x, y, width, height);
, wobei graphics das Graphikobjekt Deines Quellbitmaps ist.

Gruß
xaromz

Bummi 25. Nov 2010 17:23

AW: Resample oder Resize mit GDI+
 
ein Fetzen Code sagt mehr als 1000 Worte :)
muß nichts installiert werden

Delphi-Quellcode:
unit ExGraphicUtils;
//2010 Thomas Wassermann www.explido-software.de
interface
uses Windows, Classes, Sysutils, Graphics,GDIPAPI,GDIPOBJ, StdCtrls, jpeg, ActiveX;

procedure SetCanvasZoomFactor(Canvas: TCanvas; AZoomFactor: Integer);
Procedure SetCanvasZoomAndRotation(ACanvas:TCanvas;Zoom:Double;Angle:Double;CenterpointX,CenterpointY:Double);
Procedure ScaleImage(source:String;dest:TCanvas;DestRect:Trect;Center:Boolean=true);overload;
Procedure ScaleImage(source:TGraphic;dest:TCanvas;DestRect:Trect;Center:Boolean=true);overload;
function CreateGraphicFromFile(const Filename: string): TGraphic;
procedure MirrorBitmap(Bmp, MBmp: TBitmap;Horizonal:Boolean=true);
implementation


Procedure ScaleImage(source:String;dest:TCanvas;DestRect:Trect;Center:Boolean=true);overload;
var
  graphics : TGPGraphics;
  image: TGPImage;
  width, height: Integer;
  faktor:Double;
  X, Y:Double;
begin
  image:= TGPImage.Create(source);
  try
  width := image.GetWidth;
  height := image.GetHeight;
  if ((DestRect.Right - DestRect.Left) / width) < ((DestRect.Bottom -DestRect.Top)/Height) then faktor := (DestRect.Right - DestRect.Left) / width else faktor:= ((DestRect.Bottom -DestRect.Top)/Height);
  Faktor := ABS(Faktor);
  if Center then
      begin
        X := ((Destrect.Right - Destrect.Left) - faktor * width ) / 2;
        Y := ((Destrect.Bottom - Destrect.Top) - faktor * Height ) / 2;
      end
  else
      begin
        X := Destrect.Left;
        Y := Destrect.Top;

      end;
  graphics := TGPGraphics.Create(dest.Handle);
  try
    graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
    graphics.DrawImage( image, MakeRect(X, Y , faktor * width, faktor * height), 0, 0, width, height, UnitPixel);
  finally
    graphics.Free;
  end;
  finally
  image.Free;
  end;
end;

Procedure ScaleImage(source:TGraphic;dest:TCanvas;DestRect:Trect;Center:Boolean=true);overload;
var
  graphics : TGPGraphics;
  image: TGPImage;
  width, height: Integer;
  faktor:Double;
  STR : TMemoryStream;
  X, Y:Double;
begin
  STR := TMemoryStream.Create;
  source.SaveToStream(STR);
  STR.Position := 0;
  image:= TGPImage.Create(TStreamAdapter.Create(Str));
  try
  width := image.GetWidth;
  height := image.GetHeight;
  if ((DestRect.Right - DestRect.Left) / width) < ((DestRect.Bottom -DestRect.Top)/Height) then faktor := (DestRect.Right - DestRect.Left) / width else faktor:= ((DestRect.Bottom -DestRect.Top)/Height);
  Faktor := ABS(Faktor);
  if Center then
      begin
        X := ((Destrect.Right - Destrect.Left) - faktor * width ) / 2;
        Y := ((Destrect.Bottom - Destrect.Top) - faktor * Height ) / 2;
      end
  else
      begin
        X := Destrect.Left;
        Y := Destrect.Top;

      end;
  graphics := TGPGraphics.Create(dest.Handle);
  try
    graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
    graphics.DrawImage( image, MakeRect(X, Y , faktor * width, faktor * height), 0, 0, width, height, UnitPixel);
  finally
    graphics.Free;
  end;
  finally
  STR.Free;
  image.Free;
  end;
end;

EWeiss 26. Nov 2010 06:46

AW: Resample oder Resize mit GDI+
 
Schau hier mal rein ;)
GDIClock
Da mußt auch nichts installieren.

gruss

Schwedenbitter 26. Nov 2010 11:32

AW: Resample oder Resize mit GDI+
 
Zitat:

Zitat von EWeiss (Beitrag 1064190)
Schau hier mal rein ;)
GDIClock

Genau damit habe ich angefangen. Großes Lob im übrigen dafür. Zwar läuft die Uhr nicht bei mir (Zeiger und Untergrund passen nicht übereinander). Aber ich hatte das damals bestens für meine Experimente zum Drehen eines Bitmaps verwenden können und auch hier meine Anfänge gemacht.

Aus diesem Grunde bin ich ja auch dazu gekommen, dass ich keine fette Unit für das Resizen brauche, sondern mir das ganze selbst schlank und schnell zusammen bauen kann.
Aus schnell ist - wie man an diesem Thema sieht - nichts geworden. Aber ich habe keine andere Wahl. Denn im Moment mache ich das Scaling mit der GDI-Funktion (ohne "+")
Delphi-Quellcode:
StretchBlt()
und da leidet die Lesbarkeit unserer Dokumente erheblich. Beim Vergrößern (z.B. beim Ausdrucken mittels Printer.Canvas) ist das kein Problem. Aber wenn ich z.B. mit 300 oder 400 dpi eingescannte Bilder anwendungskonform auf 200 dpi bringen will, habe ich wirklich ein Problem.

DeddyH 26. Nov 2010 11:34

AW: Resample oder Resize mit GDI+
 
Hast Du mal versucht, vorher den StretchBltMode auf HALFTONE zu setzen? Damit habe ich recht gute Erfahrungen gemacht.

Schwedenbitter 26. Nov 2010 13:27

AW: Resample oder Resize mit GDI+
 
Zitat:

Zitat von DeddyH (Beitrag 1064258)
Hast Du mal versucht, vorher den StretchBltMode auf HALFTONE zu setzen? Damit habe ich recht gute Erfahrungen gemacht.

:duck: Wenn Du so direkt fragst: Nö!

Cool. Ich sehe grad, dass ich mich dann vermutlich bloß noch wegen des Speicherns als png-Datei mit GDI+ rumschlagen muss. Aber geschadet hat es trotzdem nichts und ich bleibe mal dran: womöglich sind die Ergebnisse mit GDI+ noch besser ;-)

Schwedenbitter 29. Nov 2010 09:55

AW: Resample oder Resize mit GDI+
 
Folgender Code funktioniert bei mir:
Delphi-Quellcode:
Procedure TBitmapEx.StretchGDI(Const NewWidth, NewHeight: Integer);
Var
  ScaleDown   : Boolean;
  W, H         : Integer;
  oldW, oldH   : Integer;
Begin
  If (self.Empty) Or
    ((NewWidth = 0) And (NewHeight = 0)) Or
    ((NewWidth = self.Width) And (NewHeight = self.Height)) Then
      Exit;

  W:=NewWidth;
  H:=NewHeight;
  oldW:=self.Width;
  oldH:=self.Height;

  If (W = 0) Then W:=H * self.Width Div self.Height;
  If (H = 0) Then H:=W * self.Height Div self.Width;

  ScaleDown:=True;
  If (W < self.Width) Or (H < self.Height) Then
    SetStretchBltMode(self.Canvas.Handle, Halftone)
  Else
  Begin
    ScaleDown:= False;
    self.Width:= W;
    self.Height:=H;
  End;
  StretchBlt(self.Canvas.Handle, 0, 0,   W,   H,
             self.Canvas.Handle, 0, 0, oldW, oldH,
             SRCCOPY);
  If (ScaleDown) Then
  Begin                                    
    self.Width:= W;
    self.Height:=H;
  End;
End;
Leider hatte ich am Wochenende wenig Zeit und den Code über GDI+ (noch) nicht zum Laufen bekommen. Ich scheitere im Moment daran, dass mir
Delphi-Quellcode:
GdipSetInterpolationMode(Dest, 4);
erklärt, ich würde falsche Parameter übergeben. Dabei ist
Delphi-Quellcode:
Type Dest: Pointer
und 4 sollte eigentlich
Delphi-Quellcode:
Type InterpolationModeBicubic: Integer
sein. Letztere sollte auch anstelle von
Delphi-Quellcode:
Cardinal
laufen, wie es wiederum in der uGDIUnit von Cardinal Emil Weiss definiert ist.

Dazu wieder 2 Fragen:
  1. Wenn GDI ohne zusätzliches Bitmap resizen kann, sollte das nicht auch mit GDI+ funktionieren? Dann brauchte ich doch kein
    Delphi-Quellcode:
    Var Source, Dest: Pointer
    , sondern es sollte dann einfach eines reichen.
  2. Wo ist der Unterschied zwischen
    Delphi-Quellcode:
    GdipDrawImageRect()
    und
    Delphi-Quellcode:
    GdipDrawImageRectI()
    ? Einfach nur, dass die eine Funktion mit Gleitkommazahlen und die andere mit Integer funktioniert?
  3. Wo ist bei den Funktionen unter 2. der Unterschied zwischen
    Delphi-Quellcode:
    Graphics
    und
    Delphi-Quellcode:
    Image
    ? Beides ist laut den Erläuterungen zu GDI+ von MS ein Pointer bzw. bei Emil Weiss Cardinal.

Gruß & Dank, Alex

xaromz 29. Nov 2010 10:42

AW: Resample oder Resize mit GDI+
 
Hallo,
Zitat:

Zitat von Schwedenbitter (Beitrag 1064735)
Dazu wieder 2 Fragen:
  1. Wenn GDI ohne zusätzliches Bitmap resizen kann, sollte das nicht auch mit GDI+ funktionieren? Dann brauchte ich doch kein
    Delphi-Quellcode:
    Var Source, Dest: Pointer
    , sondern es sollte dann einfach eines reichen.
  2. Wo ist der Unterschied zwischen
    Delphi-Quellcode:
    GdipDrawImageRect()
    und
    Delphi-Quellcode:
    GdipDrawImageRectI()
    ? Einfach nur, dass die eine Funktion mit Gleitkommazahlen und die andere mit Integer funktioniert?
  3. Wo ist bei den Funktionen unter 2. der Unterschied zwischen
    Delphi-Quellcode:
    Graphics
    und
    Delphi-Quellcode:
    Image
    ? Beides ist laut den Erläuterungen zu GDI+ von MS ein Pointer bzw. bei Emil Weiss Cardinal.

(1) Du kannst schon mit einem Bitmap arbeiten, wenn Du das Bitmap verkleinert auf sich selbst zeichnest. Ob das richtig funktioniert oder Bildfehler erzeugt, kann ich Dir aber nicht sagen, das habe ich nie ausprobiert.

(2) Genau, der Unterschied ist, dass Du einmal Float und einmal Integer verwenden kannst.

(3) Image und Graphics sind zwei völlig unterschiedliche Klassen. Das ist quasi Bitmap und Canvas in der VCL. Ein Image hat immer ein Graphics, welches zum Manipulieren der Bitmapdaten dient. Ich vermute, dass deshalb Deine Interpolation fehlschlägt, weil Du der Funktion das Image übergibst, und nicht das entsprechende Graphics-Objekt.

Gruß
xaromz

Schwedenbitter 29. Nov 2010 15:44

AW: Resample oder Resize mit GDI+
 
Danke für diesen wertvollen Hinweis!
Zitat:

Zitat von xaromz (Beitrag 1064740)
... Image und Graphics sind zwei völlig unterschiedliche Klassen. ... Ich vermute, dass deshalb Deine Interpolation fehlschlägt, weil Du der Funktion das Image übergibst, und nicht das entsprechende Graphics-Objekt.

Ich habe nun folgendes versucht, was aber nicht funktioniert. Ich komme sowei, dass ich ein GDI+ Image ausgehend von meinem TBitmap anlegen und ausgehend davon einen Zeiger auf das GDI+ Graphics bekomme. Trotzdem lässt sich der InterpolationMode nicht setzen mit dem Hinweis auf falsche Parameter:
Delphi-Quellcode:
Function GdipSetInterpolationMode(Out graphics: GdipImage;
  interpolationMode: GdipInterpolationMode): GdipStatus; Stdcall;
  External Gdip;
// GdipImage = Pointer; GdipInterpolationMode = Integer

Procedure TBitmapEx.StretchGdip(Const NewWidth, NewHeight: Integer;
  Const PreserveAspectRatio: Boolean = True);
Var
  Source : GdipImage; // = Pointer
  Dest   : GdipGraphics; // = Pointer
  Stat   : GdipStatus;
Begin
 If (Self.Empty) Then Exit;

  If (GdipCreateBitmapFromHBITMAP(self.Handle, self.Palette, Source) = Ok) Then
  Begin
    If (GdipGetImageGraphicsContext(Source, Dest) = Ok) Then
    Begin
      // Bis hierhin ist alles OK!
      Stat:=GdipSetInterpolationMode(Dest, 4);
      // Hier kommt der Hinweis auf falsche Parameter; leider nicht genau welcher der beiden möglichen...
      ShowMessage(GdipStatusMessage[Stat]);

    End;
    GdipDisposeImage(Source);
  End;
End;
Was mache in denn da falsch?

Ich scheitere nun schon seit letzter Woche an
Delphi-Quellcode:
GdipSetInterpolationMode()
. Ich habe alle möglichen Deklarationen angefangen von GDIPlus über IGDIPlus bis hin zu dem Uhr-Beispiel von EWeiss probiert. Im Grunde fehlen doch nur noch 3 Zeilen ((1)Interpolation Mode setzen (2) Bild zeichnen mit Draw und (3) ggf. auf das TBitmap zurückschieben).

EWeiss 30. Nov 2010 06:41

AW: Resample oder Resize mit GDI+
 
Die Vorgehensweise ist folgende..
1. Ein graphic obj erstellen
Code:
GdipCreateFromHDC(Hdc, graphics);
2. Setzen der Optimierungsmodis
Verschiedene modis die du verwenden kannst.
Smoothing, Interpolation, PixelOffset, CompositingMode..

Code:
GdipSetInterpolationMode(graphics, InterpolationModeHighQualityBicubic);
wobei InterpolationModeHighQualityBicubic den Wert 7 enthält..
QualityModeHigh := 2;
InterpolationModeHighQualityBicubic := QualityModeHigh + 5;

erst dann die Bilddatei als solche öffnen!

EDIT:
Zitat:

// Hier kommt der Hinweis auf falsche Parameter; leider nicht genau welcher der beiden möglichen...
Logisch ..

Du kannst ein img nicht in ein graphic object konvertieren..
Source und Destination müssen von typ graphics sein.

Zitat:

bzw. bei Emil Weiss Cardinal.
Spielt keine Rolle ist doch auslegungssache wie ich meine Function definiere. ;)
Aber davon abgesehen ist es bei MS auch ein Cardinal .. "Pointer of Cardinal"

Bei mir gehts siehe Picture :)

gruss

EWeiss 30. Nov 2010 10:17

AW: Resample oder Resize mit GDI+
 
Hier der Source zum Bild!
Vielleicht hilft es dir ja weiter :)

gruss

Schwedenbitter 30. Nov 2010 10:17

AW: Resample oder Resize mit GDI+
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von EWeiss (Beitrag 1064949)
Die Vorgehensweise ist folgende..
1. Ein graphic obj erstellen
Code:
GdipCreateFromHDC(Hdc, graphics);

Ich komme genau bis hierher und dann nicht mehr weiter. Ich habe ein TBimap erstelle, dorthinein eine Datei geladen und dann
Delphi-Quellcode:
GdipCreateFromHDC(TBitmap.Canvas.Handle, graphics);
aufgerufen.

Zitat:

Zitat von EWeiss (Beitrag 1064949)
2. Setzen der Optimierungsmodis
Verschiedene modis die du verwenden kannst.
Smoothing, Interpolation, PixelOffset, CompositingMode..

Das bekomme ich eben nicht hin :wall:.
Ich übergebe jetzt graphics mit
Delphi-Quellcode:
GdipSetInterpolationMode(graphics, 7)
, wobei gilt:
Delphi-Quellcode:
Type
  GdipGraphics = Pointer;
  GdipInterpolationMode = Integer;

Function GdipSetInterpolationMode(Out graphics: GdipGraphics;
  interpolationMode: GdipInterpolationMode): GdipStatus; Stdcall;
  External Gdip;
Ich habe jetzt mal aus lauter Verzweiflung einen kurzen Testcode beigefügt mit der GdipAPI.inc, die ich selbst gebastelt und bislang benutzt habe. Vielleicht sieht ja jemand auf Anhieb meinen Fehler.

[EDIT]
Mist! Du warst schneller ;-)
[/EDIT]

Gruß, Alex

EWeiss 30. Nov 2010 10:20

AW: Resample oder Resize mit GDI+
 
Siehe mein Source ;)

gruss

EWeiss 30. Nov 2010 10:40

AW: Resample oder Resize mit GDI+
 
Schmeiss mal das shit OUT aus "Out graphics: GdipGraphics" raus.

Dann funktioniert es auch.

gruss

Bummi 30. Nov 2010 10:51

AW: Resample oder Resize mit GDI+
 
Liste der Anhänge anzeigen (Anzahl: 1)
@Eweiss

Hi,

Ich hatte Ihn so verstanden daß er ein bestehenden GraphicObject direkt skalieren will ohne Umweg über ein File.

Ich habe mal eine Prozedur:
Delphi-Quellcode:
function DrawImageFromGraphic(source:TGraphic;
  DrawHdc: HDC;
  Percent: Integer;
  Interpolation: InterpolationMode = InterpolationModeDefault;
  Smoothing : SmoothingMode = SmoothingModeNone;
  PixelOffset : PixelOffsetMode = PixelOffsetModeNone;
  CompositingQuality: CompositingQualityMode = CompositingQualityDefault;
  CompositingMode: CompositingModeMode = CompositingModeSourceOver): Boolean;
in Deinen Code mit eingebaut, erfordert allerdings Classes,ActiveX wg.

function GdipLoadImageFromStreamICM(stream: ISTREAM;
out image: Cardinal): Integer; stdcall;
{$EXTERNALSYM GdipLoadImageFromStreamICM}

ist nur eine Quickhack, Du darfst falls Du es brauchen kannst das ganze gern optimieren.

EWeiss 30. Nov 2010 10:55

AW: Resample oder Resize mit GDI+
 
@Bummi
Zitat:

ist nur eine Quickhack, Du darfst falls Du es brauchen kannst das ganze gern optimieren.
Werde es mir mal anschauen.. ;)

Das Problem war aber eher das setzen des InterpolationMode
Die übergebenen Parameter müssen identisch sein sonst klappt das nicht :)
Er kann nicht bei Out graphis nicht einfach einen Pointer graphics übergeben ...
Wenn schon dann bitte das OUT entfernen dann funktioniert das auch.

gruss

Schwedenbitter 30. Nov 2010 10:57

AW: Resample oder Resize mit GDI+
 
Zitat:

Zitat von Bummi (Beitrag 1064998)
... daß er ein bestehenden GraphicObject direkt skalieren will ohne Umweg über ein File.

Genau so. Ich bekomme Bilder von einem Scanner geliefert mit 300 oder 400 dpi. Unser System arbeitet Fax-konform (jedenfalls aus Sicht unseres Faxes) mit 200 dpi. Das reicht auch zum lesen. Die Bilder sehen nur nicht sehr schön aus, wenn ich das mit
Delphi-Quellcode:
StretchBlt()
verkleinere.
Außerdem drucken wir auch. Die Drucker arbeiten alle mit 600 dpi, so dass das Bild vergrößern muss. Und da ich "Blut geleckt habe", möchte ich auch die Vergrößerung optimieren. Ansonsten wäre ich mit StretchBlt() und Halftone schon fertig gewesen. Das sieht nämlich ganz passabel aus und geht (auch) schnell.

Zitat:

Zitat von Bummi (Beitrag 1064998)
Du darfst falls Du es brauchen kannst das ganze gern optimieren.

Danke Euch beiden!

EWeiss 30. Nov 2010 11:12

AW: Resample oder Resize mit GDI+
 
Wieder eine gute Tat für Heute :)
Heheheeee..

gruss

Schwedenbitter 2. Dez 2010 17:53

AW: Resample oder Resize mit GDI+
 
So. Ich habe mir jetzt mal ausgehend von BUMMI's Änderungen am Quellcode von EWeiss die Procedure nachgebaut. Danke für die Tipps! Sie sieht jetzt so aus:
Delphi-Quellcode:
Procedure StretchGdip(Var Bitmap: TBitmap; Const NewWidth, NewHeight: Integer;
  Smoothing : SmoothingMode = SmoothingModeNone;
  Interpolation: InterpolationMode = InterpolationModeDefault;
  PixelOffset : PixelOffsetMode = PixelOffsetModeNone;
  CompositingQuality: CompositingQualityMode = CompositingQualityDefault;
  CompositingMode: CompositingModeMode = CompositingModeSourceOver);
Var
  ScaleDown : Boolean;
  W, H      : Single;
  oldW, oldH : Single;
  graphics  : Cardinal;
  str       : TStream;
  image     : Cardinal;
Begin
  If (Bitmap.Empty) Or
    ((NewWidth = 0) And (NewHeight = 0)) Or
    ((NewWidth = Bitmap.Width) And (NewHeight = Bitmap.Height)) Then
  Exit;

  W:=NewWidth;
  H:=NewHeight;
  oldW:=Bitmap.Width;
  oldH:=Bitmap.Height;
  If (W = 0) Then W:=H * oldW / oldH;
  If (H = 0) Then H:=W * oldH / oldW;
  ScaleDown:=((W < oldW) Or (H < oldH));

  If GdipCreateFromHDC(Bitmap.Canvas.Handle, graphics) = 0 Then
  Begin
    GdipSetSmoothingMode(      graphics, Smoothing);
    GdipSetInterpolationMode(  graphics, Interpolation);
    GdipSetPixelOffsetMode(    graphics, PixelOffset);
    GdipSetCompositingQuality( graphics, CompositingQuality);
    GdipSetCompositingMode(    graphics, CompositingMode);

    str:=TMemoryStream.Create;
    Try
      Bitmap.SaveToStream(str);
      str.Position:=0;
      If (Not ScaleDown) Then
      Begin
        Bitmap.Width:= Round(W);
        Bitmap.Height:=Round(H);
      End;
      If GdipLoadImageFromStreamICM(TStreamAdapter.Create(str), image) = 0 Then
         GdipDrawImageRect(graphics, image, 0, 0, W, H);
      If (ScaleDown) Then // überflüssige Ränder abschneiden
      Begin
        Bitmap.Width:= Round(W);
        Bitmap.Height:=Round(H);
      End;
    Finally
      str.Free;
    End;
    GdipDeleteGraphics(graphics);
  End;
End;
Das Verkleinern funktioniert insoweit einwandfrei. Das vergrößern denknotwendiger Weise nicht. Denn ich muss vor dem Vergrößern den notwendigen Platz schaffen, indem ich Width und Height schon setze. Dann vergrößert er aber nichts mehr und ich habe weiter den weißen Rand, was auch logisch ist.

Ich wollte aber für die Ausgabe auf dem 600dpi-Drucker die 200dpi-Bilder entsprechend vergrößern. Bei meiner Version mit GDI (ohne "+") gibt es da keine Probleme, weil ich über
Delphi-Quellcode:
StretchBlt(self.Canvas.Handle,
  0, 0, W, H,
  self.Canvas.Handle,
  0, 0, oldW, oldH,
  SRCCOPY);
die alte und neue Größe angeben kann.

Ich komme mit den weiteren Parametern bei
Delphi-Quellcode:
GdipDrawImageRectRect
nicht klar. Gibt es dafür noch irgend eine Alternative?

Gruß, Alex

EWeiss 2. Dez 2010 18:02

AW: Resample oder Resize mit GDI+
 
Du mußt graphics vorher neu erstellen
und dann kenn ich nur GdipDrawImageRectRectI :)

Sample!
Delphi-Quellcode:
    if GdipCreateFromHDC(hDCdest, Graphics) = 0 then
    begin
      GdipCreateBitmapFromHBITMAP(hBM2, 0, Pointer(Img));
      GdipSetInterpolationMode(Graphics, 2);
      GdipDrawImageRectRectI(Graphics, Img, xDest, yDest,
        xWidth, yHeight, 0, 0, xDiv, yDiv, 2, nil, False, nil);
      GdipDeleteGraphics(Graphics);
      GdipDisposeImage(Img);
    end;
    skDeleteObject(hBM2);
    DeleteDC(hDC2);
EDIT:
Definition von GdipDrawImageRectRectI ist..
GdipDrawImageRectRectI Graphics, Img, 0, 0, newwidth, newheight, 0, 0, origwidth, origheight, UnitPixel


gruss

Bummi 2. Dez 2010 18:13

AW: Resample oder Resize mit GDI+
 
Daß Du hier nach Printer.BeginDoc
direkt auf auf das Printercanvas gehen kannst weißt Du schon, oder?
Delphi-Quellcode:
GdipCreateFromHDC(Printer.Canvas.Handle .....

Katte 27. Mai 2019 16:50

AW: Resample oder Resize mit GDI+
 
@Schwedenbitter:
Auch wenn das Ganze schon uralt ist. Der oben abgebildete Source Code funktioniert so nur bedingt.

Sobald die Ergebnis-Bitmap größer als die Ursprungs-Bitmap wird, bekommt man eine schöne Windows Exception präsentiert. Das liegt daran, dass die Routine GdipDrawImageRect nicht prüft, ob die Destination auch genügend Speicherplatz bietet.

Eine möglich Lösung wäre:
1. Zuerst das Image GDI Objekt erstellen
2. Bitmap mit SetSize entsprechend der Ergebnisgröße setzen
3. Erst danach dipDrawImageRect(graphics, image, 0, 0, W, H) aufrufen!

Gruß,
Katte


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:18 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz