Einzelnen Beitrag anzeigen

Benutzerbild von Gausi
Gausi

Registriert seit: 17. Jul 2005
885 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: TPicture, TJPegImage, TBitmap, TBitmap32 und Threads ...

  Alt 28. Sep 2019, 21:16
Hier jetzt mein aktueller Code dazu.

Sieht furchtbar lang aus, ist aber so ziemlich genau das, was auch beim TWICImage beim Laden und Speichern alles so gemacht wird, und was die WIC halt so verlangt. Besonders die Variablenliste kann einen erstmal schocken.

Zur Erklärung:
Parameter
  • aStream: Ein Stream mit Bilddaten (FileStream von einer Bilddatei, MemoryStream mit Bilddaten aus einem ID3-Tag, ...)
  • aFilename: Dateiname der Zieldatei (wird ggf. erstellt)
  • destWidth/destHeight: Zielgröße des Bildes. Das Originalbild wird so skaliert, dass es in das Rechteck destWidth*destHeight hineinpasst
  • aWICImagingFactory: eine WICImagingFactory, damit diese bei vielen Skalierungen nicht immer neu erstellt werden muss. Bei NIL wird eine lokale neu erzeugt.
  • Overwrite: Flag, das in meiner Anwendung gelegentlich gebraucht wird. Damit werden bereits vorhandene Dateien überschrieben, ansonsten wird abgebrochen - aber trotzdem "Erfolg" zurückgeliefert. Das ist in meinem Anwendungsfall so sinnvoll
Ausgabe:
  • True, falls das skalierte Bild erfolgreich erstellt wurde (oder ggf. bereits existiert)
Das Bildformat wird automatisch aus dem Stream ermittelt. Das Ausgabeformat ist immer JPEG.
Eine ggf. vorhandene WICImagingFactory muss im Kontext des Threads erzeugt werden, in dem die Funktion laufen soll.

Fehlerbehandlung könnte intensiver sein, und ein Rückgabewert mit mehr Info als "hat geklappt" wäre ggf. auch sinnvoll. Das ist dann aber dem geneigten Leser zur Übung überlassen.

Aber das sollte dann so ziemlich Threadsafe sein, komplett ohne VCL und TGraphic.

Delphi-Quellcode:
function ScalePicStreamToFile(aStream: TStream; aFilename: UnicodeString; destWidth, destHeight: Integer; aWICImagingFactory: IWICImagingFactory; OverWrite: Boolean = False): boolean;
var
    hr: HRESULT;
    isLocalFactory: Boolean;
    // for proper scaling
    xfactor, yfactor:double;
    origWidth, origHeight: Cardinal;
    newWidth, newHeight: Cardinal;
    // reading the source image
    SourceAdapter: IStream;
    BitmapDecoder: IWICBitmapDecoder;
    DecodeFrame: IWICBitmapFrameDecode;
    SourceBitmap: IWICBitmap;
    SourceScaler: IWICBitmapScaler;
    // writing the resized image
    DestStream: TMemoryStream;
    DestAdapter: IStream;
    DestWICStream: IWICStream;
    BitmapEncoder: IWICBitmapEncoder;
    EncodeFrame: IWICBitmapFrameEncode;
    Props: IPropertyBag2;
begin
    result := False;
    if Not Overwrite and FileExists(aFilename) then
    begin
        result := True;
        exit;
    end;

    isLocalFactory := (aWICImagingFactory = nil);
    if isLocalFactory then
        CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER or
          CLSCTX_LOCAL_SERVER, IUnknown, aWICImagingFactory);

    // read the image data from stream
    SourceAdapter := TStreamAdapter.Create(aStream);
    hr := aWICImagingFactory.CreateDecoderFromStream(SourceAdapter, guid_null, WICDecodeMetadataCacheOnDemand, BitmapDecoder);
    if Succeeded(hr) then hr := BitmapDecoder.GetFrame(0, DecodeFrame);
    if Succeeded(hr) then hr := aWICImagingFactory.CreateBitmapFromSource(DecodeFrame, WICBitmapCacheOnLoad, SourceBitmap);
    if Succeeded(hr) then hr := SourceBitmap.GetSize(origWidth, origHeight);

    // calculate proper scaling
    xfactor:= (destWidth) / origWidth;
    yfactor:= (destHeight) / origHeight;
    if xfactor > yfactor then
    begin
        newWidth := round(origWidth * yfactor);
        newHeight := round(origHeight * yfactor);
    end else
    begin
        newWidth := round(origWidth * xfactor);
        newHeight := round(origHeight * xfactor);
    end;

    // scale the original image
    if Succeeded(hr) then hr := aWICImagingFactory.CreateBitmapScaler(SourceScaler);
    if Succeeded(hr) then hr := SourceScaler.Initialize(SourceBitmap, NewWidth, NewHeight, WICBitmapInterpolationModeFant);

    if Succeeded(hr) then
    begin
        // Reading and scaling the original image was successful.
        // Now try to save the scaled image
        DestStream := TMemoryStream.create;
        try
            // create new WICStream
            DestAdapter := TStreamAdapter.Create(DestStream);
            if Succeeded(hr) then hr := aWICImagingFactory.CreateStream(DestWICStream);
            if Succeeded(hr) then hr := DestWICStream.InitializeFromIStream(DestAdapter);
            // create and prepare JPEG-Encoder
            if Succeeded(hr) then hr := aWICImagingFactory.CreateEncoder(GUID_ContainerFormatJpeg, guid_null, BitmapEncoder);
            if Succeeded(hr) then hr := BitmapEncoder.Initialize(DestWICStream, WICBitmapEncoderNoCache);
            if Succeeded(hr) then hr := BitmapEncoder.CreateNewFrame(EncodeFrame, Props);
            if Succeeded(hr) then hr := EncodeFrame.Initialize(Props);
            if Succeeded(hr) then hr := EncodeFrame.SetSize(newWidth, newHeight);
            // write image data
            if Succeeded(hr) then hr := EncodeFrame.WriteSource(SourceScaler, nil);
            if Succeeded(hr) then hr := EncodeFrame.Commit;
            if Succeeded(hr) then hr := BitmapEncoder.Commit;
            // finally save the stream to the destination file
            if Succeeded(hr) then
                try
                    DestStream.SaveToFile(aFilename);
                    result := True;
                except
                    // silent exception here, but (try to) delete the destination file, if it exists
                    result := False;
                    if FileExists(aFilename) then DeleteFile(aFilename);
                end;
        finally
            DestStream.Free;
        end;
    end;

    if isLocalFactory then
        aWICImagingFactory._Release;
end;
The angels have the phone box.
  Mit Zitat antworten Zitat