31 de julio de 2007

Codigo de barra EAN 128 en FoxPro

Una clase para descargar del sitio de José Guillermo Ortiz Hernández, que permite incluir código EAN 128 en aplicaciones Visual FoxPro.

En el sitio Tortuga Productiva dedicado a Visual FoxPro, está publicado el siguiente artículo con código fuente incluido que permite incluir códigos de barra EAN 128 en aplicaciones Visual FoxPro.

-- El EAN 128, Uso e implementación en FoxPro --
http://www.tortugaproductiva.galeon.com/docs/ean128/index.html



23 de julio de 2007

Nueva versión de GradObjects

Artículo original: New version of GradObjects
http://weblogs.foxite.com/vfpimaging/archive/2007/06/28/4212.aspx
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York


Acabo de subir una nueva versión de la clase GradObjects

Gracias a Nigel por informar de un error que ocurría cuando se utilizaban objetos OptionsGroups gráficos.

He aquí la versión actualizada.

http://www.geocities.com/macmarbr/gradobjects.zip
http://weblogs.foxite.com/files/vfpimaging/gradobjects/gradobjects.zip





Se puede obtener más información en el escrito original:

GRADIENT OBJECTS WITH GDI+
http://weblogs.foxite.com/vfpimaging/archive/2006/07/26/2076.aspx

20 de julio de 2007

Incruste imágenes a sus correos electrónicos con CDOSYS

Artículo original: Embed images to your emails with CDOSYS
http://weblogs.foxite.com/vfpimaging/archive/2007/07/03/4256.aspx
Autor: Cesar Ch.
Traducido por: Luis María Guayán

Basado en algunos excelentes ejemplos que encontré en la Web, especialmente los de Mike Gagnon, he creado mi propia clase para enviar correos electrónicos. Decidí usar CDOSYS (Microsoft Collaboration Data Objects for Windows 2000) porque esto viene con Win2000 / XP / Vista y 2003, y permite enviar mensajes de varios maneras, especialmente incrustando imágenes que son usadas en el contenido HTML del mensaje.

MAPI era mi primera opción, pero lamentablemente esto no permite incrustar imágenes, sólo son permitidos los adjuntos. La automatización de MS Outlook también funcionaría, pero no lo tengo.

Abajo está un simple código que genera una página HTML que contiene algunas imágenes que deben ser enviadas. Las imágenes son incrustadas al mensaje, ¡no como simples adjuntos!. Este permite que mostremos las imágenes en el sitio y con las características que deseamos.

Aparte de las características más comunes, esto también permite que incruste imágenes, agregue adjuntos, configure la prioridad, solicite confirmación de lectura y envíe HTML.

La propiedad "Body" de la clase acepta 3 diferentes tipos de parámetros: 1-un código de HTML, 2-una URL (CDOSYS incrustará la página entera en el mensaje) o 3-un archivo HTML que será incustado. Para esta última opción, mis pruebas fallaron cuándo usé espacios en el nombre de directorio y/o en el nombre del archivo, ¡entonces evite espacios en nombres del archivo!.

Las imágenes de abajo muestran algunas pantallas de ejemplo que generé y recibí, utilizando el código de abajo, abriendo con Outlook Express. Esto también fue probado con algunos correos Web, como por ejemplo Hotmail y los resultados son similares.





Guarde el código de abajo como un PRG, y no olvide cambiar la información en Rojo por la propia, y ejecútelo.


19 de julio de 2007

Informe VFP a PowerPoint

He visto peticiones para pasar un informe de VFP a PowerPoint, aquí tengo un código para hacerlo.
*** Creamos un Objeto de PowerPoint
oPPT=CREATEOBJECT('POWERPOINT.APPLICATION')
PPTPRES=oPPT.PRESENTATIONS.ADD(1)
PPTSLIDE1=PPTPRES.SLIDES.ADD(1,1)

*** Creamos un Cuadro de Texto
oTXT1=PptSlide1.Shapes.AddTextBox(1,5,220,700,25)

WITH oTXT1
 .Line.ForeColor.RGB=RGB(180,180,180)
 .LINE.VISIBLE=.T.
 .TEXTFRAME.TEXTRANGE.TEXT='ING. ERIK REYES LIMA'
 .TEXTFRAME.TEXTRANGE.FONT.SIZE=18
 .TEXTFRAME.TEXTRANGE.FONT.BOLD=.T.
 .FILL.ForeColor.RGB = rgb(255,255,255)
 .FILL.VISIBLE=.T.
 .Shadow.ForeColor.RGB = RGB(180, 0, 119)
 .Shadow.VISIBLE=.T. 
