Artículo original: Convert BMP to ICON - Part 2
http://weblogs.foxite.com/vfpimaging/archive/2007/08/14/4528.aspx
Autor: Cesar Ch.
Traducido por: Luis María Guayán
Aquí está un código que convierte un Bitmap GDI u objeto imagen a un archivo de ícono .ICO, conservando la misma calidad que la imagen original.
En un envio anterior (Nota del traductor: Traducido en este Blog como Convertir BMP a ICONO - Parte 1), mostré el modo más fácil de convertir un Bitmap a ícono, usando la función API OleCreatePictureIndirect. Con muy poco código podríamos lograr archivos .ICO, pero lamentablemente, los resultados eran solo imágenes de 4bpp (bit por pixel), con 16 colores. Sospecho que Windows no proporciona apoyo a más de 16 colores. En una pequeña búsqueda en la web, encontré el siguiente tip de Cetin Basoz en FoxyClasses tip, que da una posible razón de los 16 colores de los iconos: "Aquí está el truco al hacer sus propios iconos: Las imágenes utilizadas deben ser solo de 16 colores. Si utiliza 256 colores, el ícono del logotipo de Fox será mostrado en lugar del suyo! También debe incluir el tamaño correcto de imágenes en el icono". Esto parece ser un antiguo consejo, ya que en mis pruebas en formularios de VFP trabajan muy bien con iconos de más de 16 colores.
Afortunadamente, VFP nos permite utilizar colores hasta 32bpp, de modo que podemos convertir cualquier imagen a un icono. La limitación es que VFP soporta sólo imágenes de iconos de 16x16 o 32x32 píxeles.
Agradecimiento especial a Sergey Karimov
Mi punto de partida fue un código de Sergey Karimov, que publicó en UT el año pasado. Su código original trata con BMPs físicos, convirtiéndolos a archivos .ICO. Pero mi verdadera necesidad era hacer éste sin acceso de disco adicional.
La fuente principal de la información sobre .ICO que encontré en la web fue un gran artículo de John Hornick, en MSDN, "Iconos en Win32". Para crear mis iconos, todo lo que tuve que debía hacer es seguir con cuidado todas las instrucciones proporcionadas allí, obviamente con una gran ayuda de Anatolyi Mogylevetz, en algunos de sus grandes artículos: "Retrieving information about the specified icon" y "Converting image file to .ICO file".
Abajo está mi función BMP2ICO, que crea archivos físicos .ICO de la alta calidad. En la primera parte del código de abajo encontrará el llamado al programa que cambia el tamaño a la imagen a 32x32 pixeles. El código llamado es muy fácil para entender, y puede adaptarlo según sus necesidades. Pero la parte principal es mucho más complicada, porque este tiene que crear muchas estructuras en C siguiendo las instrucciones de "Iconos en Win32".
IMPORTANTE
Requiere VFP9 y GdiPlusX para funcionar.
¡Por favor asegúrese que tiene la última versión!
http://www.codeplex.com/VFPX/Wiki/View.aspx?title=GDIPlusX&referringTitle=Home
LOCAL lcPict, lcIconFile lcPict = GETPICT("bmp") IF EMPTY(lcPict) RETURN ENDIF lcIconFile = "c:\_" + JUSTSTEM(JUSTFNAME(lcPict)) + ".ico" * Make sure we have initialized the GDIPlusX library * http://www.codeplex.com/VFPX/Wiki/View.aspx?title=GDIPlusX IF VARTYPE(_SCREEN.System) <> "O" ADDPROPERTY(_SCREEN,"System",NEWOBJECT("xfcSystem",LOCFILE("system.vcx"))) ENDIF LOCAL loBmp as xfcBitmap loBmp = _SCREEN.System.Drawing.Bitmap.FromFile(lcPict) LOCAL loResized as xfcBitmap loResized = loBmp.GetThumbnailImage(32,32) =BMP2ICO(loResized, lcIconFile, .T.) RETURN * Function: BMP2ICO * Parameters: toBmp (xfcImage), tcFileName * Related Articles: * Icons in Win32 - by John Hornick http://msdn2.microsoft.com/en-us/library/ms997538.aspx * Retrieving information about the specified icon http://www.news2news.com/vfp/?example=206&function=331 * Converting image file to .ICO file http://www.news2news.com/vfp/?example=503&function=331 * Special Thanks: * Sergey Karimov, Ontario, Canada * Anatolyi Mogylevetz FUNCTION BMP2ICO(toBmp AS xfcBitmap, tcFileName AS Character, tlChangePixFormat AS Boolean) * Make sure we have initialized the GDIPlusX library * http://www.codeplex.com/VFPX/Wiki/View.aspx?title=GDIPlusX IF VARTYPE(_SCREEN.System) <> "O" ADDPROPERTY(_SCREEN,"System",NEWOBJECT("xfcSystem",LOCFILE("system.vcx"))) ENDIF LOCAL hIcon, lcBuffer, lnWidth, lnHeight, lnBitsPerPixel LOCAL lhColorBitmap, lhMaskBitmap LOCAL loBmp as xfcBitmap lnWidth = toBmp.Width lnHeight = toBmp.Height IF tlChangePixFormat * Convert the original bitmap to ensure better quality and compatibility loBmp = _SCREEN.System.Drawing.Bitmap.New(toBmp, lnWidth, lnHeight) && This is to transform the Bitmap to 32bppARGB lnBitsPerPixel = 32 && 32bpp ARGB ELSE loBmp = toBmp lnBitsPerPixel = loResized.GetPixelFormatSize(loResized.PixelFormat) ENDIF * Obtain hIcon from Bitmap hIcon = loBmp.GetHicon() * API Declarations needed DECLARE SHORT DestroyIcon IN user32 INTEGER hIcon DECLARE INTEGER GetIconInfo IN user32 INTEGER hIcon, STRING @piconinfo DECLARE INTEGER GetDIBits IN gdi32; INTEGER hdc, INTEGER hbmp, INTEGER uStartScan,; INTEGER cScanLines, INTEGER lpvBits, STRING @lpbi, INTEGER uUsage DECLARE INTEGER CreateCompatibleDC IN gdi32 INTEGER hdc DECLARE INTEGER DeleteDC IN gdi32 INTEGER hdc DECLARE INTEGER ReleaseDC IN user32 INTEGER hwnd, INTEGER hdc DECLARE INTEGER GetWindowDC IN user32 INTEGER hwnd DECLARE RtlZeroMemory IN kernel32 As ZeroMemory INTEGER dest, INTEGER numBytes DECLARE INTEGER SelectObject IN gdi32 INTEGER hdc, INTEGER hObject DECLARE INTEGER DeleteObject IN gdi32 INTEGER hObject DECLARE INTEGER GlobalFree IN kernel32 INTEGER hMem DECLARE INTEGER GlobalAlloc IN kernel32 INTEGER wFlags, INTEGER dwBytes * ICONINFO structure *| typedef struct _ICONINFO { *| BOOL fIcon; 0:4 *| DWORD xHotspot; 4:4 *| DWORD yHotspot; 8:4 *| HBITMAP hbmMask; 12:4 *| HBITMAP hbmColor; 16:4 *| } ICONINFO; total bytes = 20 #DEFINE ICONINFO_SIZE 20 lcBuffer = REPLICATE(CHR(0), ICONINFO_SIZE) = GetIconInfo(hIcon, @lcBuffer) lhColorBitmap = CTOBIN(SUBSTR(lcBuffer,17,4),"4rs") lhMaskBitmap = CTOBIN(SUBSTR(lcBuffer,13,4),"4rs") = DestroyIcon(hIcon) * DIB BITMAPINFOHEADER. * Only the following members are used: biSize, biWidth, biHeight, biPlanes, biBitCount, biSizeImage *!* typedef struct tagBITMAPINFOHEADER{ *!* DWORD biSize; *!* LONG biWidth; *!* LONG biHeight; *!* WORD biPlanes; *!* WORD biBitCount; *!* DWORD biCompression; *!* DWORD biSizeImage; *!* LONG biXPelsPerMeter; *!* LONG biYPelsPerMeter; *!* DWORD biClrUsed; *!* DWORD biClrImportant; *!* } BITMAPINFOHEADER, *PBITMAPINFOHEADER #DEFINE DIB_RGB_COLORS 0 #DEFINE RGBQUAD_SIZE 4 #DEFINE BHDR_SIZE 40 #DEFINE GMEM_FIXED 0 #DEFINE BI_RGB 0 LOCAL lcBIHdr, lcBInfo, lcRgbQuad, lnRgbQuadSize, lpBitsArray, lnBitsSize1, lnBitsSize2 LOCAL lnBytesPerScan * Obtain the XOR Bitmap m.lnBytesPerScan = INT((m.lnWidth * m.lnBitsPerPixel)/8) IF MOD(m.lnBytesPerScan, 4) # 0 m.lnBytesPerScan = m.lnBytesPerScan + 4 - MOD(m.lnBytesPerScan, 4) ENDIF m.lnBitsSize1 = m.lnHeight * m.lnBytesPerScan m.lcBIHdr = BINTOC(BHDR_SIZE ,"4RS") + ; && biSize BINTOC(m.lnWidth,"4RS") + ; && biWidth BINTOC(m.lnHeight, "4RS") + ; && biHeight BINTOC(1, "2RS") + ; && biPlanes BINTOC(lnBitsPerPixel, "2RS") + ; && biBitCount BINTOC(BI_RGB, "4RS") + ; && biCompression BINTOC(lnBitsSize1, "4RS") + ; && biSizeImage REPLICATE(CHR(0), 16) IF m.lnBitsPerPixel <= 8 m.lnRgbQuadSize = (2^m.lnBitsPerPixel) * RGBQUAD_SIZE m.lcRgbQuad = REPLICATE(CHR(0), m.lnRgbQuadSize) ELSE m.lcRgbQuad = "" ENDIF m.lcBInfo = m.lcBIHdr + m.lcRgbQuad m.lpBitsArray = GlobalAlloc (GMEM_FIXED, m.lnBitsSize1) = ZeroMemory (m.lpBitsArray, m.lnBitsSize1) LOCAL lhdc, lhMemDC m.lhDC = GetWindowDC(_screen.HWnd) m.lhMemDC = CreateCompatibleDC(m.lhDC) = ReleaseDC (_screen.HWnd, m.lhDC) = SelectObject(lhMemDC, lhColorBitmap) = GetDIBits (m.lhMemDC, m.lhColorBitmap, 0, m.lnHeight, m.lpBitsArray , @lcBInfo, DIB_RGB_COLORS) LOCAL lqColorBinary, lqMaskBinary m.lqColorBinary = SYS(2600, m.lpBitsArray, m.lnBitsSize1) = DeleteObject (m.lhColorBitmap) = GlobalFree(m.lpBitsArray) * Obtain the AND Mask * The icAND member contains the bits for the monochrome AND mask. * The number of bytes in this array is determined by examining the icHeader member, and assuming 1bpp. * The dimensions of this bitmap must be the same as the dimensions of the XOR mask. * The AND mask is applied to the destination using the AND operation, to preserve or remove destination pixels before applying the XOR mask. LOCAL lpBitsArray2, lcBInfo2 m.lnBitsSize2 = m.lnHeight * INT((m.lnWidth * 1)/8) && 1BPP m.lpBitsArray2 = GlobalAlloc (GMEM_FIXED, m.lnBitsSize2) = ZeroMemory (m.lpBitsArray2, m.lnBitsSize2) = SelectObject(lhMemDC, lhMaskBitmap) m.lcBInfo2 = BINTOC(BHDR_SIZE ,"4RS") + ; && biSize BINTOC(m.lnWidth,"4RS") + ; && biWidth BINTOC(m.lnHeight, "4RS") + ; && biHeight BINTOC(1, "2RS") + ; && biPlanes BINTOC(1, "2RS") + ; && CHR(INT(1/256))) + && biBitCount BINTOC(BI_RGB, "4RS") + ; && biCompression BINTOC(lnBitsSize2, "4RS") + ; && biSizeImage REPLICATE(CHR(0), 16) = GetDIBits (m.lhMemDC, m.lhMaskBitmap, 0, m.lnHeight, m.lpBitsArray2 , @lcBInfo2, DIB_RGB_COLORS) m.lqMaskBinary = SYS(2600, m.lpBitsArray2, m.lnBitsSize2) = DeleteObject (m.lhMaskBitmap) = GlobalFree(m.lpBitsArray2) = DeleteDC (m.lhMemDC) LOCAL lcIconDir, lnOffset *!* typedef struct *!* { *!* WORD idReserved; // Reserved (must be 0) *!* WORD idType; // Resource Type (1 for icons) *!* WORD idCount; // How many images? *!* ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) *!* } ICONDIR, *LPICONDIR lcIconDir = BINTOC(0, "2RS") + ; && 0 reserved BINTOC(1, "2RS") + ; && 2 type BINTOC(1, "2RS") && 4 Number of Icons in this file lnOffset = LEN(lcIconDir)+16 *!* typedef struct *!* { *!* BYTE bWidth; // Width, in pixels, of the image *!* BYTE bHeight; // Height, in pixels, of the image *!* BYTE bColorCount; // Number of colors in image (0 if >=8bpp) *!* BYTE bReserved; // Reserved ( must be 0) *!* WORD wPlanes; // Color Planes *!* WORD wBitCount; // Bits per pixel *!* DWORD dwBytesInRes; // How many bytes in this resource? *!* DWORD dwImageOffset; // Where in the file is this image? *!* } ICONDIRENTRY, *LPICONDIRENTRY LOCAL lnColors, lcIconDirEntry lnColors = IIF(lnBitsPerPixel > 8, 0, 4*2^lnBitsPerPixel) lcIconDirEntry = CHR(lnWidth) + ; && 0 width of the image, in pixels CHR(lnHeight) + ; && 1 height of image, in pixels (OR & AND bmps) CHR(lnColors) + ; && 2 Number of colors in image (0 if >=8bpp) CHR(0) + ; && 3 reserved BINTOC(1,"2RS") + ; && 4 Number of Planes BINTOC(lnBitsPerPixel,"2RS") && 6 Bits per pixel lcBInfo = STUFF(lcBInfo,9,1,CHR(lnHeight*2)) && height of img, in pixels (OR & AND bmps) && The biHeight member of the icHeader structure represents the combined height of the XOR and AND masks lcBInfo = STUFF(lcBInfo,21,4, BINTOC((lnBitsSize1 + lnBitsSize2), "4RS")) && Size of Image (OR & AND bitmaps) *!* typdef struct *!* { *!* BITMAPINFOHEADER icHeader; // DIB header *!* RGBQUAD icColors[1]; // Color table *!* BYTE icXOR[1]; // DIB bits for XOR mask *!* BYTE icAND[1]; // DIB bits for AND mask *!* } ICONIMAGE, *LPICONIMAGE lqBinary = lcBInfo + lqColorBinary + lqMaskBinary lcIconDirEntry = lcIconDirEntry + BINTOC(LEN(lqBinary),"4RS") + ; && 8 Size of img area BINTOC(lnOffset,"4RS") && 12 offset to image area * Finally, save the icon to the disk * This still needs some error checking ! LOCAL lhFile lhFile= FCREATE(tcFileName) IF lhFile<1 =MESSAGEBOX("Cannot create file " + tcFileName + " !") RETURN .F. ENDIF =FWRITE(lhFile, (lcIconDir + lcIconDirEntry)) =FWRITE(lhFile, lqBinary) =FCLOSE(lhFile) RETURN
No hay comentarios. :
Publicar un comentario
Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.