6 de octubre de 2006

Dibujar o indexar formatos de píxel a imágenes con GDI+

Artículo original: DRAWING ON GIFS OR INDEXED PIXEL FORMAT IMAGES WITH GDI+
http://weblogs.foxite.com/vfpimaging/2006/03/18/drawing-on-gifs-or-indexed-pixel-format-images-with-gdi-/
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York


Es muy común que necesitemos dibujar algunas formas, textos o aplicar algunos efectos a las imágenes.

Pero GDI+ tiene limitaciones al trabajar con imágenes creadas en formatos de índices pixelados, tales como 1 bppIndexed (0x00030101), 4bppIndexed (0x00030402) and 8bppIndexed (0x00030803). Estos son formatos de pixel utilizados también por GIF (Graphics Interchange Format). Si intenta dibujar en este tipo de imágenes puede recibir un error de este tipo:



Una solución muy sencilla es cargar el GIF utilizando GpImage, y obtener algunas propiedades, tales como Width y Height. Luego, crea un objeto imagen nuevo y vacío con el mismo tamaño que el GIF original; pero utilizando formato indizado pixel. _gdiplus.vcx utiliza PIXELFORMAT_32bppPARGB como predeterminado.

El siguiente paso es "dibujar" el GIF original en el bitmap creado en un formato pixel no indizado utilizando el procedimiento DrawImage. Ahora puede dibujar libremente sobre esta nueva imagen.
Finalmente, puede guardar la imagen nueva como GIF o cualquier otro tipo.

Un detalle importante que debe conocer es que cuando le pide a GDI+ que lo almacene como GIF, lo convertirá automáticamente la imagen nuevamente 8 a un formato 8bppIndexed, que es el predeterminado para GDI+.

He aquí un ejemplo de código que realiza esto, dibujando dos elipses y un rectángulo sobre una imagen.
#DEFINE GDIPLUS_PIXELFORMAT_1bppIndexed 0x00030101
#DEFINE GDIPLUS_PIXELFORMAT_4bppIndexed 0x00030402
#DEFINE GDIPLUS_PIXELFORMAT_8bppIndexed 0x00030803
#DEFINE GDIPLUS_PIXELFORMAT_16bppGrayScale 0x00101004
#DEFINE GDIPLUS_PIXELFORMAT_16bppRGB555 0x00021005
#DEFINE GDIPLUS_PIXELFORMAT_16bppRGB565 0x00021006
#DEFINE GDIPLUS_PIXELFORMAT_16bppARGB1555 0x00061007
#DEFINE GDIPLUS_PIXELFORMAT_24bppRGB 0x00021808
#DEFINE GDIPLUS_PIXELFORMAT_32bppRGB 0x00022009
#DEFINE GDIPLUS_PIXELFORMAT_32bppARGB 0x0026200A
#DEFINE GDIPLUS_PIXELFORMAT_32bppPARGB 0x000E200B
#DEFINE GDIPLUS_PIXELFORMAT_48bppRGB 0x0010300C
LOCAL lcSource, lcDest
lcSource = GETPICT("gif")
IF EMPTY(lcSource)
  RETURN
ENDIF
lcDest = ADDBS(JUSTPATH(lcSource))+ "_" + JUSTSTEM(lcSource)

*** Carga la imagen y verifica si está indizada :
LOCAL loImage AS GPIMAGE OF ffc/_gdiplus.vcx
loImage = NEWOBJECT('gpImage',HOME() + 'ffc/_gdiplus.vcx')
loImage.CreateFromFile(lcSource)
wImg = loImage.Imagewidth
hImg = loImage.ImageHeight
lcPixFormat = GetPixFormatName(loImage.PixelFormat)
IF NOT "INDEXED" $ UPPER(lcPixFormat)
  MESSAGEBOX("Dibuje directamente sobre la imagen, ya que no tiene formato " + ;
    "de pixel indizado !",64, "UTILICE LA TÉCNICA HABITUAL")
  RETURN
ENDIF

*** Crea un nuevo bitmap con las mismas dimensiones :
LOCAL loBitmap AS GpBitmap OF ffc/_gdiplus.vcx
loBitmap = NEWOBJECT("GpBitmap", HOME() + "ffc/_gdiplus.vcx")
LOCAL loGraph AS GpGraphics OF HOME() + ffc/_gdiplus.vcx
loGraph = NEWOBJECT('GpGraphics', HOME() + "ffc/_gdiplus.vcx")
loBitmap.CREATE(wImg, hImg, GDIPLUS_PIXELFORMAT_16bppRGB555 )

*** Pega la imagen original sobre la nueva imagen recién creada :
loGraph.CreateFromImage(loBitmap)
loGraph.DrawImageScaled(loImage, 0, 0, wImg, hImg)

