Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Gráficos (https://www.clubdelphi.com/foros/forumdisplay.php?f=8)
-   -   Bitmap a texto y texto a bitmap (https://www.clubdelphi.com/foros/showthread.php?t=52059)

kotai 09-01-2008 03:03:28

Bitmap a texto y texto a bitmap
 
Hola, he hecho un juego online en el que toda la comunicación se hace envíando cadenas de texto.

Ahora me gustaría poder enviar un TImage el cual siempre será un bitmap de 36 x 36 pixels y 256 colores. Pero no quería tocar el online del juego, me gustaría pasar el bitmap a una cadena de texto y enviarla. Cuando la reciba el otro jugador hay que convertirlo de nuevo a un bitmap, sabiendo que será un TImage de 36 x 36 pixels y 256 colores.

Me he fijado que si miras un form en formato texto los componentes TImage los pone como cadena con una propiedad Picture.Data
Código:

 
object Image1: TImage
    Left = 108
    Top = 68
    Width = 36
    Height = 36
    AutoSize = True
    Picture.Data = {  07544269746D61704609..... }
end

pero esta propiedad Picture.Data no existe, solo es para hacer el form.

También he probado a forzar el tipo Image1.Picture as String ó Image1.Picture.Bitmap as String pero no funciona.

¿ Alguna forma de hacerlo para poder envíar online la imagen como una cadena de texto ?

Gracias ;)

jachguate 09-01-2008 05:19:53

El editor de formularios de delphi almacena una representación hexadecimal de los datos del componente (propiedad data). Podrías hacer lo mismo, o también usar un codificador/decodificador como los que vienen con las INDY, que hacen justamente eso. Representan cualqueir objeto binario en texto y lo inverso.

Estos métodos se inventaron para transmitir datos binarios sobre protocolos basados en texto, como el caso de SMTP (envío de correos) y se siguen utilizando hoy en día.

TidEncoderMIME/TidDecoderMIME (base64)
TidEncoderUUE/TidDecoderUUE
TidEncoderQuotedPrintable/TidDecoderQuotedPrintable

Cito la ayuda de la clase base para estos componentes, TidEncoder

Cita:

Empezado por indy help
Declaration
TIdEncoder = class(TIdBaseComponent)
Summary
Ancestor for Indy encoder classes.
Description
TIdEncoder is a TIdBaseComponent descendant that is the ancestor for Indy classes that perform encoding operations on String- or Stream-based values, and returns the encoded value as a String value.

Hasta luego.

;)

seoane 09-01-2008 14:31:56

Ademas de lo dicho por jachguate puedes usar las funciones que describo aqui:

http://delphi.jmrds.com/?q=node/43

Con ellas podrás convertir un Stream en un String y luego hacer el proceso inverso, y junto con los métodos SaveToStream y LoadFromStream del TBitmap son todo lo que necesitas.

kotai 09-01-2008 16:40:35

Gracias a los dos por contestar.

Primero he estado mirando los componentes de Indy, pero no acabo de aclararme y no he encontrado ningún ejemplo.

Luego he probado las funciones de seoane, que es justo lo que buscaba, pero tengo un problema al recuperar el TBitmap desde el stream.

He hecho una prueba rápida:
Código Delphi [-]
     Stream1 := TMemoryStream.Create;
     Image1.Picture.Bitmap.SaveToStream(Stream1);
     Texto:= BinToStr(Stream1.Memory,Stream1.Size);
     Stream2 := TMemoryStream.Create;
     StrToStream(Texto,Stream2);
     Image2.Picture.Bitmap.LoadFromStream(Stream2);

Inicialmente las dos imágenes (TImage) tienen cargada una imagen del mismo tamaño y formato, pero son distintas, para ver si me copia el contenido de la imagen1 en la imagen2. Cuando lo pruebo la imagen2 se queda vacia, transparente.

El problema está en el Image2.Picture.Bitmap.LoadFromStream() porque he probado a cargar el stream1 y me pasa el mismo problema.

¿Hay que preparar el TBitmap o el TImage de alguna manera para que cargue bien desde el stream ?

Una vez cargada la imagen desde el strem he porbado a hacer un Image2.Rapaint, Image2.Update... pero sigue transparente.

Saludos ;)

jachguate 09-01-2008 16:48:23

Cita:

Empezado por kotai (Mensaje 256940)
Primero he estado mirando los componentes de Indy, pero no acabo de aclararme y no he encontrado ningún ejemplo.

Básicamente, creas una instancia del Encoder/Decoder y luego llamás al método correspondiente (Encode que recibe un stream y devuelve un string y Decode que recibe un string y devuelve un stream).

Pero si te vale lo de seoane, creo que puede resultar mas simple, aunque menos orientado a objetos :D

Cita:

Empezado por kotai (Mensaje 256940)
Luego he probado las funciones de seoane, que es justo lo que buscaba, pero tengo un problema al recuperar el TBitmap desde el stream.

Esto lo digo sin probarlo, pero se me ocurre que quizás Picture no esté preparado para que el bitmap contenido en ella se lea desde un stream.

Probá con esto:

Código Delphi [-]
  Stream1 := TMemoryStream.Create;
  Image1.Picture.Bitmap.SaveToStream(Stream1);
  Texto:= BinToStr(Stream1.Memory,Stream1.Size);
  Stream2 := TMemoryStream.Create;
  StrToStream(Texto,Stream2);
  Bitmap1 := TBitmap.Create();
  try   
    Bitmap1.LoadFromStream(Stream2);
    Image2.Picture := Bitmap1;
  finally
    Bitmap1.Free;
  end;

Hasta luego.

;)

seoane 09-01-2008 23:34:27

Cita:

Empezado por jachguate (Mensaje 256941)
Pero si te vale lo de seoane, creo que puede resultar mas simple, aunque menos orientado a objetos :D

Hago lo que puedo :D :p

Por otro lado, antes del LoadFromStream nunca viene mal colocar la posición del stream a cero:
Código Delphi [-]
Stream2.Position:= 0;
Por lo demás, la solución de jachguate me parece impecable.

jachguate 09-01-2008 23:43:47

ah.. claro está. Supongo que todos agarran el stream de la posición donde se encontraba, que seguramente es la última.

Como sugerencia... creo que el obligado a dejar el stream en la primera posición es la función StrToStream. No imagino un caso donde la llames y no querrás que el stream esté al inicio. Si lo hay, será la excepción de la regla.

Hasta luego.

;)

seoane 09-01-2008 23:57:51

Cita:

Empezado por jachguate (Mensaje 257056)
ah.. claro está. Supongo que todos agarran el stream de la posición donde se encontraba, que seguramente es la última.

Así es, de hecho, si juntas en un mismo stream varios bitmaps luego los puedes leer uno a uno con LoadFromStream (aquí un ejemplo)

Cita:

Empezado por jachguate (Mensaje 257056)
Como sugerencia... creo que el obligado a dejar el stream en la primera posición es la función StrToStream. No imagino un caso donde la llames y no querrás que el stream esté al inicio. Si lo hay, será la excepción de la regla.

No me gustan las funciones que toman decisiones por mi :p . Y además, a mi si que se me ocurren ejemplos donde no quiero que el stream vuelva automáticamente al inicio, por ejemplo, si mando el bitmap en varios trozos y quiero juntarlos en el destino. Cuestión de gustos supongo :D

jachguate 10-01-2008 00:08:11

Cita:

Empezado por seoane (Mensaje 257063)
No me gustan las funciones que toman decisiones por mi :p . Y además, a mi si que se me ocurren ejemplos donde no quiero que el stream vuelva automáticamente al inicio, por ejemplo, si mando el bitmap en varios trozos y quiero juntarlos en el destino. Cuestión de gustos supongo :D

Pues la función está tomando la decisión de dejarle en la última posición. Como dije en mi post anterior, creo que puede pensarse que es la excepción de la regla... aunque pensándolo bien... no hay reglas, puesto que no hay garantía alguna de que la función reciba un stream vacio..

Está bien, me has convencido... creo que simplemente vale aclararlo, por aquellos despistados (como yo) que puedan suponer lo contrario.. :D

Hasta luego.

;)

kotai 11-01-2008 10:00:43

Perdón por tardar tanto en contestar, pero antes de la respuesta 5 ya lo tenía solucionado con lo de Position=0, pero cambié el email del foro porque tenía uno viejo y no me llegaban los avisos de respuesta y hasta hoy no me ha llegado el email de reactivación y por eso no podía escribir.

Mirando ejemplos de uso de stream vi lo de position=0 y al ponerlo todo funcionó a la perfeccción.

He enviado imágenes de otro tamaño a ver que pasaba y también lo coge perfectamente.

Muchas gracias a los dos por la ayuda.

Por cierto si queréis ver el juego es: www.miniracingonline.com y la función la quería para que al entrar a una partida si no tienes alguno de los coches de los otros jugadores se te envíe por el online.

Saludos. ;)

kotai 14-01-2008 12:01:57

Hola de nuevo.

Como comenté las funciones van perfectas y ya puedo envíar las imágenes online.

La gente ha empezado a probar el juego y de momento, un jugador japonés que tiene windows 2000 service pack 4 con Internet explore 6 actualizado le da un error al arrancar el juego que dice que no encuentra la función (no recuerdo cual de las dos que usa el código) en la librería cryt32.dll y se cierra el juego. Le he pasado mi librería del windows vista para que la ponga en la carpeta del juego, pero sigue dando el mismo error, por lo visto intenta cargarla desde Winnt\system32 y no desde la carpeta del juego. También le propuse que hiciera una copia de su DLL y pusiera la mía en system32, pero no es plan que toda la gente que tenga Win2000 o inferior tengan que machacar librerías del windows, de todas formas no le deja sobreescribirla, por lo que ya no quise insistir en que entrara en modo a prueba de fallos.
Lo que me gustaría saber es que parche o update oficial de microsoft hay que pasar al Windows 2000 con SP4 para que actualice esa DLL, y ponerlo como requisitos del juego.
¿ Alguien lo sabe ?

Otra opción es poner una condición en el compilador {$IFDEF ....} {$ENDIF} donde se definen esas funciones y luego se llaman, pero la condición debería ser si la librería contiene esas dos funciones, y eso no se como hacerlo. Podría poner que solo funcione con windowsxp y vista, pero prefiero que le funcione a la mayor gente posible.
¿ Alguna idea de hacerlo, u otra solución mejor ?

Gracias.;)

seoane 14-01-2008 14:29:31

Aquí el compañero marceloalegre afirma que instalando las librerías nuevas funciona bien:
http://www.clubdelphi.com/foros/show...8&postcount=14

Pero si no quieres depender del sistema operativo, ni tener que instalar nada, puedes buscar en internet alguna librería para codificar en base64. Seguro que encuentras un montón, ya que este tipo de codificación es ampliamente utilizada.

kotai 15-01-2008 18:16:43

Hola de nuevo.

Pues todas las funciones que he encontrado son para pasar de String a String.

Al final he decidido hacerlo por condición en el compilador {$IFDEF ....} {$ENDIF} y que solo funcione con WinXP, Win2003 o Win Vista pero no se como detectar eso en tiempo de compilación.

¿ Que debería poner en el {$IFDEF ....} para que solo se compile cuando sea un WinXP o superior ?

Gracias.

seoane 15-01-2008 22:25:43

Una solución puede ser comprobar la existencias de las funciones antes de utilizarlas, para eso vamos a cargarlas de forma dinámica en vez de vincularlas a nuestro programa de forma estática, y si no las encontramos lanzamos una excepción que deberás de manejar en tu programa utilizando un bloque try ... except.

La cosa queda mas o menos así:
Código Delphi [-]
unit base64;

interface

uses Windows, SysUtils, Classes;

