Deutsch
Forum

GDI+

 

Georg
Hovenbitzer
Hallo zusammen,

mich erdrückt gerade eine riesige Wissenslücke und ich hoffe ihr könnt mir weiterhelfen.
Ziel ist es, ein Bild zu laden, es zu einem Thumbnail zu verkleiner und dann in einer Datenbank abzulegen.
Mein Problem ist nun der Teil zwischen Thumbnail und Datenbank und da der, wie ich an eine Speicheradresse / Array des Bildes und dessen Größe komme:
Als Vorlage habe ich das geile GDI+ Demo von Andreas genommen.
 $H \windows.ph
 $H \includes\Functions_GDIPLUS.ph
 $H \includes\GDIP_Imageging.ph
 $I \includes\GDIP_Helper.inc
Declare gdiplusToken&,ImageObject&,ImageWidth&,ImageHeight&,GraphicObject&
Declare hPic&
Declare imgThumb&
Declare lngImage&
Declare hBitmap&
Window 0,0-1024,786
Cls ~GetSysColor(~COLOR_3DFACE)
gdiplusToken& = InitGDIPlus()
hPic& = @Create(hPic,-1,Bild.jpg)
~GdipCreateBitmapFromHBITMAP(hPic&,0,@Addr(lngImage&))
~GdipGetImageThumbnail(lngImage&,92,69,@Addr(imgThumb&),0,0)
~GdipCreateHBITMAPFromBitmap(imgThumb&,@Addr(hBitmap&))
DrawPic hBitmap&,50,50;0
hier bräuchte ich nun etwas, was mir eine Adresse oder Array und dessen Größe ermittelt !!!
WaitInput
DeleteObject hPic&
DeleteObject hBitmap&
~GdipDisposeImage(imgThumb&)
~GdipDisposeImage(lngImage&)
ExitGDIPlus(gdiplusToken&)
End
 
Viele Grüsse, Georg Hovenbitzer(Windows XP Pro, XProfan 11.2, Profan2Cpp 1.6a)
02.02.2009  
 



Ich glaube hier [...]  ./. hier [...]  steht die Lösung.

Ich würde wohl die Thumbs per XProfan11s hPic-Funktionen erzeugen, und die paar Pixel des Thumbs per z.B. inlineasm oder ner kleinen DLL abpixeln, wenn der DIB-Krahm nicht will. *g*
 
02.02.2009  
 




Georg
Hovenbitzer
Hallo if,

danke für die Links, werde sie mir gleich mal genauer ansehen.
Mit XProfan11 erzeugte Thumbnails sind von der Qualität nicht der Hit, da muss man dann zuerst noch mal einen Weichzeichner drüber laufen lassen, keine saubere Lösung.
Per GDI+ könnte ich sie zur Zeit schon abspeichern und dann einfach wieder neu einlesen und in die Datenbank speichern.
Aber ich würde es gerne schaffen ohne diesen Umweg, rein um was zu lernen.
Mal sehen vielleicht komme ich mit den Links ja klar oder es hat jemand noch einen Tip!

Nachtrag:
Ich habe mir beide Demos mal angesehen, leider helfen sie mir nicht weiter.
Leider komme ich an keine brauchbare Adresse und dessen Größe um per BlockWrite den Speicher wegzuschreiben.
 
Viele Grüsse, Georg Hovenbitzer(Windows XP Pro, XProfan 11.2, Profan2Cpp 1.6a)
02.02.2009  
 




Uwe
''Pascal''
Niemeier
Hi Georg!

Habe hier eine alte Routine gefunden und auf die Schnelle angepaßt; damit läßt sich ein bmp-Handle in Bilddaten zerlegen. Vielleicht hilf das was:
KompilierenMarkierenSeparieren
window 30,30-500,500
 $H Windows.ph
