本文則介紹將GDI+位圖轉換為PCX格式圖像。 下面是GDI+位圖轉換為PCX格式圖像代碼: [cpp] INT PackPcxLine(LPBYTE dest, LPBYTE source, INT bytesPreLine, INT planes) { LPBYTE pd = dest; INT delta = planes --; LPBYTE ps = source + planes; INT bytes = bytesPreLine; while(planes >= 0) { INT count = 0; BYTE c = *ps; do { count ++; if (-- bytes == 0) { if (-- planes < 0) break; bytes = bytesPreLine; ps = source + planes; } else ps += delta; } while(c == *ps && count < 0x3f); if (c >= 0xc0 || count > 1) *pd ++ = count | 0xc0; *pd ++ = c; } return pd - dest; } //--------------------------------------------------------------------------- typedef union { WORD value; struct { BYTE low; BYTE high; }; }testMask; INT PackPcx4Line(LPBYTE dest, LPBYTE source, INT bytesPreLine, INT width) { INT bytes = bytesPreLine << 2; LPBYTE buf = dest + bytes; testMask mask; mask.value = 0x1001; width = (width + 1) >> 1; while(mask.high) { BYTE c = 0; BYTE bit = 0x80; LPBYTE pb = buf; for (INT i = 0; i < width; i ++) { if (source[i] & mask.high) c |= bit; bit >>= 1; if (source[i] & mask.low) c |= bit; bit >>= 1; if (bit == 0) { *pb ++ = c; c = 0; bit = 0x80; } } buf += bytesPreLine; mask.value <<= 1; } return PackPcxLine(dest, dest + bytes, bytes, 1); } //--------------------------------------------------------------------------- VOID ARGBQuadToRGBTriple(PRGBTriple dest, PRGBQuad source, INT count) { for (INT i = 0; i < count; i++) { dest[i].rgbtBlue = source[i].rgbRed; dest[i].rgbtGreen = source[i].rgbGreen; dest[i].rgbtRed = source[i].rgbBlue; } } //--------------------------------------------------------------------------- BOOL SavePcxImageToStream(IStream *stream, Bitmap *bmp) { PixelFormat format = bmp->GetPixelFormat(); if (format == PixelFormatUndefined) return FALSE; PcxFileHeader header; ColorPalette *pal = NULL; BYTE palette[256 * 3 + 1]; ZeroMemory(&header, sizeof(PcxFileHeader)); header.bitsPrePixel = GetPixelFormatSize(format); header.planes = 1; if (header.bitsPrePixel > 8) { format = PixelFormat24bppRGB; header.planes = 3; header.bitsPrePixel = 8; } else //if (header.bitsPrePixel > 1) { pal = (ColorPalette*)new BYTE[256 * sizeof(ARGB) + sizeof(ColorPalette)]; bmp->GetPalette(pal, bmp->GetPaletteSize()); PRGBTriple ppal = (PRGBTriple)&palette[1]; // 如果是16色位圖,調色板保存到文件頭 if (format == PixelFormat4bppIndexed) { header.planes = header.bitsPrePixel; header.bitsPrePixel = 1; ppal = (PRGBTriple)header.palette; } ARGBQuadToRGBTriple(ppal, (PRGBQuad)pal->Entries, pal->Count); delete[] pal; } Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight()); header.flag = 0x0A; header.version = 5; header.encodeing = 1; header.xMax = r.Width - 1; header.yMax = r.Height - 1; header.hRes = 96; header.vRes = 96; header.paletteType = 1; header.bytesPreLine = (r.Width * header.bitsPrePixel + 7) >> 3; if (header.bytesPreLine & 1) header.bytesPreLine ++; // 保存PCX文件頭 if (stream->Write(&header, sizeof(PcxFileHeader), NULL) != S_OK) return FALSE; // 獲取GDI+位圖數據到位圖數據結構 BitmapData data; data.Stride = ((r.Width * GetPixelFormatSize(format) + 31) & -32) >> 3; INT size = r.Height * data.Stride; // size為位圖數據字節數,header.bytesPreLine*header.planes*2為編碼緩沖區字節數 data.Scan0 = (LPVOID)new BYTE[size + header.bytesPreLine * header.planes * 2]; bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf, format, &data); bmp->UnlockBits(&data); // 如果單色位圖調色板首項不為0,位圖數據反向 if (format == PixelFormat1bppIndexed && (*(ARGB*)&palette[1] & 0xffffff)) { INT count = data.Height * (data.Stride >> 2); LPDWORD pd = (LPDWORD)data.Scan0; for (INT i = 0; i < count; pd[i] ^= (DWORD)(-1), i ++); } LPBYTE p = (LPBYTE)data.Scan0; LPBYTE buffer = p + size; INT bytes; // 逐行進行RLE編碼並保存到流 for (UINT y = 0; y < data.Height; y ++, p += data.Stride) { if (format == PixelFormat4bppIndexed) bytes = PackPcx4Line(buffer, p, header.bytesPreLine, data.Width); else bytes = PackPcxLine(buffer, p, header.bytesPreLine, header.planes); stream->Write(buffer, bytes, NULL); } delete[] data.Scan0; // 如果是256色位圖,調色板保存到流的尾部 if (format == PixelFormat8bppIndexed) { palette[0] = 0x0c; stream->Write(palette, 256 * 3 + 1, NULL); } return TRUE; } //--------------------------------------------------------------------------- BOOL SavePcxImageToFile(LPTSTR fileName, Bitmap *bmp) { IStream *stream = new FileStream(fileName, FALSE); stream->AddRef(); BOOL result = SavePcxImageToStream(stream, bmp); stream->Release(); return result; } //--------------------------------------------------------------------------- INT PackPcxLine(LPBYTE dest, LPBYTE source, INT bytesPreLine, INT planes) { LPBYTE pd = dest; INT delta = planes --; LPBYTE ps = source + planes; INT bytes = bytesPreLine; while(planes >= 0) { INT count = 0; BYTE c = *ps; do { count ++; if (-- bytes == 0) { if (-- planes < 0) break; bytes = bytesPreLine; ps = source + planes; } else ps += delta; } while(c == *ps && count < 0x3f); if (c >= 0xc0 || count > 1) *pd ++ = count | 0xc0; *pd ++ = c; } return pd - dest; } //--------------------------------------------------------------------------- typedef union { WORD value; struct { BYTE low; BYTE high; }; }testMask; INT PackPcx4Line(LPBYTE dest, LPBYTE source, INT bytesPreLine, INT width) { INT bytes = bytesPreLine << 2; LPBYTE buf = dest + bytes; testMask mask; mask.value = 0x1001; width = (width + 1) >> 1; while(mask.high) { BYTE c = 0; BYTE bit = 0x80; LPBYTE pb = buf; for (INT i = 0; i < width; i ++) { if (source[i] & mask.high) c |= bit; bit >>= 1; if (source[i] & mask.low) c |= bit; bit >>= 1; if (bit == 0) { *pb ++ = c; c = 0; bit = 0x80; } } buf += bytesPreLine; mask.value <<= 1; } return PackPcxLine(dest, dest + bytes, bytes, 1); } //--------------------------------------------------------------------------- VOID ARGBQuadToRGBTriple(PRGBTriple dest, PRGBQuad source, INT count) { for (INT i = 0; i < count; i++) { dest[i].rgbtBlue = source[i].rgbRed; dest[i].rgbtGreen = source[i].rgbGreen; dest[i].rgbtRed = source[i].rgbBlue; } } //--------------------------------------------------------------------------- BOOL SavePcxImageToStream(IStream *stream, Bitmap *bmp) { PixelFormat format = bmp->GetPixelFormat(); if (format == PixelFormatUndefined) return FALSE; PcxFileHeader header; ColorPalette *pal = NULL; BYTE palette[256 * 3 + 1]; ZeroMemory(&header, sizeof(PcxFileHeader)); header.bitsPrePixel = GetPixelFormatSize(format); header.planes = 1; if (header.bitsPrePixel > 8) { format = PixelFormat24bppRGB; header.planes = 3; header.bitsPrePixel = 8; } else //if (header.bitsPrePixel > 1) { pal = (ColorPalette*)new BYTE[256 * sizeof(ARGB) + sizeof(ColorPalette)]; bmp->GetPalette(pal, bmp->GetPaletteSize()); PRGBTriple ppal = (PRGBTriple)&palette[1]; // 如果是16色位圖,調色板保存到文件頭 if (format == PixelFormat4bppIndexed) { header.planes = header.bitsPrePixel; header.bitsPrePixel = 1; ppal = (PRGBTriple)header.palette; } ARGBQuadToRGBTriple(ppal, (PRGBQuad)pal->Entries, pal->Count); delete[] pal; } Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight()); header.flag = 0x0A; header.version = 5; header.encodeing = 1; header.xMax = r.Width - 1; header.yMax = r.Height - 1; header.hRes = 96; header.vRes = 96; header.paletteType = 1; header.bytesPreLine = (r.Width * header.bitsPrePixel + 7) >> 3; if (header.bytesPreLine & 1) header.bytesPreLine ++; // 保存PCX文件頭 if (stream->Write(&header, sizeof(PcxFileHeader), NULL) != S_OK) return FALSE; // 獲取GDI+位圖數據到位圖數據結構 BitmapData data; data.Stride = ((r.Width * GetPixelFormatSize(format) + 31) & -32) >> 3; INT size = r.Height * data.Stride; // size為位圖數據字節數,header.bytesPreLine*header.planes*2為編碼緩沖區字節數 data.Scan0 = (LPVOID)new BYTE[size + header.bytesPreLine * header.planes * 2]; bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf, format, &data); bmp->UnlockBits(&data); // 如果單色位圖調色板首項不為0,位圖數據反向 if (format == PixelFormat1bppIndexed && (*(ARGB*)&palette[1] & 0xffffff)) { INT count = data.Height * (data.Stride >> 2); LPDWORD pd = (LPDWORD)data.Scan0; for (INT i = 0; i < count; pd[i] ^= (DWORD)(-1), i ++); } LPBYTE p = (LPBYTE)data.Scan0; LPBYTE buffer = p + size; INT bytes; // 逐行進行RLE編碼並保存到流 for (UINT y = 0; y < data.Height; y ++, p += data.Stride) { if (format == PixelFormat4bppIndexed) bytes = PackPcx4Line(buffer, p, header.bytesPreLine, data.Width); else bytes = PackPcxLine(buffer, p, header.bytesPreLine, header.planes); stream->Write(buffer, bytes, NULL); } delete[] data.Scan0; // 如果是256色位圖,調色板保存到流的尾部 if (format == PixelFormat8bppIndexed) { palette[0] = 0x0c; stream->Write(palette, 256 * 3 + 1, NULL); } return TRUE; } //--------------------------------------------------------------------------- BOOL SavePcxImageToFile(LPTSTR fileName, Bitmap *bmp) { IStream *stream = new FileStream(fileName, FALSE); stream->AddRef(); BOOL result = SavePcxImageToStream(stream, bmp); stream->Release(); return result; } //--------------------------------------------------------------------------- 代碼中SavePcxImageToStream函數已經將大致的轉換流程作了注釋,本文不再羅嗦,而SavePcxImageToFile函數仍然是利用我寫的簡易文件流將轉換後的PCX格式圖像保存到文件, 下面是個GDI+位圖轉換為PCX格式圖像例子(BCB2010): [cpp] void __fastcall TForm1::Button3Click(TObject *Sender) { Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(L"d:\\1-4.bmp"); if (bmp->GetLastStatus() != Ok) throw new Exception("Load Image fail"); Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle); g->DrawImage(bmp, 0, 0); delete g; SavePcxImageToFile("d:\\1-4.pcx", bmp); delete bmp; }