function BinToStr(Binary: PByte; Len: Cardinal): String;
procedure StrToStream(Str: String; Stream: TStream);

implementation

const
  CRYPT_STRING_BASE64HEADER = 0;
  CRYPT_STRING_BASE64 = 1;
  CRYPT_STRING_BINARY = 2;
  CRYPT_STRING_BASE64REQUESTHEADER = 3;
  CRYPT_STRING_HEX = 4;
  CRYPT_STRING_HEXASCII = 5;
  CRYPT_STRING_BASE64X509CRLHEADER = 9;
  CRYPT_STRING_HEXADDR = 10;
  CRYPT_STRING_HEXASCIIADDR = 11;
  CRYPT_STRING_HEXRAW = 12;
  CRYPT_STRING_NOCRLF = $40000000;
  CRYPT_STRING_NOCR = $80000000; 

function BinToStr(Binary: PByte; Len: Cardinal): String;
var
  Dll: HModule;
  Count: DWORD;
  CryptBinaryToString: function (pbBinary: PByte; cbBinary: DWORD; dwFlags: DWORD;
    pszString: PChar; var pcchString: DWORD): BOOL; stdcall;
begin
  Dll:= LoadLibrary('Crypt32.dll');
  if Dll <> 0 then
  try
    @CryptBinaryToString:= GetProcAddress(Dll,'CryptBinaryToStringA');
    if @CryptBinaryToString <> nil then
    begin
      Count:= 0;
      if CryptBinaryToString(Binary,Len,12,nil,Count) then
      begin
        SetLength(Result,Count);
        if not CryptBinaryToString(Binary,Len,12,PChar(Result),
          Count) then
          Result:= EmptyStr;
      end;
    end else
      raise Exception.Create('No encuentro CryptBinaryToStringA');
  finally
    FreeLibrary(Dll);
  end else
    raise Exception.Create('No encuentro Crypt32.dll');
end;

procedure StrToStream(Str: String; Stream: TStream);
var
  Dll: HModule;
  Buffer: PByte;
  Count: DWORD;
  CryptStringToBinary: function (pszString: PChar; cchString: DWORD; dwFlags: DWORD;
    pbBinary: PByte; var pcbBinary: DWORD; pdwSkip: PDWORD;
    pdwFlags: PDWORD): BOOL; stdcall;
begin
  Dll:= LoadLibrary('Crypt32.dll');
  if Dll <> 0 then
  try
    @CryptStringToBinary:= GetProcAddress(Dll,'CryptStringToBinaryA');
    if @CryptStringToBinary <> nil then
    begin
      Count:= 0;
      if CryptStringToBinary(PChar(Str),Length(Str),CRYPT_STRING_BASE64,nil,Count,
        nil,nil) then
      begin
        GetMem(Buffer,Count);
        try
          if CryptStringToBinary(PChar(Str),Length(Str),CRYPT_STRING_BASE64,Buffer,
            Count,nil,nil) then
            Stream.WriteBuffer(Buffer^,Count);
        finally
          FreeMem(Buffer);
        end;
      end;
    end else
      raise Exception.Create('No encuentro CryptStringToBinaryA');
  finally
    FreeLibrary(Dll);
  end else
    raise Exception.Create('No encuentro Crypt32.dll');
end;

end.

Y cuando la uses utiliza algo como esto:
Código Delphi [-]
try
  BinToStr( ...
except
  // Si llegamos hasta aqui es que las funciones no existen
end;

¿Que te parece? es solo una improvisación pero puede servir.

kotai 16-01-2008 01:14:17

Muchas gracias, va perfecto.

Pues no me quedan cosas que aprender de delphi....

Saludos. ;)

jachguate 16-01-2008 06:02:31

Cita:

Empezado por kotai (Mensaje 258352)
Pues no me quedan cosas que aprender de delphi....

:eek: :eek: :eek:
Quisiera estar en tu situación... a mi me falta tanto por aprender... :rolleyes:


La franja horaria es GMT +2. Ahora son las 06:57:01.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi