PDA

Ver la Versión Completa : Cargar imagen desde Firebird


Angel.Matilla
11-10-2017, 18:25:26
Estoy usando este código que me facilitaron en otro foro:
TJPEGImage* __fastcall BlobToJpg(TBlobField *CampoBLOB)
{
if(!CampoBLOB->Value.IsEmpty())
{
TJPEGImage *JPeg = new TJPEGImage;
TMemoryStream *StreamTmp = new TMemoryStream;
CampoBLOB->SaveToStream(StreamTmp); //sacamos el jpg y se convierte a stream
StreamTmp->Seek(0,soFromBeginning);
JPeg->LoadFromStream(StreamTmp);
return(JPeg);
}
else
return(NULL);
}para poder sacar imágenes desde una tabla Firebird. Siguiendo las instrucciones del autor trato de cargar la misma en un objeto TImage de la siguiente forma a partir de un query:
Imagen->Picture->Assign(::BLOBtoJPG(Query->FieldByName("Imagen")));pero tanto con esta sintaxis como de esta otra forma:
Imagen->Picture->Assign(::BLOBtoJPG(Query->FieldByName("Imagen")->Value));En ambos casos me da error al compilar; en el primer caso me dice:
E2034 Cannot convert 'TField *' to 'TBlobField *'y en el segundo algo parecido:
E2034 Cannot convert 'Variant' to 'TBlobField *'¿Alguna idea para resolver el problema?

TOPX
11-10-2017, 18:48:25
Si fuera yo, definiría el tipo del parámetro CampoBLOB como TField, de manera que no tenga que preocuparme por el code smell del casteo (https://en.wikipedia.org/wiki/Downcasting).
-

Angel.Matilla
11-10-2017, 19:14:53
Haré la prueba.

Angel.Matilla
11-10-2017, 19:17:58
Si fuera yo, definiría el tipo del parámetro CampoBLOB como TField, de manera que no tenga que preocuparme por el code smell del casteo (https://en.wikipedia.org/wiki/Downcasting).
No funciona. Y al compliar me he dado cuenta de por qué: Fíjate que en la función hay un SaveToStream que si se usa en la definición un TField en lugar de un TBlobField da error.
E2316 'SaveToStream' is not a member of 'TField'

Angel.Matilla
11-10-2017, 19:24:58
Dicho de otra forma: ¿Qué habría que poner detrás del FieldByName? Igual que ponemos AsString, AsInteger o AsDateTime, ¿qué se pone en caso de campos BLOB? Porque AsBlob a mi me da error en BCB 6.

TOPX
11-10-2017, 19:38:02
No funciona. Y al compliar me he dado cuenta de por qué: Fíjate que en la función hay un SaveToStream que si se usa en la definición un TField en lugar de un TBlobField da error.

Ok, entonces hay que hacer el casteo al tipo de dato del parámetro, osea algo como
::BLOBtoJPG((TBlobField*)Query->FieldByName("Imagen"))

Igual que ponemos AsString, AsInteger o AsDateTime, ¿qué se pone en caso de campos BLOB? Porque AsBlob a mi me da error en BCB 6.

Eso es equivocado; el método BLOBtoJPG no está pidiendo un valor, está pidiendo un objeto.
-

ecfisa
12-10-2017, 00:14:59
Hola.

Solo como observación, me parece ver un problema de memory leak en esa función...

Saludos :)

ecfisa
12-10-2017, 02:03:29
Hola de nuevo.

De este modo me funciona bien:

...
#include <jpeg.hpp>

void blobTojpg( TField *fld, TImage *img )
{
TBlobField *B = static_cast <TBlobField*> ( fld );

if( !B->Value.IsEmpty() ) {
TJPEGImage *J = new TJPEGImage;
try {
J->Assign( B );
img->Picture->Bitmap->Height = J->Height;
img->Picture->Bitmap->Width = J->Width;
img->Picture->Bitmap->Canvas->Draw( 0, 0, J );
}
__finally {
delete J;
}
}
}



Y si gustas usar streams,

void blobTojpg2( TField *fld, TImage *img )
{
TBlobField *B = static_cast <TBlobField*> ( fld );

if ( !B->Value.IsEmpty() ) {
TStream *Stream = new TMemoryStream;
TJPEGImage *Jpg = new TJPEGImage;
try {
B->SaveToStream( Stream );
Stream->Seek( 0, soFromBeginning);
Jpg->LoadFromStream( Stream );
img->Picture->Assign( Jpg );
}
__finally {
delete Stream;
delete Jpg;
}
}
}


Llamada (para ambos casos):

void __fastcall TForm1::DataSetAfterScroll( TDataSet *DataSet )
{
blobTojpg( IBDataSet1->FieldByName("IMAGE"), Image1 ); // o blobToJpg2( ...
}


El resultado en ambos casos:
https://s1.postimg.org/1i4r0m9cpr/AMatilla_04.gif

Saludos :)

Angel.Matilla
13-10-2017, 11:02:29
Ok, entonces hay que hacer el casteo al tipo de dato del parámetro, osea algo como
::BLOBtoJPG((TBlobField*)Query->FieldByName("Imagen"))
¿Ves? Estas cosas no se me ocurren nunca :(. Ha funcionado a la perfección.
Eso es equivocado; el método BLOBtoJPG no está pidiendo un valor, está pidiendo un objeto.
Ya, pero si se puede usar como parámetro (Query->ParamByName("Imagen")->AsBlob), alguna forma tiene que haber para llamar a ese objeto desde un FieldByName.
Solo como observación, me parece ver un problema de memory leak en esa función...
¿Dónde? A mi no me da ningún error de memoria.

ecfisa
13-10-2017, 16:58:52
¿Dónde? A mi no me da ningún error de memoria.

TJPEGImage* __fastcall BlobToJpg(TBlobField *CampoBLOB)
{
if(!CampoBLOB->Value.IsEmpty())
{
TJPEGImage *JPeg = new TJPEGImage; // <==== (1)
TMemoryStream *StreamTmp = new TMemoryStream; // <==== (2)
CampoBLOB->SaveToStream(StreamTmp); //sacamos el jpg y se convierte a stream
StreamTmp->Seek(0,soFromBeginning);
JPeg->LoadFromStream(StreamTmp);
return(JPeg);
}
else
return(NULL);
}

Tal vez no todavia...

Cuando solicitas recursos usando VCL es tu responsabilidad liberarlos, Builder C++ no liberará automáticamente la memoria ocupada, aún cuando se trate de variables locales a la función.

En ese código no se libera la variable SteramTmp; y lo mas serio es que no se puede liberar la variable JPeg sin dejar inoperante la función.

Saludos :)

TOPX
14-10-2017, 00:30:33
Cuando solicitas recursos usando VCL es tu responsabilidad liberarlos, Builder C++ no liberará automáticamente la memoria ocupada, aún cuando se trate de variables locales a la función.

Es muy importante esto que dice ecfisa (http://www.clubdelphi.com/foros/member.php?u=7718), hay que aplicar esa directriz siempre.

Verguenza para mí que no lo había pillado en el código inicial.
-

Angel.Matilla
14-10-2017, 10:56:53
¡Pues no me había dado cuenta! Tenéis razón. El problema es cómo liberar ambas variables ya que el jpeg es lo que devuelve como respuesta la función; no se me ocurre como. Acaso sea mejor sistema el que me sugieres en tu código, invocando en la función tanto el campo blob como el TImage en que ha de situarse; de esa forma se pueden liberar recursos. Voy a probarlo.