ENDWITH

*** CREAMOS UNA LINEA
oLine1 = pptslide1.Shapes.AddLine(0,43,720,43)
WITH oLine1.Line
 .ForeColor.RGB = rgb(180,0,119)
 .Weight = 2
ENDWITH

*** CREAMOS UNA IMAGEN
PptSlide1.Shapes.Addpicture('C:\FOTOS\MIFOTO.JPG',1,1,0,0,70,40)

*** ACTIVAMOS EL POWERPOINT
OPPT.VISIBLE = .T.
[Sadex]

17 de julio de 2007

VFP9 a MySQL5 - Almacenamiento de Imágenes

Últimante he leído en los foros sobre gente que tiene problemas a la hora de guardar imágenes en una base de datos de MySQL.

Vamos a solucionar esto desglosando tres pequeños ejemplos de almacenamiento de imágenes en MySQL.
  1. Crear una tabla en MySQL para almacenar las imágenes.
  2. Archivar una imágen en un campo de la tabla de nuestra base de datos.
  3. Proceder a su descarga posterior.
1. Crear una tabla de MySQL

Vamos a tener que crear una pequeña tabla en nuestra base de datos MySQL con la siguiente estructura:

CODIGO, DESCRIPCION, FOTO (Atención al campo foto ya que será del tipo BLOB)

Crearemos la tabla desde nuestro querido VFP.
********************************************
* CREACION DE TABLA (albumfotos)
********************************************

LOCAL CSQL, NH, CCADENA
CSQL=""
NH=0
CCADENA=""
CSQL= "DRIVER={MySQL ODBC 3.51 Driver};" + ;
"SERVER=127.0.0.1;" + ;
"PORT=3306;" + ;
"UID=usuario;" + ;
"PWD=pasword;" + ;
"DATABASE=mybasededatos;" + ;
"OPTIONS=2049;"

NH=SQLSTRINGCONNECT(""+CSQL, .T.)
IF NH > 0
        SQLSETPROP(NH,'Asynchronous', .T.)
        SQLSETPROP(NH,'BatchMode', .T.)
        TEXT TO CSQL TEXTMERGE NOSHOW

            CREATE TABLE albumfotos (
                codigo char(03) NOT NULL default '000',
                descripcion varchar(50) default '',
                foto longblob, 
                PRIMARY KEY (codigo)) 
                ENGINE=InnoDB ROW_FORMAT=DYNAMIC

        ENDTEXT
        SQLPREPARE(NH,""+CSQL)
        SQLEXEC(NH)

        WAIT WINDOW 'Tabla Creada'

        SQLDISCONNECT(NH)
 
ENDIF

RELEASE CSQL,NH,CCADENA
*************************************************************

2. Archivar imagen en la tabla de MySQL.

Vamos a archivar una imagen en la tabla (albumfotos). La imagen estará ubicada en el directorio raiz C:\ y será el archivo de imagen FOTO001.JPG

Crearemos una variable en VFP del tipo BLOB que contendrá la imagen antes de su posterior grabación: (En este caso omitiremos las ordenes de conexión a la base de datos)
********************************************
* ARCHIVAR IMAGEN 
********************************************

LOCAL BIMAGEN AS BLOB
BIMAGEN=(0h)         && Inicializamos la Variable
BIMAGEN=FILETOSTR("C:\FOTO001.JPG")

TEXT TO CSQL TEXTMERGE NOSHOW

        REPLACE INTO albumfotos 
                SET   CODIGO='001', 
                        DESCRIPCION='FOTO-001', 
                        FOTO=?BIMAGEN
 
ENDTEXT
SQLPREPARE(NH,""+CSQL)
SQLEXEC(NH)

WAIT WINDOW 'Imagen Grabada'

RELEASE BIMAGEN
*************************************************************

3. Extraer imagen de la tabla de MySQL.

Vamos a Extraer la misma imagen de la tabla de MySQL y ahora la guardaremos en el directorio fotos. (Obviamente en este caso también omitiremos las ordenes de conexión a la base de datos)
********************************************
* EXTRAER IMAGEN 
********************************************

TEXT TO CSQL TEXTMERGE NOSHOW

    SELECT FOTO
        FROM albumfotos WHERE CODIGO='001' 
ENDTEXT
SQLPREPARE(NH,""+CSQL,"TCURSOR")
SQLEXEC(NH)
SELECT TCURSOR
IF RECCOUNT() > 0
    STRTOFILE(TCURSOR.FOTO,"C:\FOTOS\FOTO001.JPG")