declare a#,b#
declare Pic$,Handle&,Neu&
---------------------------------Diesen Pfad bitte anpassen!
Pic$=C:Dokumente und EinstellungenPascalDesktopBild.jpg
Handle&=create(hPic,-1,Pic$)--Liefert immer 32-Bit-Handle!
--BITMAPINFOHEADER-Struktur
--Nur benötigte Parameter wurden bezeichnet
struct bmpHEADER=Size&,Width&,Height&,Planes%,BitCount%,Compression&,SizeImage&,X#(24)
dim a#,bmpHEADER
a#.Size&=sizeof(a#)------------------Größe von BITMAPINFOHEADER
~GetDIBits(%hdc,Handle&,0,0,0,a#,0)--Dummy-Aufruf, um bmpHeader zu füllen (Rückgabe: 1=OK)
dim b#,a#.SizeImage&-----------------Bereich für Bildinformationen (Pixel) bereitstellen
~GetDIBits(%hdc,Handle&,0,a#.Height&,b#,a#,0)--Eigendliche Bilddaten aus Handle ermitteln
Neu&=~CreateDIBitmap(%hdc,a#,4,b#,a#,0)------Neue DIB mit geänderten Farben erzeugen
drawpic Neu&,10,10;0
dispose a#
dispose b#
waitkey

BTW: Das mit der Qualität (create(hSizedPic... ?)ist mir noch nicht aufgefallen; aber GDI+ ist deutlich schneller.
BTW2: Kriegst du die (binären) Daten denn überhaupt in die DB? Kann mich dunkel erinnern, daß manche DBs non-ASCII-Daten ignorieren oder umwandeln.

HTH
Pascal
 
02.02.2009  
 




Georg
Hovenbitzer
Hallo Pascal,

vielen Dank für den Tipp.
Aber ich komme damit nicht ganz klar, ich habe zwar nun die Größe des Bitmaps und ein Array, aber das wieder Abspeichern haut nicht hin.
In meinem Beispiel habe ich das Handle vom erstellten Thumbnail und könnte nun mit der vom Andreas geschriebenen Funktion SaveAsJPG das Bild auch abspeichern. Dazu benutzt er in seiner Funktion GdipSaveImageToFile! Es könnte sein, dass die Funktion GdipSaveImageToStream das richtige für mich ist, habe aber keine Ahnung wie man sie richtig einsetzt.

Viele Grüße
Georg
 
Viele Grüsse, Georg Hovenbitzer(Windows XP Pro, XProfan 11.2, Profan2Cpp 1.6a)
02.02.2009  
 




Uwe
''Pascal''
Niemeier
Hi Georg!

Das mit dem Speichern sollte kein Problem sein - wohlgemerkt in eine Datei!
Aber ich ahne langsam, worauf du hinauswillst: Du willst eine JPG-Datei im Speicher erzeugen und die dann in die DB einfügen, richtig? (Wobei zu bedenken ist, daß Bild-Handles  immer Bitmap-basiert sind)

Das mit dem Stream könnte klappen, ist aber nicht einfach; Streams sind COM-Objekte und damit nicht leicht zu handhaben. Außerdem bleibt noch die Frage, ob und wie du die Binärdaten in die DB kriegst.

Eine Alternative wäre, die Thumbs in eine ImageList zu packen und diese als eine  Datei (parallel zur DB) abzuspeichern. In der DB wäre dann nur ein Index aufs Bild in der ImageList vermerkt. Habe ich schon erfolgreich praktiziert

Vielleicht verwendest du aber auch eine DB, die von Haus aus Bilder enthalten kann; dann müßte man die Sache mit ActiveX / VBA angehen.

Oder eben der Trick mit dem DataGenerator

HTH
Pascal
 
03.02.2009  
 




Georg
Hovenbitzer
Hallo Pascal,

genau dies war meine Idee, um den Umweg des Speichers in eine Datei zu umgehen.
Ich habe viel im Internet gesucht und was ich gefunden habe, sah alles andere als einfach aus.
Dies wäre ein Beispiel [...]  wo ein Bild am Ende in einem Byte Array landet und von da könnte man es in eine DB schreiben. aber alle meine Versuche es nachzubauen sind gescheiter, da mir hier auch das tiefere Grundwissen fehlt.
Ich habe mich mit SQLite befasst und konnte dort ohne Probleme JPG Bilder in Blob Felder ablegen und auch wieder herausholen.
Wenn du noch einen Tipp hast immer her damit
 
Viele Grüsse, Georg Hovenbitzer(Windows XP Pro, XProfan 11.2, Profan2Cpp 1.6a)
03.02.2009  
 



Kleine Frage hierzu! Solch ein z.B. 48x48 JPG 60% sollte unter 1 KB Daten benötigen, also auch base64-kodiert kein Problem...

Also warum nicht einfach komplette JPG (681 Byte) Laden? var meinString$=[decode64](jpgDateiInhalt) ?


1 kB
Hochgeladen:03.02.2009
Ladeanzahl134
Herunterladen
 
03.02.2009  
 




Thomas
Freier
Ach Georg, verrate uns doch einmal den Datenbanktyp: dBaseV.dbf, access.mdb mit Ole-Feld oder HiPer-Six.dbf,FoxPro.dbf mit Blob-Feld oder ???
Die genannten können sicher mit auch über sql bearbeitet werden.
Und in deinem Hinweis wird auf :


Für die Funktion PictureToString wird die IStream.TLB
von madmax benötigt und ist in diesem Download enthalten.


verwiesen.
 
Gruß Thomas
Windows XP SP2, XProfan X2
04.02.2009  
 




Georg
Hovenbitzer
Hallo zusammen,

es geht mir hier nicht nur darum die Thumbnails in eine Datenbank zu speichern, sondern auch die original Bilder.
Bei dem Original ist dies kein Problem, da ich es per Blockread einlesen und so in ein Blob Feld abspeichern kann.
Bei den Thumbnails ist es anders, da sie ja erst erstellt werden und daher nur als Bitmap im Speicher vorliegen, dies muss zuerst ja noch in ein JPG Format gestreamt werden und dann kann ich es ablegen. Das Streamen geht wenn ich das Bitmap mit GdipSaveImageToFile abspeichere, dafür gibt es Vorlagen.
Man kann nun das gespeicherte Thumbnail wieder Einlesen und in die Datenbank speichern, nur dies würde ich mir gerne sparen, allein um zu sehen wie es geht.

Anbei mal ein Demo wie man mit SQLite ein bild in eine Datenbank ablegt, die benötigte DLL bekommt man bei SQLite.
Das Demo legt eine DB an, lädt ein Bild, speichert dies in die DB, liest es wieder aus und speicher es und einen anderen Namen ab.
Die Ausgaben auf dem Bildschirm sind nur Statusmeldungen, es werden auch keine Fehler abgefangen.
Pfade bitte anpassen.
KompilierenMarkierenSeparieren
Def ZeroMemory(2) !KERNEL32,RtlZeroMemory
Def MoveMemory(3) !KERNEL32,RtlMoveMemory
Def WriteFile(5) !KERNEL32,WriteFile
Def CreateFile(7) !KERNEL32,CreateFileA
Def ReadFile(5) !KERNEL32,ReadFile
Def OpenFile(3) !KERNEL32,OpenFile
Def GetFileSizeAPI(2) !KERNEL32,GetFileSize
Def CloseHandle(1) !KERNEL32,CloseHandle
Def &SQLITE_OK           0/* Successful result */
Def &SQLITE_ERROR        1/* SQL error or missing database */
Def &SQLITE_INTERNAL     2/* Internal logic error in SQLite */
Def &SQLITE_PERM         3/* Access permission denied */
Def &SQLITE_ABORT        4/* Callback routine requested an abort */
Def &SQLITE_BUSY         5/* The database file is locked */
Def &SQLITE_LOCKED       6/* A table in the database is locked */
Def &SQLITE_NOMEM        7/* A malloc() failed */
Def &SQLITE_READONLY     8/* Attempt to write a readonly database */
Def &SQLITE_INTERRUPT    9/* Operation terminated by sqlite3_interrupt()*/
Def &SQLITE_IOERR       10/* Some kind of disk I/O error occurred */
Def &SQLITE_CORRUPT     11/* The database disk image is malformed */
Def &SQLITE_NOTFOUND    12/* NOT USED. Table or record not found */
Def &SQLITE_FULL        13/* Insertion failed because database is full */
Def &SQLITE_CANTOPEN    14/* Unable to open the database file */
Def &SQLITE_PROTOCOL    15/* NOT USED. Database lock protocol error */
Def &SQLITE_EMPTY       16/* Database is empty */
Def &SQLITE_SCHEMA      17/* The database schema changed */
Def &SQLITE_TOOBIG      18/* String or BLOB exceeds size limit */
Def &SQLITE_CONSTRAINT  19/* Abort due to constraint violation */
Def &SQLITE_MISMATCH    20/* Data type mismatch */
Def &SQLITE_MISUSE      21/* Library used incorrectly */
Def &SQLITE_NOLFS       22/* Uses OS features not supported on host */
Def &SQLITE_AUTH        23/* Authorization denied */
Def &SQLITE_FORMAT      24/* Auxiliary database format error */
Def &SQLITE_RANGE       25/* 2nd parameter to sqlite3_bind out of range */
Def &SQLITE_NOTADB      26/* File opened that is not a database file */
Def &SQLITE_ROW         100/* sqlite3_step() has another row ready */
Def &SQLITE_DONE        101/* sqlite3_step() has finished executing */
Def &SQLITE_OPEN_READONLY         $00000001
Def &SQLITE_OPEN_READWRITE        $00000002
Def &SQLITE_OPEN_CREATE           $00000004
Def &SQLITE_OPEN_DELETEONCLOSE    $00000008
Def &SQLITE_OPEN_EXCLUSIVE        $00000010
Def &SQLITE_OPEN_MAIN_DB          $00000100
Def &SQLITE_OPEN_TEMP_DB          $00000200
Def &SQLITE_OPEN_TRANSIENT_DB     $00000400
Def &SQLITE_OPEN_MAIN_JOURNAL     $00000800
Def &SQLITE_OPEN_TEMP_JOURNAL     $00001000
Def &SQLITE_OPEN_SUBJOURNAL       $00002000
Def &SQLITE_OPEN_MASTER_JOURNAL   $00004000
Def &SQLITE_OPEN_NOMUTEX          $00008000
Def &SQLITE_OPEN_FULLMUTEX        $00010000
Def &SQLITE_STATIC 0
Def &SQLITE_TRANSIENT -1
Declare Datei$
Declare hDLL&
Declare dll$
Declare DBName$
Declare hDB&
Declare Error&
Declare SQLCmd$
Declare Result&
Declare Titel$[]
Declare Titel$
Declare Titel&
Declare zKey$
Declare zBlob#
Declare nBlob&
Declare pnBlob&
Declare pzBlob#
Declare BWrite&
Declare FBuf#
Declare FHdl&
Declare FSize&
Window 0,0-1024,786
@Set(CallConv,CDECL)
dll$ = \sqlite3.dll
hDLL& = ImportDLL(dll$,_)
DBName$ = \TestDB3.db3
Print ---------
Error& = _sqlite3_open_v2(@Addr(DBName$),@Addr(hDB&),(&SQLITE_OPEN_READWRITE | &SQLITE_OPEN_CREATE),0)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
SQLCmd$ = CREATE TABLE blobs(key TEXT PRIMARY KEY, value BLOB);
Error& = _sqlite3_exec(hDB&,@Addr(SQLCmd$),0,0,@Addr(Result&))
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
SQLCmd$ = INSERT INTO blobs(key, value) VALUES(?, ?);
Error& = _sqlite3_prepare_v2(hDB&,@Addr(SQLCmd$),-1,@Addr(Result&),0)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
zKey$ = Bild
_sqlite3_bind_text(Result&,1,@Addr(zKey$),-1,&SQLITE_STATIC)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
Datei$ = \Wasserlilien.jpg
Dim zBlob#,@FileSize(Datei$)
nBlob& = @BlockRead(Datei$,zBlob#,0,@FileSize(Datei$))
_sqlite3_bind_blob(Result&,2,zBlob#,@FileSize(Datei$),&SQLITE_TRANSIENT)
Print @String$(_sqlite3_errmsg(hDB&),0)
Dispose zBlob#
Print ---------
_sqlite3_step(Result&)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
_sqlite3_finalize(Result&)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
SQLCmd$ = SELECT value FROM blobs WHERE key = ?;
Error& = _sqlite3_prepare_v2(hDB&,@Addr(SQLCmd$),-1,@Addr(Result&),0)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
zKey$ = Bild
_sqlite3_bind_text(Result&,1,@Addr(zKey$),-1,&SQLITE_STATIC)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
_sqlite3_step(Result&)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
pnBlob& = _sqlite3_column_bytes(Result&,0)
Datei$ = \Blob_Wasserlilien.jpg
Dim FBuf#,1024
String FBuf#,0 = Datei$ + @Chr$(0)
FHdl& = CreateFile(FBuf#,$40000000,0,0,$4,$80,0)
WriteFile(FHdl&,_sqlite3_column_blob(Result&,0),pnBlob&,@Addr(BWrite&),0)
CloseHandle(FHdl&)
Dispose FBuf#
Print ---------
_sqlite3_finalize(Result&)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
Error& = _sqlite3_close(hDB&)
Print @String$(_sqlite3_errmsg(hDB&),0)
Print ---------
WaitInput
FreeDLL hDLL&
End
 
Viele Grüsse, Georg Hovenbitzer(Windows XP Pro, XProfan 11.2, Profan2Cpp 1.6a)
04.02.2009  
 




Uwe
''Pascal''
Niemeier
Hi Georg!

Die Idee fängt an, mir zu gefallen...
Das mit der ImageList oder bmp-Daten in Bereichsvariablen ist zwar einfacher, aber da kommen schnell ein paar MB zusammen (ohne Original-Bilder!).

Das VB-Beispiel könnte da nützlich sein...

iF
Solch ein z.B. 48x48 JPG 60% sollte unter 1 KB Daten benötigen, also auch base64-kodiert kein Problem...


Gibt es da eine Grenze, was die Datenlänge betrifft?

SeeYou
Pascal
 
04.02.2009  
 




Uwe
''Pascal''
Niemeier
Ich nochmal

Aus deinem Beispiel und dem Code von Andreas habe ich mal schnell dies gebastelt:
KompilierenMarkierenSeparieren
 $H windows.ph
 $H Functions_GDIPLUS.ph
 $H GDIP_Imageging.ph
 $I GDIP_Helper.inc
---Für Header:
CoInitialize(=External(ole32,CoInitialize,;
CoUninitialize(=External(ole32,CoUninitialize,;
CreateStreamOnHGlobal(=External(ole32,CreateStreamOnHGlobal,;
GetHGlobalFromStream(=External(ole32,GetHGlobalFromStream,;
def CoInitialize(1) !ole32,CoInitialize
def CoUninitialize(0) !ole32,CoUninitialize
def CreateStreamOnHGlobal(3) !ole32,CreateStreamOnHGlobal
def GetHGlobalFromStream(2) !ole32,GetHGlobalFromStream

Proc SaveJpgToMem---------------------------------------SaveJpgToMem

    Parameters Bitmap&,Quality&
    Declare encoderCLSID#,result&
    result& = 0
    Dim encoderCLSID#,16
    Declare encoderParameters#
    Declare wTemp$,encoder#
    wTemp$ = S2WS(~EncoderQuality)
    Dim encoder#,16
    IIDFromString(ADDR(wTemp$),encoder#)
    Dim encoderParameters#,32
    Long encoderParameters#,0 = 1
    MoveMemory(encoderParameters#+4,encoder#,16)
    Long encoderParameters#,20 = 1
    Long encoderParameters#,24 = ~EncoderParameterValueTypeLong
    Long encoderParameters#,28 = ADDR(Quality&)

    If GetEncoderCLSID(image/jpeg,encoderCLSID#)

        --Original: ~GdipSaveImageToFile(Bitmap&,ADDR(newFile$),encoderCLSID#,encoderParameters#)
        CoInitialize(0)--------------------------------------------------
        declare IStream&
        CreateStreamOnHGlobal(0,1,addr(IStream&))
        ~GdipSaveImageToStream(Bitmap&,IStream&,encoderCLSID#,encoderParameters#)
        declare Global&,Mem&,Size&
        GetHGlobalFromStream(IStream&,addr(Global&))
        Mem&=~GlobalLock(Global&)
        Size&=~GlobalSize(Global&)
        dim PicData#,Size&---------------PicData# global deklariert
        MoveMemory(PicData#,Mem&,Size&)--MoveMemory definiert in GDIP_Helper.inc
        ~GlobalUnlock(Global&)
        ~GlobalFree(Global&)
        CoUninitialize()-------------------------------------------------
        result& = 1

    Endif

    Dispose encoder#
    Dispose encoderParameters#
    Dispose encoderCLSID#
    Return result&
    EndProc-------------------------------------------------------------
    Declare gdiplusToken&,ImageObject&,ImageWidth&,ImageHeight&,GraphicObject&
    Declare hPic&
    Declare imgThumb&
    Declare lngImage&
    Declare hBitmap&
    Window 0,0-500,500
    gdiplusToken& = InitGDIPlus()
    hPic&=create(hPic,-1,C:1TestBild.jpg)-----------------------Pfad anpassen!
    ~GdipCreateBitmapFromHBITMAP(hPic&,0,addr(lngImage&))
    ~GdipGetImageThumbnail(lngImage&,120,120,addr(imgThumb&),0,0)
    ~GdipCreateHBITMAPFromBitmap(imgThumb&,addr(hBitmap&),0)--Drei Parameter!!
    DrawPic hBitmap&,50,50;0
    declare PicData#--wird vor Ort jeweils passend dimensioniert
    SaveJpgToMem(imgThumb&,75)
    -----------------------------Test
    assign #1,C:1TestTest.jpg
    openrw #1
    blockwrite #1,PicData#,0,sizeof(PicData#)
    close #1
    WaitInput
    DeleteObject hPic&
    DeleteObject hBitmap&
    ~GdipDisposeImage(imgThumb&)
    ~GdipDisposeImage(lngImage&)
    ExitGDIPlus(gdiplusToken&)
    End

Nach Aufruf von SaveJpgToMem  findet sich der vollständige Code der neu erstellten JPG in PicData#.
Sicher noch verbesserungsfähig,aber damit könnte man schon was anfangen...

HTH
Pascal
 
04.02.2009  
 




Antworten


Thementitel, max. 100 Zeichen.
 

Systemprofile:

Kein Systemprofil angelegt. [anlegen]

XProfan:

 Beitrag  Schrift  Smilies  ▼ 

Bitte anmelden um einen Beitrag zu verfassen.
 

Themenoptionen

18.788 Betrachtungen

Unbenanntvor 0 min.
Thomas Freier27.08.2019
Georg11.05.2015
Andre Rohland14.02.2015
Uwe ''Pascal'' Niemeier11.12.2014
Mehr...

Themeninformationen



Admins  |  AGB  |  Anwendungen  |  Autoren  |  Chat  |  Datenschutz  |  Download  |  Eingangshalle  |  Hilfe  |  Händlerportal  |  Impressum  |  Mart  |  Schnittstellen  |  SDK  |  Services  |  Spiele  |  Suche  |  Support

Ein Projekt aller XProfaner, die es gibt!


Mein XProfan
Private Nachrichten
Eigenes Ablageforum
Themen-Merkliste
Eigene Beiträge
Eigene Themen
Zwischenablage
Abmelden
 Deutsch English Français Español Italia
Übersetzungen

Datenschutz


Wir verwenden Cookies nur als Session-Cookies wegen der technischen Notwendigkeit und bei uns gibt es keine Cookies von Drittanbietern.

Wenn du hier auf unsere Webseite klickst oder navigierst, stimmst du unserer Erfassung von Informationen in unseren Cookies auf XProfan.Net zu.

Weitere Informationen zu unseren Cookies und dazu, wie du die Kontrolle darüber behältst, findest du in unserer nachfolgenden Datenschutzerklärung.


einverstandenDatenschutzerklärung
Ich möchte keinen Cookie