*** Ahora podemos dibujar lo que deseemos sobre la imagen :
LOCAL loBlue, loRed, loGreen AS GpColor OF ffc/_gdiplus.vcx
LOCAL loPen AS GpPen OF HOME() + ffc/_gdiplus.vcx
loBlue = NEWOBJECT("GpColor", HOME() + "ffc/_gdiplus.vcx","",40,40,240 )
loRed = NEWOBJECT("GpColor", HOME() + "ffc/_gdiplus.vcx","",240,40,40 )
loGreen = NEWOBJECT("GpColor", HOME() + "ffc/_gdiplus.vcx","",40,230,40 )
loPen = NEWOBJECT("GpPen", HOME() + "ffc/_gdiplus.vcx")
loPen.CREATE(loBlue, 12)
loGraph.DrawEllipse( loPen, 0, 0, wImg, hImg)
loPen.PenColor = loRed
loGraph.DrawEllipse( loPen, 0+12, 0+12 , wImg-24 , hImg-24)
loPen.PenColor = loGreen
loGraph.DrawRectangle( loPen, 0+30, 0+30 , wImg-60 , hImg-60)

*** Guarda la imagen en GIF y JPG :
loBitmap.SaveToFile(lcDest+".jpg","image/jpeg","quality=100")
loBitmap.SaveToFile(lcDest+".gif","image/gif")
RETURN
PROCEDURE GetPixFormatName(nPix)
  DO CASE
    CASE nPix = 0x00030101
      RETURN "1bppIndexed"
    CASE nPix = 0x00030402
      RETURN "4bppIndexed"
    CASE nPix = 0x00030803
      RETURN "8bppIndexed"
    CASE nPix = 0x00101004
      RETURN "16bppGrayScale"
    CASE nPix = 0x00021005
      RETURN "16bppRGB555"
    CASE nPix = 0x00021006
      RETURN "16bppRGB565"
    CASE nPix = 0x00061007
      RETURN "16bppARGB1555"
    CASE nPix = 0x00021808
      RETURN "24bppRGB"
    CASE nPix = 0x00022009
      RETURN "32bppRGB"
    CASE nPix = 0x0026200A
      RETURN "32bppARGB"
    CASE nPix = 0x000E200B
      RETURN "32bppPARGB"
    CASE nPix = 0x0010300C
      RETURN "48bppRGB"
    CASE nPix = 0x001C400E
      RETURN "64bppPARGB"
    OTHERWISE
      RETURN "No identificado"
  ENDCASE
ENDPROC
Este procedimiento tiene un gran inconveniente, ya que en esta conversión automática, GDI+ escribe el archivo utilizando una paleta de medio tono por lo que el objeto imagen tendrá el color reducido. GDI+ hace la conversión a color desde 32 bits-por-pixel (32 BPP) al escribir la imagen al archivo.

De acuerdo con Szaak Priester: "Cuando GDI+ guarda un bitmap en formato GIF, realiza una forma cruda de determinación de los valores (cuantización) de los colores. Siempre emplea la misma paleta de colores, se rellena fundamentalmente con los 216 "colores Web-safe" Al inicio de Internet, estos colores eran los únicos mostrados consistentemente por la mayoría de los examinadores, de ahí su nombre... los 216 colores web-safe fueron escogidos básicamente por sus méritos técnicos (dividen uniformemente el espacio d color RGB) y no debido a sus cualidades visuales. Como consecuencia, la paleta web-safe (llamada también "paleta de medio tono") contiene fundamentalmente púrpuras casi perceptibles, muchos verdosos y marrones turbios, por tato, mucha parte utilizable del espectrum es seriamente poco poplado."

Debajo, puede ver el resultado que se obtiene utilizando la técnica que se ha comentado aquí.

GIF - Imagen original GIF - 8bppIndexed Image JPEG - 32bppARGB

Preste atención a la diferencia entre la calidad de estas tres imágenes.

Esta técnica puede ser utilizada también para redimensional cualquier imagen, incluyendo GIFs. Con el objeto de redimensionar, todo lo que necesitamos es cambiar en el código anterior las instrucciones que tratan del tamaño de imagen, loBitmap.Create(NewWidth, NewHeight, PixelFormat) y al pegar la imagen original en la creada nueva, loGraph.DrawImagePortionat(loOriginalImage, 0, 0, NewWidth, newHeight).

Necesitas solamente conocer las limitaciones de GIFs y decidir si guardará la imagen como GIF o como otro formato de imagen.

He aquí algunas diferencias entre formatos GIFs y JPEGs, de acuerdo con MSDN:
GIF vs. JPEG

¿Debe guardar las imágenes en formato GIF o JPEG? Aunque esta pregunta no está relacionada directamente con la administración de la paleta, confunde a mucha gente, y es de alguna manera relevante para nuestro tema.

Existen dos diferencias fundamentales entre imágenes GIF y JPEG:
Las imágenes GIF están comprimidas de forma que conservan todos sus datos, mientras las imágenes JPEG están comprimidas de forma que pierden muchos de sus datos.
Las imágenes GIF están limitadas como máximo a 256 colores, mientras las imágenes JPEG no están limitadas por la cantidad de colores que utilizan.
Pero ... ¿existe una forma de convertir una imagen en un formato GIF sin emplear una paleta de colores mejor distribuida en lugar de una paleta de medio tono que estropea las imágenes? Por supuesto; pero esto ya es tema para otro artículo ...

No hay comentarios. :

Publicar un comentario