Triển khai lớp hiển thị hoạt ảnh GIF trong môi trường EVC

Trong một dự án nhúng sử dụng môi trường phát triển Embedded Visual C++ (EVC), việc cung cấp phản hồi trực quan cho người dùng trong quá trình xử lý dài hạn như khởi động hệ thống, chờ xử lý ảnh lớn hoặc chuyển đổi giữa các chế độ là rất cần thiết. Một giải pháp hiệu quả là sử dụng hoạt ảnh GIF để biểu thị trạng thái đang chờ. Ban đầu, thông báo đơn giản được thực hiện qua form tùy chỉnh, nhưng do giới hạn về mặt thẩm mỹ, nhóm phát triển đã quyết định tích hợp khả năng hiển thị GIF.

Lớp CGIFShow được xây dựng dựa trên việc tham khảo và cải tiến từ mã nguồn mở có tên CGif89a, hỗ trợ đầy đủ chuẩn GIF89a bao gồm cả trong suốt và thời gian trì hoãn khung hình. Lớp này đã được kiểm thử thành công trong dự án EVC4, chạy ổn định trên nền tảng nhúng.

Cấu trúc lớp CGIFShow

Dưới đây là phiên bản đã được tối ưu hóa và tái cấu trúc phần khai báo của lớp:

#ifndef GIF_RENDERER_H
#define GIF_RENDERER_H

#include <windows.h>
#include <tchar.h>

// Các hằng số mô tả hành vi dọn dẹp khung hình
#define DISPOSAL_UNSPECIFIED    0
#define DISPOSAL_KEEP           1
#define RESTORE_TO_BACKGROUND   2
#define RESTORE_TO_PREVIOUS     3

typedef struct {
    BYTE codeBit;
    WORD prevCode;
    WORD nextCode;
} GifDecompressNode;

class CGIFRenderer {
private:
    HDC m_hDisplayDC;
    HBITMAP m_hBackBuffer;
    HBITMAP m_hPauseBuffer;
    HBITMAP m_hFrameBuffer;
    
    volatile BOOL m_bRunning;
    volatile BOOL m_bPaused;
    DWORD m_dwFrameDelay;
    DWORD m_dwDefaultSpeed;

    // Thông tin file GIF
    BYTE* m_pGifData;
    BYTE* m_pReadPtr;
    int m_nTotalBytes;
    int m_nCurrentOffset;

    // Kích thước và vị trí
    int m_nGifWidth;
    int m_nGifHeight;
    int m_nImageLeft;
    int m_nImageTop;
    int m_nImageWidth;
    int m_nImageHeight;
    int m_nRowPad;

    // Bảng màu
    BYTE* m_pGlobalPalette;
    int m_nGlobalPalSize;
    COLORREF m_crTransparent;

    // Trạng thái giải nén
    int m_nPassMode;
    int m_nCurrentRow;
    int m_nCurrentCol;
    BYTE m_cCurrentByte;
    UINT m_uBitBufferSize;
    UINT m_uCurrentBitSize;
    UINT m_uRemainingBits;
    UINT m_uBlockSize;
    UINT m_uBytesInBlock;

    // Mã điều khiển
    int m_nClearCode;
    int m_nEofCode;
    int m_nTableSize;
    int m_nInitialBitSize;

    // Hành vi hiển thị
    int m_nDisposalMethod;
    BOOL m_bHasTransparency;
    int m_nTransIndex;

    void ResetDecompressionState();
    WORD ReadLzwCode();
    void OutputPixel(BYTE pixel);
    HBITMAP DecodeFrame();
    HBITMAP CreateDIBitmapFromInfo(BYTE* pBitmapInfo);

public:
    CGIFRenderer(HDC hDisplay);
    ~CGIFRenderer();

    BOOL LoadFromFile(LPCTSTR szFilePath);
    void Play();
    void Pause(BOOL bPause);
    void Stop();
    void SetLocation(int x, int y);

private:
    static DWORD WINAPI PlaybackThread(LPVOID lpParam);
    BOOL PlaybackLoop();
};

#endif

Triển khai chức năng chính

Hàm LoadFromFile chịu trách nhiệm đọc dữ liệu GIF từ file và phân tích header cơ bản:

BOOL CGIFRenderer::LoadFromFile(LPCTSTR szFilePath) {
    HANDLE hFile = CreateFile(szFilePath, GENERIC_READ, FILE_SHARE_READ,
                              NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) return FALSE;

    DWORD dwFileSize = GetFileSize(hFile, NULL);
    if (dwFileSize == 0xFFFFFFFF) {
        CloseHandle(hFile);
        return FALSE;
    }

    BYTE header[6];
    DWORD bytesRead;
    if (!ReadFile(hFile, header, 6, &bytesRead, NULL) || 
        bytesRead != 6 || 
        memcmp(header, "GIF", 3) != 0) {
        CloseHandle(hFile);
        return FALSE;
    }

    // Giải phóng tài nguyên cũ nếu có
    Stop();
    if (m_pGifData) delete[] m_pGifData;
    if (m_pGlobalPalette) delete[] m_pGlobalPalette;
    if (m_hBackBuffer) DeleteObject(m_hBackBuffer);

    m_pGifData = new BYTE[dwFileSize];
    if (!ReadFile(hFile, m_pGifData, dwFileSize, &bytesRead, NULL)) {
        CloseHandle(hFile);
        return FALSE;
    }
    CloseHandle(hFile);

    // Đọc kích thước ảnh từ offset 6-9
    m_nGifWidth = *(WORD*)(m_pGifData + 6);
    m_nGifHeight = *(WORD*)(m_pGifData + 8);

    BYTE flags = m_pGifData[10];
    if (flags & 0x80) { // Có bảng màu toàn cục
        m_nGlobalPalSize = 1 << ((flags & 7) + 1);
        m_pGlobalPalette = new BYTE[m_nGlobalPalSize * 3];
        memcpy(m_pGlobalPalette, m_pGifData + 13, m_nGlobalPalSize * 3);
    }

    m_pReadPtr = m_pGifData + 13 + (m_pGlobalPalette ? m_nGlobalPalSize * 3 : 0);
    m_nTotalBytes = dwFileSize;
    m_nCurrentOffset = (int)(m_pReadPtr - m_pGifData);

    return TRUE;
}

Phương thức Play khởi tạo luồng riêng để phát lại hoạt ảnh, đảm bảo giao diện không bị đóng băng:

void CGIFRenderer::Play() {
    if (!m_pGifData || m_bRunning) return;
    
    m_bRunning = TRUE;
    m_bPaused = FALSE;
    m_pReadPtr = m_pGifData + m_nCurrentOffset;
    
    HANDLE hThread = CreateThread(NULL, 0, PlaybackThread, this, 0, NULL);
    if (hThread) {
        CloseHandle(hThread);
    }
}

DWORD WINAPI CGIFRenderer::PlaybackThread(LPVOID lpParam) {
    CGIFRenderer* pThis = (CGIFRenderer*)lpParam;
    pThis->PlaybackLoop();
    return 0;
}

Sử dụng trong ứng dụng

Dưới đây là ví dụ cách tích hợp lớp vào một cửa sổ Win32:

// Khai báo biến toàn cục
CGIFRenderer* g_pGifPlayer = nullptr;

// Trong xử lý WM_CREATE
case WM_CREATE: {
    HDC hdc = GetDC(hWnd);
    g_pGifPlayer = new CGIFRenderer(hdc);
    ReleaseDC(hWnd, hdc);
    break;
}

// Khi cần hiển thị hoạt ảnh
if (g_pGifPlayer->LoadFromFile(_T("\\MyApp\\waiting.gif"))) {
    g_pGifPlayer->SetLocation(100, 150);
    g_pGifPlayer->Play();
}

// Dừng khi hoàn tất tác vụ
g_pGifPlayer->Stop();

// Dọn dẹp bộ nhớ khi thoát
if (g_pGifPlayer) {
    delete g_pGifPlayer;
    g_pGifPlayer = nullptr;
}

Lớp này hỗ trợ đầy đủ các tính năng như khung trong suốt, trì hoãn theo thời gian chỉ định, và các phương thức dọn dẹp khung (disposal method). Việc sử dụng bộ đệm kép giúp giảm hiện tượng nhấp nháy, đồng thời cơ chế luồng riêng biệt đảm bảo trải nghiệm người dùng mượt mà ngay cả trên thiết bị nhúng có tài nguyên hạn chế.

Thẻ: EVC GIF animation C++ Win32 API embedded systems

Đăng vào ngày 1 tháng 6 lúc 16:20