ENDIF

CLOSE TABLES ALL

********************************************
Esto no solo sirve para almacenar imágenes solamente, También es posible almacenar documentos PDF, Word, lo que se nos ocurra. Yo por ejemplo lo utilizo para almacenar cada logo de empresa en mi base de datos, para posteriormente imprimirlo en la cabecera de las facturas e informes.


Antonio L. Montagut
www.ontarioxb.es


16 de julio de 2007

SQL Select - Bufferred Queries

Artículo original: SQL Select - Bufferred Queries
http://rickschummer.com/blog/2007/07/sql-select-bufferred-queries.html
Autor: Rick Schummer
Traducido por: Luis María Guayán - 16/07/2007

Utilicé hoy por primera vez, una nueva característica de VFP 9 en un escenario del mundo real: la nueva capacidad de consultar datos de un cursor con buffering, usando la nueva cláusula WITH (BUFFERING = .T.)

Sé que hay desarrolladores de VFP y administradores de bases de datos que dirán que usar esta sintaxis es absolutamente incorrecta porque los datos aun pueden ser revertidos, y así el resultado de la consulta no es reproducible. La información se podría utilizar incorrectamente para tomar decisiones de negocio y esto no es elegante, y no es un enfoque profesional. Estos individuos están absolutamente acertados, cuando necesitas tener control y equilibrio en la base de datos, integridad de los datos en la base de datos, e integridad en informes y análisis.

Por otra parte, yo tenía que usar esta nueva funcionalidad para comprobar la validez de los datos antes de que estos fueran grabados en la base de datos. Aquí está mi escenario:
  1. Tengo datos para importar de una fuente exterior.
  2. Tengo datos para importar de una fuente interna, pero exportados de una aplicación diferente.
  3. Hay una gran posibilidad de datos duplicados en las dos fuentes de datos.
  4. Todos los datos se importan a cursores con buffering.
  5. Tengo que ejecutar la importación de los datos a través de tres tablas distintas, de una vez, o no. Dos de las tablas están relacionadas.
Comienzo la transacción e importo los datos de la fuente interna, después importo los datos de la fuente externa comprobando para saber si hay duplicados. Quiero revisarlos antes de enviar la transacción. En el proceso de la revisión consulto las tablas para asegurarme de que no hay personas duplicadas, ningún vendedor duplicado, y necesito verificar que todas los registraciones son tomadas en el año apropiado.

Aquí está mi consulta para contar el número de la personas con registros duplicados:
SELECT cLastName, cFirstName, COUNT(*) as nCount ;
  FROM people WITH (BUFFERING = .T.) ;
    JOIN registration WITH (BUFFERING = .T.) ;
      ON people.cPeople_PK = registration.cPeople_FK  ;
  GROUP BY cLastName, cFirstName ;
  HAVING nCount > 1 ;
  INTO CURSOR curMultiYearRegistrations
Antes de la importación conté los registros de los datos de ejemplo para hacer mis comparaciones. Esta división y manejo de los datos almacenados en el buffer me ahorró muchos la molestia de quitar los datos importados cuando encontraba errores. Habría podido hacer volar la base de datos y restaurar una backup, pero ésa habría tomado más tiempo. Habría podido importar a otra área, pero con esto habría hecho dos veces el trabajo. Podía suspender el proceso de la importación y hacer algunas consultas específicas para verificar que todo está bien durante cada paso de la importación.

Tengo otra idea donde esta nueva sintaxis ayudará al producto HackCX Professional. Espero agregar en la próxima versión un informe o resumen en pantalla, así se puedan ver todos los cambios almacenados en buffer que se realizaron en la biblioteca de clases que esta cambiando. Seguro que puedo hacer esto sin una sintaxis SQL Select, pero pienso que que escribir un simple SQL Select es más rápido y menos código que recorrer toda la tabla para determinar qué registros han cambiado.
¿Has utilizado esta nueva sintaxis? Si es así ¿Para qué?

6 de julio de 2007

VFP 9 a MySQL5

Voy a Intentar en las próximas semanas escribir una serie de artículos sobre la utilización de VFP atacando bases de datos MySQL. Estos artículos reflejan en cierta manera la experiencia de mi trabajo diario con MySQL.

Función UUID()

El propósito de esta función es devolver un identificador único universal. Un UUID es un número de 128 bits representado por una cadena de cinco números hexadecimales en el formato:
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee. 
Un UUID está diseñado para ser un único número globalmente en el espacio y el tiempo.

Es de suponer pués que dos llamadas a UUID() generen dos valores diferentes, aunque esas llamadas se realizen en dos ordenadores separados físicamente y que no están conectados en red.

La utilidad de esta función está mas que clara. Nos permite obtener un identificador único por cada registro de una tabla, podemos utilizar esta cadena como clave primaria de la tabla, esto nos permitiria que en el caso de replicación de la base de datos, registros de la misma tabla pero en diferentes bases de datos no se sobreescribieran entre si. Cosa que si prodría pasar si utilizáramos como clave primaria campos autoincrementales.

Ya que al estar en bases de datos diferentes podría darse el caso de tener registros duplicados con el mismo identificador.

Vamos ha aplicar esta función con nuestro querido FOX:
********************************************
* Ejemplo de uso función UUID 
********************************************

LOCAL CSQL, NH, CCADENA
CSQL = ""
NH = 0
CCADENA = ""
CSQL = "DRIVER={MySQL ODBC 3.51 Driver};" + ;
  "SERVER=127.0.0.1;" + ;
  "PORT=3306;" + ;
  "UID=usuario;" + ;
  "PWD=pasword;" + ;
  "DATABASE=mybasededatos;" + ;
  "OPTIONS=2049;"

NH = SQLSTRINGCONNECT("" + CSQL, .T.)
IF NH > 0
  SQLSETPROP(NH,'Asynchronous', .F.)
  SQLSETPROP(NH,'BatchMode', .T.)
  CCADENA = PADR("",32,'0')
  SQLPREPARE(NH, "SELECT UUID()","CURSOR")
  SQLEXEC(NH)
  SELECT CURSOR
  IF RECCOUNT()>0
    CCADENA = LEFT(FIELD(1),08)+ ; 
      SUBSTR(FIELD(1),10,04)+ ;
      SUBSTR(FIELD(1),15,04)+ ;
      SUBSTR(FIELD(1),20,04)+ ;
      RIGHT(FIELD(1),12)
  ENDIF

  WAIT WINDOW CCADENA && Muestra la Cadena
  SELECT CURSOR
  USE
ENDIF
*************************************************************
Antonio L. Montagut
www.ontarioxb.es

4 de julio de 2007

Diferentes formas de obtener las dimensiones de las imágenes (ancho y alto)

Artículo original: Different ways to get Image Dimensions (width and height)
http://weblogs.foxite.com/vfpimaging/archive/2007/05/28/3862.aspx
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York

Una de las preguntas más comunes que he visto en los foros de VFP relacionadas con imágenes es "¿Cómo puedo obtener las dimensiones de un archivo de imagen?"

Afortunadamente, esto es muy fácil de obtener.

A continuación muestro varias técnicas que podemos utilizar, con una breve explicación. cada desarrollador puede escoger cuál de ellas es mejor para sus necesidades. Todas las técnicas son muy sencillas de utilizar. Para usuarios GDI+, lo muestro empleando 3 bibliotecas con las que ya he trabajado. Como bono, la última muestra cómo obtenerlo con GDI+ utilizando llamadas directas a API, para los que no quieren utilizar una clase envoltorio GDI+.

Todos los ejemplos a continuación son libres. Estoy seguro de que hay otras formas de hacerlo. Señalo solamente aquellas que conozco y he probado.

1.- Cargar la imagen en un objeto Image VFP

Esta técnica trabaja con cualquier versión VFP. Cuando asociamos un archivo a la propiedad PicturuFile de un objeto Image, va a guardar las dimensiones en las propiedades Width y Height.
* Técnica 1
* Obtener las dimensiones del objeto Image 
LOCAL lcPictureFile
lcPictureFile = GETPICT()
LOCAL lnWidth, lnHeight
LOCAL loVFPImg as Image
loVFPImg = CREATEOBJECT("Image")
loVFPImg.Picture = lcPictureFile
lnWidth = loVFPImg.Width
lnHeight = loVFPImg.Height
loVFPImg = NULL
MESSAGEBOX("Objeto Image VFP " + CHR(13) + ;
  "Dimensiones: " + TRANSFORM(lnWIdth) + " x " + TRANSFORM(lnHeight))

2.- Utilizar la función LOADPICTURE()

El objeto devuelto por LOADPICTURE() es una instancia de la clase StdPicture. Los valores de altura y ancho son unidades HiMetric (1 HiMetric = 0.01 milímetro).

No estoy realmente seguro de cuál es la forma correcta para utilizar esta función para obtener las dimensiones de una imagen.

