📄 gifwin.cpp
字号:
/*********************************************************** File: GifWin.cpp* Title: Graphics Interchange Format implementation** Author: Lennie Araki* Date: 24-Nov-1999** This class is a thin wrapper around the open source* giflib-1.4.0 for opening, parsing and displaying* Compuserve GIF files on Windows.** The baseline code was derived from fragments extracted* from the sample programs gif2rgb.c and giftext.c.* Added support for local/global palettes, transparency* and "dispose" methods to improve display compliance* with GIF89a.** Copyright (c) 1999 CallWave, Inc.* CallWave, Inc.* 136 W. Canon Perdido Suite A* Santa Barbara, CA 93101** Licensed under the terms laid out in the libungif * COPYING file.**********************************************************/#include "stdafx.h"#include <windowsx.h>#include "GifWin.h"extern "C"{ #include "gif_lib.h"}#define LOCAL static//// Implements the GIF89a specification with the following omissions://// Section 18. Logical Screen Descriptor:// Pixel Aspect Ratio is ignored - square pixels assumed (1:1)// Section 23. Graphic Control Extension:// User Input Flag is ignored - could be added but not very useful// Section 25. Plain Text Extension// Not implemented - would require embedding fonts and text drawing// code to be added// Section 26. Application Extension// Not implemented. Note: this includes Netscape 2.0 looping// extensions//// _______________________________// | reserved | disposal |u_i| t |// |___|___|___|___|___|___|___|___|//#define GIF_TRANSPARENT 0x01#define GIF_USER_INPUT 0x02#define GIF_DISPOSE_MASK 0x07#define GIF_DISPOSE_SHIFT 2#define GIF_NOT_TRANSPARENT -1#define GIF_DISPOSE_NONE 0 // No disposal specified. The decoder is // not required to take any action.#define GIF_DISPOSE_LEAVE 1 // Do not dispose. The graphic is to be left // in place.#define GIF_DISPOSE_BACKGND 2 // Restore to background color. The area used by the // graphic must be restored to the background color.#define GIF_DISPOSE_RESTORE 3 // Restore to previous. The decoder is required to // restore the area overwritten by the graphic with // what was there prior to rendering the graphic.// Initialize BITMAPINFO LOCAL void InitBitmapInfo(LPBITMAPINFO pBMI, int cx, int cy){ ::ZeroMemory(pBMI, sizeof(BITMAPINFOHEADER)); pBMI->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pBMI->bmiHeader.biWidth = cx; pBMI->bmiHeader.biHeight = -cy; // negative for top-down bitmap pBMI->bmiHeader.biPlanes = 1; pBMI->bmiHeader.biBitCount = 24; pBMI->bmiHeader.biClrUsed = 256;}// Copy GIF ColorMap into Windows BITMAPINFOLOCAL void CopyColorMap(ColorMapObject* pColorMap, LPBITMAPINFO pBMI){ int iLen = pColorMap->ColorCount; ASSERT( iLen <= 256 ); int iCount = min(iLen, 256); for (int i = 0; i < iCount; i++) { BYTE red = pColorMap->Colors[i].Red; BYTE green = pColorMap->Colors[i].Green; BYTE blue = pColorMap->Colors[i].Blue; TRACE("%3d: %02xh %02xh %02xh ", i, red, green, blue); pBMI->bmiColors[i].rgbRed = red; pBMI->bmiColors[i].rgbGreen = green; pBMI->bmiColors[i].rgbBlue = blue; pBMI->bmiColors[i].rgbReserved = 0; if (i % 4 == 3) TRACE("\n"); } TRACE("\n");}#define DWORD_PAD(x) (((x) + 3) & ~3)// Copy bytes from source to destination skipping transparent bytesLOCAL void CopyGIF(LPBYTE pDst, LPBYTE pSrc, int width, const int transparent, GifColorType* pColorTable){ ASSERT( pColorTable ); if (width) { do { BYTE b = *pSrc++; if (b != transparent) { // Translate to 24-bit RGB value if not transparent const GifColorType* pColor = pColorTable + b; pDst[0] = pColor->Blue; pDst[1] = pColor->Green; pDst[2] = pColor->Red; } // Skip to next pixel pDst += 3; } while (--width); }}// Fix pixels in 24-bit GIF bufferLOCAL void FillGIF(LPBYTE pDst, const COLORREF rgb, int width){ if (width) { do { pDst[0] = GetBValue(rgb); pDst[1] = GetGValue(rgb); pDst[2] = GetRValue(rgb); pDst += 3; } while (--width); }}// Constructor/destructorCGIFWin::CGIFWin(){ m_pGifFile = NULL; m_pBits = NULL; // Clear bitmap information ::ZeroMemory(&m_bmiGlobal, sizeof(m_bmiGlobal)); ::ZeroMemory(&m_bmiDisplay, sizeof(m_bmiDisplay)); // // Per Section 11 of GIF spec: // If no color table is available at all, the decoder is free to use a // system color table or a table of its own. In that case, the decoder // may use a color table with as many colors as its hardware is able // to support; it is recommended that such a table have black and // white as its first two entries, so that monochrome images can be // rendered adequately. // const RGBQUAD rgbWhite = { 255, 255, 255, 0 }; const RGBQUAD rgbBlack = { 0, 0, 0, 0 }; m_bmiGlobal.bmi.bmiColors[0] = rgbBlack; for (int i = 1; i < 256; ++i) { m_bmiGlobal.bmi.bmiColors[i] = rgbWhite; }}CGIFWin::~CGIFWin(){ TRACE("*** CGIFWin destructor called ***\n"); Close();}// Open GIF file and allocate "screen" bufferint CGIFWin::Open(LPCTSTR pszGifFileName, COLORREF rgbTransparent){ m_rgbBackgnd = m_rgbTransparent = rgbTransparent; // First close and delete previous GIF (if open) Close(); m_pGifFile = ::DGifOpenFileName(pszGifFileName); int iResult = -1; if (m_pGifFile) { const int cxScreen = m_pGifFile->SWidth; const int cyScreen = m_pGifFile->SHeight; TRACE("\n%s:\n\n\tScreen Size - Width = %d, Height = %d.\n", pszGifFileName, cxScreen, cyScreen); TRACE("\tColorResolution = %d, BackGround = %d.\n", m_pGifFile->SColorResolution, m_pGifFile->SBackGroundColor); // Allocate buffer big enough for 2 screens + 1 line // Use 24-bit (3-bytes per pixel) to correctly handle local palettes const DWORD dwRowBytes = DWORD_PAD(cxScreen * 3); const DWORD dwScreen = dwRowBytes * cyScreen; m_pBits = (LPBYTE) GlobalAllocPtr(GHND, dwScreen * 2 + dwRowBytes); iResult = -2; if (m_pBits) { // Fill in current and next image with background color for (int y = 0; y < cyScreen * 2; ++y) { ::FillGIF(m_pBits + y * dwRowBytes, rgbTransparent, cxScreen); } ::InitBitmapInfo(&m_bmiGlobal.bmi, cxScreen, cyScreen); if (m_pGifFile->SColorMap) { TRACE("\tGlobal Color Map:\n"); ::CopyColorMap(m_pGifFile->SColorMap, &m_bmiGlobal.bmi); GifColorType* pColor = m_pGifFile->SColorMap->Colors + m_pGifFile->SBackGroundColor; m_rgbBackgnd = RGB(pColor->Red, pColor->Green, pColor->Blue); } iResult = 0; m_iImageNum = 0; m_uLoopCount = 0U; } } return iResult;}// Close the GIF file and free resources allocated by libgifvoid CGIFWin::Close(){ // Close GIF file if opened if (m_pGifFile) { int iError = DGifCloseFile(m_pGifFile); if (iError == GIF_ERROR) { TRACE("DGifCloseFile error=%d\n", GifLastError()); } m_pGifFile = NULL; } // Free memory if allocated if (m_pBits) { GlobalFreePtr(m_pBits); m_pBits = NULL; }}//// Draw entire GIF to a Windows Device Context// iFactor Percent Ratio// -3 25% (1:4)// -2 33% (1:3)// -1 50% (1:2)// 0 100% (1:1)// 1 200% (2:1)// 2 300% (3:1)// 3 400% (4:1)//int CGIFWin::Draw(HDC hDC, LPCRECT pRect, int iFactor /*=0*/){ int iResult = 0; if (m_pGifFile && m_pBits) { const int Width = m_pGifFile->SWidth; const int Height = m_pGifFile->SHeight; int zoomWidth = Width; int zoomHeight = Height; if (iFactor < 0) { zoomWidth /= (1 - iFactor); zoomHeight /= (1 - iFactor); } else if (iFactor > 0) { zoomWidth *= (1 + iFactor); zoomHeight *= (1 + iFactor); } int x, y; if (pRect) { // Center image in rectangle x = (pRect->right - pRect->left - zoomWidth) / 2 + pRect->left; y = (pRect->bottom - pRect->top - zoomHeight) / 2 + pRect->top; } else { // Draw image at top-left x = y = 0; } if (Width && Height) { if (iFactor < 0) { HBITMAP hBitmap = CreateMappedBitmap(NULL, 0, 1 - iFactor); if (hBitmap) { HDC hdcMem = ::CreateCompatibleDC(hDC); if (hdcMem) { HBITMAP hOldBm = (HBITMAP) ::SelectObject(hdcMem, hBitmap); // Blast bits from memory DC to target DC. iResult = ::BitBlt(hDC, x, y, zoomWidth, zoomHeight, hdcMem, 0, 0, SRCCOPY); ::SelectObject(hdcMem, hOldBm); ::DeleteDC(hdcMem); } ::DeleteObject(hBitmap); } } else // (iFactor >= 0) { // Display bitmap on screen (-negative height to flip DIB upside down) iResult = ::StretchDIBits(hDC, x, y, zoomWidth, zoomHeight, 0, 0, Width, Height, m_pBits, &m_bmiDisplay.bmi, DIB_RGB_COLORS, SRCCOPY); } } } return iResult;}// Compute least squared color differenceLOCAL COLORREF ColorDiff(COLORREF rgb1, COLORREF rgb2){ // If matching color, replace with Windows color const int rDiff = GetRValue(rgb1) - GetRValue(rgb2); const int gDiff = GetGValue(rgb1) - GetGValue(rgb2); const int bDiff = GetBValue(rgb1) - GetBValue(rgb2); // Use least squared difference const long lDiff = rDiff * rDiff + gDiff * gDiff + bDiff * bDiff; return lDiff;}LOCAL COLORREF AvePixel(LPBYTE pSrcRow, DWORD dwSrcRowBytes, LPCOLORMAP pColorMap, UINT uColors, int iScale){ const int iPower = iScale * iScale; const int iPower2 = iPower / 2; int red = iPower2; // For rounding int grn = iPower2; int blu = iPower2; for (int row = iScale; row > 0; --row) { LPBYTE pSrc = pSrcRow; for (int col = iScale; col > 0; --col) { COLORREF rgb = RGB(pSrc[2], pSrc[1], pSrc[0]); pSrc += 3;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -