Forum | | | | 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* |
| | | | |
| | 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: KompilierenMarkierenSeparierenwindow 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 |
| | | | |
| | 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 |
| | | | |
| | 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) ?
|
| | | | |
| | 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. |
| | | | |
| | 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. KompilierenMarkierenSeparierenDef 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 |
| | | | |
| | 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 |
| | | | |
|
AntwortenThemenoptionen | 19.197 Betrachtungen |
ThemeninformationenDieses Thema hat 7 Teilnehmer: |