Lisa Slater Nicholls publicó hace mucho tiempo una función para obtener las dimensiones; pero a mí no me ha funcionado: http://www.spacefold.com/lisa/lisa_fx4.htm#loadpicture

En una discusión en Foxite, entre Eric den Doop y Boudewijn Lutgerik: http://www.foxite.com/archives/what-mysterious-calculation-is-behind-this-0000067503.htm, Boudewijn obtuvo las dimensiones utilizando un factor diferente.

En mi caso, escogí la técnica que encontré en http://www.xtremevbtalk.com/archive/index.php/t-13097.html

¡ Pero no confío en este proceder porque parece que depende de la resolución de la pantalla !


* Técnica 2
* Obtener las dimensiones del objeto Imagen utilizando la función LOADPICT()
* Código de Lisa Slater Nicholls
* El objeto devuelto por LOADPIC() es una instancia de la clase StdPicture. 
* Los valores de altura y ancho son unidades HiMetric (1 HiMetric = 0.01 milímetro). 
* http://www.foxite.com/archives/what-mysterious-calculation-is-behind-this-0000067503.htm  
* http://www.xtremevbtalk.com/archive/index.php/t-13097.html 
LOCAL lcPictureFile
lcPictureFile = GETPICT()
LOCAL lnWidth, lnHeight
LOCAL loLoadedPict
loLoadedPict = LOADPICTURE(lcPictureFile)
lnHeight = ROUND((loLoadedPict.Height / 26.45454545455),0)
lnWidth = ROUND((loLoadedPict.Width / 26.45454545455),0)
loLoadedPict = NULL
MESSAGEBOX("Función VFP LOADPICTURE()" + CHR(13) + ;
  "Dimensiones: " + TRANSFORM(lnWidth) + " x " + TRANSFORM(lnHeight))

3.- Obtener la información de la cabecera del archivo imagen

Las dimensiones de la imagen se almacenan en sus binarios. El siguiente código utiliza FOPEN y FREAD para extraer esa información. Puede obtener las dimensiones para las imágenes tipo BMP, GIF y JPG.
* Técnica 3
* Obtener la información de la cabecera del archivo imagen
* Los ejemplos se han adaptado a partir de las funciones de Thomas Gehrke en West-Wind Wiki:
* http://www.west-wind.com/wiki/wc.dll?wc~JpgSizeFunction  
* http://www.west-wind.com/wiki/wc.dll?wc~GifSizeFunction 
LOCAL lcPictureFile
lcPictureFile = GETPICT()
LOCAL lnWidth, lnHeight, lcExt
lcExt = LOWER(JUSTEXT(lcPictureFile))
DO CASE
  CASE lcExt = "bmp"
    LOCAL lnHandle, lcBytes
    lnHandle = FOPEN(lcPictureFile)
    IF m.lnHandle > -1
      * Leer los primeros 27 bytes:
      lcBytes = FREAD( m.lnHandle, 27)
      = FCLOSE( m.lnHandle)
      lnWidth = CTOBIN(SUBSTR(lcBytes,19,4),"4RS")
      lnHeight = CTOBIN(SUBSTR(lcBytes,23,4),"4RS")
    ENDIF
  CASE lcExt = "gif"
    LOCAL lnHandle, lcBytes
    lnHandle = FOPEN(lcPictureFile)
    IF m.lnHandle > -1
      * Leer los primeros 10 bytes:
      lcBytes = FREAD( m.lnHandle, 10)
      = FCLOSE( m.lnHandle)
      lnWidth = CTOBIN(SUBSTR( m.lcBytes, 7, 2),"2RS")
      lnHeight = CTOBIN(SUBSTR( m.lcBytes, 9, 2),"2RS")
    ENDIF
  CASE lcExt = "jpg"
    LOCAL lnHandle, lcBytes
    lnHandle = FOPEN(lcPictureFile)
    IF m.lnHandle > -1
      LOCAL lnFileSize, lnCounter, lcBytes
      lnFileSize = FSEEK( m.lnHandle, 0, 2)
      lnCounter = 0
      = FSEEK( m.lnHandle, 0, 0)
      FOR lnCounter = 1 TO m.lnFileSize - 2
        lcBytes = FREAD( m.lnHandle, 3)  
        IF m.lcBytes = CHR(0) + CHR(17) + CHR(8) OR ;
          m.lcBytes = CHR(0) + CHR(11) + CHR(8)
          * ¡Encontrar las marcas de bloque para las dimensiones!
          lcBytes = FREAD( m.lnHandle, 4)
          EXIT
        ELSE
          = FSEEK( m.lnHandle, -2, 1)
        ENDIF
      ENDFOR
      = FCLOSE( m.lnHandle)

      lnWidth = CTOBIN(SUBSTR( m.lcBytes, 3, 2),"2S")
      lnHeight = CTOBIN(SUBSTR( m.lcBytes, 1, 2),"2S")
    ENDIF
ENDCASE
MESSAGEBOX("Información desde la cabecera de imagen" + CHR(13) + ;
  "Dimensiones: " + TRANSFORM(lnWIdth) + " x " + TRANSFORM(lnHeight))

4.- Utilizar GDIPlusX

GDI+ puede brindar esta información y muchas otras, fácilmente.

El ejemplo que muestro a continuación utiliza la biblioteca GdiPlusX de VFPX.
http://www.codeplex.com/VFPX/Wiki/View.aspx?title=GDIPlusX&referringTitle=Home
* Técnica 4
* Obtener la información de la imagen utilizando GDI+, con GdiPlusX
* http://www.codeplex.com/VFPX/Wiki/View.aspx?title=GDIPlusX&referringTitle=Home  
LOCAL lcPictureFile
lcPictureFile = GETPICT()
LOCAL lnWidth, lnHeight
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx")))
WITH _SCREEN.System.Drawing
  * Crear un Bitmap nuevo
  LOCAL loBmp as xfcBitmap
  loBmp = .Bitmap.FromFile(lcPictureFile)
  lnWidth = loBmp.Width
  lnHeight = loBmp.Height
  loBmp = NULL
  MESSAGEBOX("Biblioteca GDI+ - GdiPlusX" + CHR(13) + ;
    "Dimensiones: " + TRANSFORM(lnWIdth) + " x " + TRANSFORM(lnHeight))
ENDWITH

5.- Utilizar GPIMAGE2

El ejemplo que se muestra a continuación utiliza la biblioteca GpImage de Alexander Golovlev. En el siguiente enlace puede encontrar la versión más reciente para esta clase que contiene algunas funciones Graphics, que he agregado: http://cchalom.sites.uol.com.br/GPIMAGE
* Técnica 5
* Obtener la información de la imagen utilizando GDI+, con GpImage2
* http://cchalom.sites.uol.com.br/GPIMAGE 
LOCAL lcPictureFile
lcPictureFile = GETPICT()
LOCAL lnWidth, lnHeight
IF Not "gpImage" $ SET("Procedure")
  SET PROCEDURE TO gpImage ADDITIVE
ENDIF
loGdip = CREATEOBJECT("gpInit")
loBmp = CREATEOBJECT("gpImage")
loBmp.Load(lcPictureFile)
lnWidth = loBmp.ImageWidth
lnHeight = loBmp.ImageHeight
loBmp = NULL
loGdip = NULL
MESSAGEBOX("GDI+ library - GpImage2" + CHR(13) + ;
  "Dimensiones: " + TRANSFORM(lnWIdth) + " x " + TRANSFORM(lnHeight))

6.- Utilizar _GDIPLUS.VCX

El siguiente ejemplo utiliza la biblioteca de clases Foundations (FCC) _GdiPlus.vcx de Walter Nicholls, liberada con VFP9.
* Técnica 6
* Obtener la información de la imagen utilizando GDI+, con _GdiPlus.vcx, de Walter Nicholls
LOCAL lcPictureFile
lcPictureFile = GETPICT()
LOCAL lnWidth, lnHeight
LOCAL loBitmap as GpBitmap OF HOME() + "/FFC/_GdiPlus.vcx"
loBitmap = NEWOBJECT("GpBitmap", HOME() + "/FFC/_GdiPlus.vcx")
loBitmap.CreateFromFile(lcPictureFile)
lnWidth = loBitmap.ImageWidth
lnHeight = loBitmap.ImageHeight
loBitmap = NULL
MESSAGEBOX("Biblioteca GDI+ - FFC _GdiPlus.vcx" + CHR(13) + ;
  "Dimensiones: " + TRANSFORM(lnWIdth) + " x " + TRANSFORM(lnHeight))

7.- Utilizar GDIPLUS con llamadas directas a API

Si no desea utilizar ninguna clase envoltorio GDI+, he aquí un código que obtiene las dimensiones de la imagen utilizando llamadas directas de API a gdiplus.dll. Asegúrese de tener gdiplus.dll en su sistema.

Gdiplus.dll está disponible gratis y se puede instalar con Win98 y superior. Si no tiene instalado WinXP or .NET runtime entonces debe bajar esta DLL desde http://www.microsoft.com/downloads/details.aspx?FamilyID=6a63ab9c-df12-4d41-933c-be590feaa05a&DisplayLang=en o directamente desde http://download.microsoft.com/download/platformsdk/redist/3097/W98NT42KMeXP/EN-US/gdiplus_dnld.exe

Puede obtener información valiosa sobre GDIPlus en el sitio oficial de Microsoft
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdicpp/GDIPlus/GDIPlusreference/flatgraphics.asp
* Técnica 7
* Obtener información de la imagen utilizando GDI+, con lamadas directas de API
LOCAL lcPictureFile
lcPictureFile = GETPICT()

* API Declarations for GDI+
DECLARE LONG GdiplusStartup IN GDIPLUS.DLL ;
  LONG @ token, STRING @ INPUT, LONG @ OUTPUT
DECLARE LONG GdiplusShutdown IN GDIPLUS.DLL LONG token
DECLARE INTEGER GdipLoadImageFromFile IN GDIPLUS.DLL ;
  STRING wFilename, INTEGER @ nImage
DECLARE INTEGER GdipDisposeImage IN GDIPLUS.DLL INTEGER nImage
DECLARE INTEGER GdipGetImageWidth IN GDIPLUS.DLL ;
  INTEGER nImage, INTEGER @ nWidth
DECLARE INTEGER GdipGetImageHeight IN GDIPLUS.DLL ;
  INTEGER nImage, INTEGER @ nHeight

* Inicializar GDI+.
LOCAL gdiplusStartupInput, lhGdiPlusToken
gdiplusStartupInput = CHR(1) + REPLICATE(CHR(0), 15)
lhGdiPlusToken = 0
IF GdiplusStartup(@lhGdiPlusToken, @gdiplusStartupInput, 0) != 0
  RETURN .F.
ENDIF
* Cargar el objeto Picture
LOCAL lnPictHandle
lnPictHandle = 0
= GdipLoadImageFromFile( STRCONV(lcPictureFile + CHR(0),5), @lnPictHandle)
* Obtener las dimensiones de la imagen
LOCAL lnWidth, lnHeight
STORE 0 TO lnWidth, lnHeight
= GdipGetImageWidth(lnPictHandle, @lnWidth)
= GdipGetImageHeight(lnPictHandle, @lnHeight)
* Limpiar el controlador principal GDI+ 
= GdiplusShutdown(lhGdiPlusToken)
MESSAGEBOX("GDI+ con llamadas API" + CHR(13) + ;
  "Dimensiones: " + TRANSFORM(lnWidth) + " x " + TRANSFORM(lnHeight))

Espero que esto ayude

¡ Que lo disfruten !

2 de julio de 2007

Convertir imágenes a color monocromático con GdiPlusX

Artículo original: Convert Images to Monochrome with GdiPlus X
http://weblogs.foxite.com/vfpimaging/archive/2007/05/26/3857.aspx
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York


Cada cierto tiempo, alguien pregunta sobre la creación de imágenes monocromáticas, que en la mayoría de los casos se utilizarán para enviar por Fax.

Desafortunadamente, GDI+ no brinda soporte para imágenes indexadas a 1 bpp (bipmaps monocromos)

Pero con la ayuda de Anatoliy Mogylevets y Mike Gagnon pude encontrar que la buena y vieja GDI (no GdiPlus) ofrece algunas funciones (CopyImage y LoadImage) para hacerlo en un flash. Entonces, estuvimos de acuerdo en añadir esta importante funcionalidad a la biblioteca como un método nuevo agregado a las clases Image y Bitmap: GetMonochrome().

Pasos para obtener la versión 1bpp monocromática de cualquier imagen utilizando las clases GDI+X
  1. Cargar la imagen con GdiPlus
  2. Llamar a Image.GetMonochrome para obtener un objeto nuevo que contenga la imagen 1bpp.
  3. Guardar normalmente como Bmp.
IMPORTANTE:
Todos los ejemplos siguientes utilizan la nueva biblioteca GdiPlus-X, que está en versión ALPHA; pero que es muy estable y fiable para hacer la mayoría de las tareas GDI+. Descargue la versión más actualizada desde Codeplex:

http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX&title=GDIPlusX

Este es el código de ejemplo:
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx")))
WITH _SCREEN.System.Drawing
  * Crear un objeto Bitmap basado en un archivo BMP.
  LOCAL loOriginalBmp AS xfcBitmap
  loOriginalBmp = .Bitmap.New(GETPICT())
  LOCAL loMonoChrBmp as xfcBitmap
  loMonoChrBmp = loOriginalBmp.GetMonochrome()
  * Guardar el Bmp monocromático que ha sido creado
  loMonoChrBmp.Save("c:\Monocromatico.bmp", .Imaging.ImageFormat.Bmp)
ENDWITH
RETURN
Y este es el resultado para dos imágenes:





La primera imagen tiene buen aspecto, a que si?

Pero el logo VFPX no aparece en color monocromático. Esto ocurre porque el fondo naranja del texto es oscuro y GDI lo convierte en negro.

Pero utilizando un truco podemos arreglarlo, al convertir la imagen en escala de grises antes de convertirlo en monocromático. No está dentro del alcance de este corto artículo explicar cómo hacer esto, porque ya me dediqué a esto profundamente en dos artículos publicados en UTMAG Special effects on images with new GDIPlus-X classes - Part 1 y Special effects on images with new GDIPlus-X classes - Part 2.

Para convertir a escala de grises, podemos utilizar una matriz de color que va a convertir cada píxel en un promedio entre sus componentes rojo, azul y verde. Para escala de grises normalmente multiplicamos cada componente de color con 0.33 (1/3). Para obtener una imagen más brillante tenemos que multiplicar cada componente por un factor más alto.

Por tanto, he aquí los pasos para convertir a color monocromático:
  1. Crear la imagen originar con GDI+
  2. Crear un Bitmap nuevo temporal del mismo tamaño que el original
  3. Crear un objeto ImageAttributes
  4. Aplicar una matriz de color con escala de grises a la imagen Attributes
  5. Dibujar la imagen original utilizando ImageAttributes
  6. Guardar la imagen
Para este ejemplo he creado un lazo utilizando un factor con rango .10 a 1, para obtener imágenes diferentes.
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx")))
WITH _SCREEN.System.Drawing
  LOCAL lcImgFile
  lcImgFile = GETPICT()
  * Crea un objeto Bitmap basado en un archivo BMP.
  LOCAL loOriginalBmp AS xfcBitmap
  loOriginalBmp = .Bitmap.FromFile(lcImgFile)
  LOCAL loTempBmp as xfcBitmap
  loTempBmp = .Bitmap.New(loOriginalBmp.Width, loOriginalBmp.Height)
  LOCAL loGfx as xfcGraphics
  loGfx = .Graphics.FromImage(loTempBmp)
  LOCAL loAttr as xfcImageAttributes
  LOCAL loGreyScaleMatrix as xfcColorMatrix
  LOCAL loMonoChrBmp as xfcBitmap
  LOCAL loRectBounds as xfcRectangle
  loRectBounds = loOriginalBmp.GetBounds()
  FOR lnFactor = .10 TO 1 STEP .10
    loGreyScaleMatrix = _Screen.System.Drawing.Imaging.ColorMatrix.New( ;
      lnFactor, lnFactor, lnFactor, 0.0, 0.0, ;
      lnFactor, lnFactor, lnFactor, 0.0, 0.0, ;
      lnFactor, lnFactor, lnFactor, 0.0, 0.0, ;
      0.0 , 0.0 , 0.0 , 1.0, 0.0, ;
      0.0 , 0.0 , 0.0 , 0.0, 1.0)
    loAttr = NULL
    loAttr = .Imaging.ImageAttributes.New()
    loAttr.SetColorMatrix(loGreyScaleMatrix)
    * Dibujar una imagen con las transformaciones en la matriz de colores
    loGfx.DrawImage(loOriginalBmp, loRectBounds, loRectBounds, 2, loAttr)
    loMonoChrBmp = loTempBmp.GetMonochrome()
    * Guarda el Bmp monocromático que ha sido creado
    loMonoChrBmp.Save("c:\" + JUSTSTEM(lcImgFile) + ;
      TRANSFORM(lnFactor * 100) + ".bmp", .Imaging.ImageFormat.Bmp)
  ENDFOR
ENDWITH
RETURN
Y esta vez podemos obtener mejores resultados, acorde con el factor deseado.

Imagen Original
Factor = 10 Factor = 20 Factor = 30 Factor = 40 Factor = 50
Factor = 60 Factor = 70 Factor = 80 Factor = 90 Factor = 100


Imagen Original
Factor = 10 Factor = 20 Factor = 30 Factor = 40 Factor = 50
Factor = 60 Factor = 70 Factor = 80 Factor = 90 Factor = 100

Enlaces relacionados:

Artículos en UTMAG