WinApi/TBI(더 바인딩 오브 아이작) 모작

[Win32 API TBI 모작] 3. Manager(2)

Vfly 2025. 5. 27. 01:30

이번 포스트에서는 그래픽 관련 매니저를 알아보자.


Direct2D 매니저 ( Direct2DMgr )

#pragma once

struct ScrollArea {
	D2D1_RECT_F viewRect;     // 화면에 표시되는 영역
	D2D1_RECT_F contentRect;  // 전체 콘텐츠 영역
	D2D1_POINT_2F scrollPos;  // 현재 스크롤 위치
};

class Direct2DMgr
{
	SINGLE(Direct2DMgr)

private:
	ID2D1Factory* pD2DFactory;
	IWICImagingFactory* pWICFactory;
	IDWriteFactory* g_pDWriteFactory;
	ID2D1HwndRenderTarget* pRenderTarget;
	ID2D1BitmapRenderTarget* pBitmapRenderTarget; // 백 버퍼용 멤버 변수 추가


	// 여러 비트맵을 저장하기 위한 맵
	unordered_map<wstring, ID2D1Bitmap*> bitmapMap;
	// 쪼개진 비트맵 저장용 맵
	unordered_map<wstring, vector<ID2D1Bitmap*>> splitBitmapMap;

public:
	ID2D1HwndRenderTarget* GetRenderTarget() { return pRenderTarget; }

public:
	HRESULT init(HWND hwnd);									//초기화함수

public:
	HRESULT StoreCreateMap(ID2D1Bitmap* bitmap, const wstring& tag);
	HRESULT LoadAndStoreBitmap(const wstring& filePath, const wstring& tag, bool split);
	ID2D1Bitmap* GetStoredBitmap(const wstring& tag);
	vector<ID2D1Bitmap*> GetSplitBitmaps(const wstring& tag);
	void DeleteBitmap(const wstring& tag);

public:
	HRESULT StoreBitmapsFromFolder(const std::wstring& folderPath, const std::wstring& tag);

public:
	HRESULT SplitBitmap(ID2D1Bitmap* bitmap, const wstring& tag,
		const D2D1_POINT_2F& leftTop, const D2D1_POINT_2F& rightBottom);

	ID2D1Bitmap* SplitBitmapNoSave(ID2D1Bitmap* bitmap, const D2D1_POINT_2F& leftTop, const D2D1_POINT_2F& rightBottom);


public:
	void Cleanup();

public:
	void RenderTextWithOutline(
		const std::wstring& text,
		const D2D1_RECT_F& layoutRect,
		float fontSize,
		const D2D1_COLOR_F& textColor,
		const D2D1_COLOR_F& outlineColor,
		float outlineThickness,
		int horizontal);

private:
	HRESULT LoadBitmap(const wstring& filePath, ID2D1Bitmap** ppBitmap);
	HRESULT SplitBitmap(ID2D1Bitmap* bitmap, const wstring& tag);

};


#include "global.h"
#include "Direct2DMgr.h"
#include "CPathMgr.h"
#include "CFontMgr.h"

Direct2DMgr::Direct2DMgr()
{

}

Direct2DMgr::~Direct2DMgr()
{
    Cleanup();
}

HRESULT Direct2DMgr::init(HWND hwnd)
{
    HRESULT hr;

    // COM 라이브러리 초기화
    hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
    if (FAILED(hr)) return hr;

    // Direct2D 팩토리 생성
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
    if (FAILED(hr)) return hr;


    // 렌더 타겟 생성
    RECT rc;
    GetClientRect(hwnd, &rc);

    D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);

    hr = pD2DFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(
            
        ),
        D2D1::HwndRenderTargetProperties(hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),
        &pRenderTarget
    );
    if (FAILED(hr)) return hr;

    // WIC 팩토리 생성
    hr = CoCreateInstance(
        CLSID_WICImagingFactory,   // CLSID for WIC Imaging Factory
        nullptr,                   // No aggregation
        CLSCTX_INPROC_SERVER,      // Run in the same process
        IID_PPV_ARGS(&pWICFactory) // Output pointer to IWICImagingFactory
    );
    if (FAILED(hr)) {
        MessageBox(nullptr, L"WIC 팩토리 생성 실패!", L"오류", MB_OK);
        return hr;
    }

    hr = pRenderTarget->CreateCompatibleRenderTarget(&pBitmapRenderTarget);
    if (FAILED(hr)) return hr;


    return S_OK;

}

HRESULT Direct2DMgr::LoadAndStoreBitmap(const std::wstring& filePath, const std::wstring& tag, bool split) {
    // 이미 로드된 비트맵이 있는지 확인
    if (bitmapMap.find(tag) != bitmapMap.end()) {
        return S_OK; // 이미 로드됨
    }

    ID2D1Bitmap* newBitmap = nullptr;

    const wstring strFinalPath = CPathMgr::GetInstance()->GetContentPath() + filePath;

    HRESULT hr = LoadBitmap(strFinalPath, &newBitmap);

    if (SUCCEEDED(hr)) {
        bitmapMap[tag] = newBitmap; // 맵에 저장

        // split이 true인 경우 비트맵을 쪼갬
        if (split) {
            hr = SplitBitmap(newBitmap, tag);
            if (FAILED(hr)) {
                MessageBox(nullptr, L"비트맵 분할 실패!", L"오류", MB_OK);
                return hr;
            }
        }
    }

    return hr;
}

ID2D1Bitmap* Direct2DMgr::GetStoredBitmap(const std::wstring& tag) {
    auto it = bitmapMap.find(tag);

    if (it != bitmapMap.end()) {
        return it->second; // 저장된 비트맵bitmapMap 반환
    }

    return nullptr; // 없음
}

vector<ID2D1Bitmap*> Direct2DMgr::GetSplitBitmaps(const wstring& tag) {
    auto it = splitBitmapMap.find(tag);

    if (it != splitBitmapMap.end()) {
        return it->second; // 저장된 비트맵 반환
    }

    return {}; // 없음
}

void Direct2DMgr::DeleteBitmap(const wstring& tag)
{
    bitmapMap.erase(tag);
}


HRESULT Direct2DMgr::LoadBitmap(const std::wstring& filePath, ID2D1Bitmap** ppBitmap) {

    // 파일 존재 여부 확인
    if (!pWICFactory) {
        MessageBox(nullptr, L"WIC 팩토리가 초기화되지 않았습니다!", L"오류", MB_OK);
        return E_FAIL;
    }
    IWICBitmapDecoder* pDecoder = nullptr;
    IWICBitmapFrameDecode* pSource = nullptr;
    IWICFormatConverter* pConverter = nullptr;

    HRESULT hr = pWICFactory->CreateDecoderFromFilename(
        filePath.c_str(),            // 파일 경로
        nullptr,                     // 특정 공급업체 GUID (NULL 사용)
        GENERIC_READ,                // 읽기 전용 액세스
        WICDecodeMetadataCacheOnLoad,// 메타데이터 캐시 옵션
        &pDecoder                    // 디코더 출력
    );

    if (FAILED(hr)) {
        MessageBox(nullptr, L"이미지 디코더 생성 실패!", L"오류", MB_OK);
        return hr;
    }

    // 첫 번째 프레임 가져오기
    hr = pDecoder->GetFrame(0, &pSource);
    if (FAILED(hr)) {
        MessageBox(nullptr, L"이미지 프레임 가져오기 실패!", L"오류", MB_OK);
        pDecoder->Release();
        return hr;
    }

    // 포맷 변환기 생성
    hr = pWICFactory->CreateFormatConverter(&pConverter);
    if (FAILED(hr)) {
        MessageBox(nullptr, L"포맷 변환기 생성 실패!", L"오류", MB_OK);
        pSource->Release();
        pDecoder->Release();
        return hr;
    }

    // 포맷 변환 초기화
    hr = pConverter->Initialize(
        pSource,
        GUID_WICPixelFormat32bppPBGRA, // Direct2D에서 사용하는 포맷
        WICBitmapDitherTypeNone,
        nullptr,
        0.f,
        WICBitmapPaletteTypeMedianCut
    );

    if (FAILED(hr)) {
        MessageBox(nullptr, L"이미지 포맷 변환 실패!", L"오류", MB_OK);
        pConverter->Release();
        pSource->Release();
        pDecoder->Release();
        return hr;
    }

    // Direct2D 비트맵 생성
    hr = pRenderTarget->CreateBitmapFromWicBitmap(pConverter, nullptr, ppBitmap);

    if (FAILED(hr)) {
        MessageBox(nullptr, L"Direct2D 비트맵 생성 실패!", L"오류", MB_OK);
    }

    // 메모리 해제
    if (pConverter) pConverter->Release();
    if (pSource) pSource->Release();
    if (pDecoder) pDecoder->Release();

    return hr;
}

HRESULT Direct2DMgr::SplitBitmap(ID2D1Bitmap* bitmap, const wstring& tag) {
    if (!bitmap || !pRenderTarget) return E_FAIL;

    D2D1_SIZE_F bitmapSize = bitmap->GetSize();
    UINT rows = static_cast<UINT>(bitmapSize.height / ((float)TILE_SIZE / 2.f));
    UINT cols = static_cast<UINT>(bitmapSize.width / ((float)TILE_SIZE / 2.f));

    vector<ID2D1Bitmap*> splitBitmaps;

    for (UINT row = 0; row < rows; ++row) {
        for (UINT col = 0; col < cols; ++col) {
            // 각 64x64 영역 정의
            D2D1_RECT_F srcRect = D2D1::RectF(
                col * ((float)TILE_SIZE / 2.f),
                row * ((float)TILE_SIZE / 2.f),
                (col + 1) * ((float)TILE_SIZE / 2.f),
                (row + 1) * ((float)TILE_SIZE / 2.f)
            );

            // 호환 렌더 타겟 생성
            ID2D1BitmapRenderTarget* compatibleRenderTarget = nullptr;
            HRESULT hr = pRenderTarget->CreateCompatibleRenderTarget(
                D2D1::SizeF(((float)TILE_SIZE / 2.f), ((float)TILE_SIZE / 2.f)),
                &compatibleRenderTarget
            );
            if (FAILED(hr)) {
                MessageBox(nullptr, L"호환 렌더 타겟 생성 실패!", L"오류", MB_OK);
                return hr;
            }

            // 호환 렌더 타겟에 원본 비트맵 그리기
            compatibleRenderTarget->BeginDraw();
            compatibleRenderTarget->DrawBitmap(bitmap, D2D1::RectF(0, 0, TILE_SIZE / 2, TILE_SIZE / 2), 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, srcRect);
            hr = compatibleRenderTarget->EndDraw();
            if (FAILED(hr)) {
                MessageBox(nullptr, L"비트맵 그리기 실패!", L"오류", MB_OK);
                compatibleRenderTarget->Release();
                return hr;
            }

            // 호환 렌더 타겟에서 비트맵 가져오기
            ID2D1Bitmap* subBitmap = nullptr;
            hr = compatibleRenderTarget->GetBitmap(&subBitmap);
            compatibleRenderTarget->Release(); // 렌더 타겟 해제
            if (FAILED(hr)) {
                MessageBox(nullptr, L"비트맵 가져오기 실패!", L"오류", MB_OK);
                return hr;
            }

            splitBitmaps.push_back(subBitmap);
        }
    }

    splitBitmapMap[tag] = splitBitmaps;
    return S_OK;
}

HRESULT Direct2DMgr::SplitBitmap(ID2D1Bitmap* bitmap, const wstring& tag,
    const D2D1_POINT_2F& leftTop, const D2D1_POINT_2F& rightBottom)
{
    if (!bitmap || !pRenderTarget) return E_FAIL;

    // 입력 값 유효성 검사
    D2D1_SIZE_F bitmapSize = bitmap->GetSize();
    if (leftTop.x >= rightBottom.x || leftTop.y >= rightBottom.y ||
        rightBottom.x > bitmapSize.width || rightBottom.y > bitmapSize.height)
    {
        MessageBox(nullptr, L"잘못된 영역 좌표 입력!", L"오류", MB_OK);
        return E_INVALIDARG;
    }

    // 추출 영역 계산
    const float width = rightBottom.x - leftTop.x;
    const float height = rightBottom.y - leftTop.y;

   
    // 호환 렌더 타겟 생성
    ID2D1BitmapRenderTarget* compatibleRenderTarget = nullptr;
    HRESULT hr = pRenderTarget->CreateCompatibleRenderTarget(
        D2D1::SizeF(width, height),
        &compatibleRenderTarget
    );

    if (FAILED(hr)) {
        MessageBox(nullptr, L"호환 렌더 타겟 생성 실패!", L"오류", MB_OK);
        return hr;
    }

    // 원본에서 특정 영역 추출
    compatibleRenderTarget->BeginDraw();
    compatibleRenderTarget->DrawBitmap(
        bitmap,
        D2D1::RectF(0, 0, width, height),  // 대상 영역(전체 채움)
        1.0f,
        D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
        D2D1::RectF(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y)  // 원본 영역
    );

    hr = compatibleRenderTarget->EndDraw();
    if (FAILED(hr)) {
        MessageBox(nullptr, L"비트맵 추출 실패!", L"오류", MB_OK);
        compatibleRenderTarget->Release();
        return hr;
    }

    // 결과 비트맵 저장
    ID2D1Bitmap* subBitmap = nullptr;
    hr = compatibleRenderTarget->GetBitmap(&subBitmap);
    compatibleRenderTarget->Release();

    if (FAILED(hr)) {
        MessageBox(nullptr, L"비트맵 가져오기 실패!", L"오류", MB_OK);
        return hr;
    }

    
    bitmapMap[tag] = subBitmap;
    return S_OK;
}

ID2D1Bitmap* Direct2DMgr::SplitBitmapNoSave(ID2D1Bitmap* bitmap, const D2D1_POINT_2F& leftTop, const D2D1_POINT_2F& rightBottom)
{
    if (!bitmap || !pRenderTarget) return nullptr;

    // 입력 값 유효성 검사
    D2D1_SIZE_F bitmapSize = bitmap->GetSize();
    if (leftTop.x >= rightBottom.x || leftTop.y >= rightBottom.y ||
        rightBottom.x > bitmapSize.width || rightBottom.y > bitmapSize.height)
    {
        MessageBox(nullptr, L"잘못된 영역 좌표 입력!", L"오류", MB_OK);
        return nullptr;
    }

    // 추출 영역 계산
    const float width = rightBottom.x - leftTop.x;
    const float height = rightBottom.y - leftTop.y;


    // 호환 렌더 타겟 생성
    ID2D1BitmapRenderTarget* compatibleRenderTarget = nullptr;
    HRESULT hr = pRenderTarget->CreateCompatibleRenderTarget(
        D2D1::SizeF(width, height),
        &compatibleRenderTarget
    );

    if (FAILED(hr)) {
        MessageBox(nullptr, L"호환 렌더 타겟 생성 실패!", L"오류", MB_OK);
        return nullptr;
    }

    // 원본에서 특정 영역 추출
    compatibleRenderTarget->BeginDraw();
    compatibleRenderTarget->DrawBitmap(
        bitmap,
        D2D1::RectF(0, 0, width, height),  // 대상 영역(전체 채움)
        1.0f,
        D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
        D2D1::RectF(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y)  // 원본 영역
    );

    hr = compatibleRenderTarget->EndDraw();
    if (FAILED(hr)) {
        MessageBox(nullptr, L"비트맵 추출 실패!", L"오류", MB_OK);
        compatibleRenderTarget->Release();
        return nullptr;
    }

    // 결과 비트맵 저장
    ID2D1Bitmap* subBitmap = nullptr;
    hr = compatibleRenderTarget->GetBitmap(&subBitmap);
    compatibleRenderTarget->Release();

    
    if (FAILED(hr)) {
        MessageBox(nullptr, L"비트맵 가져오기 실패!", L"오류", MB_OK);
        return nullptr;
    }

    return subBitmap;
}

HRESULT Direct2DMgr::StoreCreateMap(ID2D1Bitmap* bitmap, const wstring& tag)
{
    // 이미 로드된 비트맵이 있다면 그 맵 삭제.
    if (bitmapMap.find(tag) != bitmapMap.end()) {
        //return S_OK; // 이미 로드됨
        bitmapMap[tag]->Release();
    }

    bitmapMap[tag] = bitmap; // 맵에 저장


    return S_OK;
}

void Direct2DMgr::Cleanup() {
    for (auto& pair : bitmapMap) {
        if (pair.second) pair.second->Release();
    }

    bitmapMap.clear();

    if (pRenderTarget) {
        pRenderTarget->Release();
        pRenderTarget = nullptr;
    }

    if (pWICFactory) {
        pWICFactory->Release();
        pWICFactory = nullptr;
    }

    if (pD2DFactory) {
        pD2DFactory->Release();
        pD2DFactory = nullptr;
    }

    CoUninitialize();
}

HRESULT Direct2DMgr::StoreBitmapsFromFolder(const wstring& folderPath, const wstring& tag)
{
    wstring header = CPathMgr::GetInstance()->GetContentPath();
    vector<ID2D1Bitmap*> splitBitmaps;
    for (int i = 0; i < 64; i++)
    {
        wstring finalPath = header + folderPath + L"mapTile_" + std::to_wstring(i) + L".png";
        ID2D1Bitmap* newBitmap = nullptr;

        LoadBitmap(finalPath, &newBitmap);
        splitBitmaps.push_back(newBitmap);
    }

    splitBitmapMap[tag] = splitBitmaps;
    return S_OK;
}

void Direct2DMgr::RenderTextWithOutline(
    const std::wstring& text,
    const D2D1_RECT_F& layoutRect,
    float fontSize,
    const D2D1_COLOR_F& textColor,
    const D2D1_COLOR_F& outlineColor,
    float outlineThickness,
    int horizontal)
{
    if (!pRenderTarget) return;

    IDWriteTextFormat* pTextFormat = CFontMgr::GetInstance()->GetTextFormat(L"Ink Free", fontSize);

    // 텍스트 정렬 설정
    if (horizontal == 0)
        pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); // 가로 가운데 정렬
    else if(horizontal == 1)
        pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); // 가로 왼쪽 정렬
    else if(horizontal == 2)
        pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING); // 가로 우측 정렬

    pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); // 세로 가운데 정렬

    // 외곽선 브러시 생성
    ID2D1SolidColorBrush* pOutlineBrush = nullptr;
    HRESULT hr = pRenderTarget->CreateSolidColorBrush(outlineColor, &pOutlineBrush);
    if (FAILED(hr)) {
        pTextFormat->Release();
        return;
    }

    // 텍스트 브러시 생성
    ID2D1SolidColorBrush* pTextBrush = nullptr;
    hr = pRenderTarget->CreateSolidColorBrush(textColor, &pTextBrush);
    if (FAILED(hr)) {
        pOutlineBrush->Release();
        pTextFormat->Release();
        return;
    }

    // 외곽선 렌더링 (텍스트를 약간씩 이동시키며 그리기)
    for (float dx = -outlineThickness; dx <= outlineThickness; dx += outlineThickness) {
        for (float dy = -outlineThickness; dy <= outlineThickness; dy += outlineThickness) {
            if (dx == 0 && dy == 0) continue; // 중심점은 건너뜀

            D2D1_RECT_F outlineRect = layoutRect;
            outlineRect.left += dx;
            outlineRect.top += dy;
            outlineRect.right += dx;
            outlineRect.bottom += dy;

            pRenderTarget->DrawText(
                text.c_str(),
                static_cast<UINT32>(text.length()),
                pTextFormat,
                &outlineRect,
                pOutlineBrush
            );
        }
    }

    // 본문 텍스트 렌더링
    pRenderTarget->DrawText(
        text.c_str(),
        static_cast<UINT32>(text.length()),
        pTextFormat,
        &layoutRect,
        pTextBrush
    );

    //리소스 해제
    pOutlineBrush->Release();
    pTextBrush->Release();
    pTextFormat->Release();
}

 

개요

Windows 환경에서 Direct2D API를 활용한 2D 그래픽 렌더링 시스템을 구축한 프로젝트다. 게임 개발에 필요한 이미지 로딩, 비트맵 관리, 텍스트 렌더링 등의 핵심 기능을 효율적으로 관리하는 매니저 클래스를 설계하고 구현했다.

 

주요 기술 스택

  • 언어: C++
  • 그래픽 API: Direct2D, WIC (Windows Imaging Component)
  • 텍스트 렌더링: DirectWrite
  • 메모리 관리: COM (Component Object Model)
  • 플랫폼: Windows

 

핵심 기능 및 구현 내용

1. 초기화 및 리소스 관리

HRESULT Direct2DMgr::init(HWND hwnd)
  • COM 라이브러리 초기화: 멀티스레드 환경 대응
  • Direct2D 팩토리 생성: 싱글 스레드 모드로 최적화
  • 렌더 타겟 설정: HWND 기반 하드웨어 가속 렌더링
  • WIC 팩토리 초기화: 다양한 이미지 포맷 지원

기술적 포인트:

  • 에러 처리를 통한 안정성 확보
  • 하드웨어 가속을 활용한 성능 최적화

 

2. 이미지 로딩 및 비트맵 관리 시스템

효율적인 비트맵 캐싱 시스템

std::map<std::wstring, ID2D1Bitmap*> bitmapMap;
std::map<std::wstring, std::vector<ID2D1Bitmap*>> splitBitmapMap;

구현한 주요 기능:

  • 태그 기반 비트맵 관리: 문자열 키를 통한 직관적인 리소스 접근
  • 중복 로딩 방지: 이미 로드된 리소스 재사용으로 메모리 효율성 증대
  • 자동 메모리 관리: 소멸자에서 안전한 리소스 해제

다양한 이미지 포맷 지원

HRESULT Direct2DMgr::LoadBitmap(const std::wstring& filePath, ID2D1Bitmap** ppBitmap)
  • WIC 디코더 활용: PNG, JPEG, BMP 등 다양한 포맷 지원
  • 포맷 변환 최적화: Direct2D 호환 포맷(32bppPBGRA)으로 자동 변환
  • 에러 핸들링: 파일 로딩 실패 시 적절한 오류 메시지 제공

 

3. 고급 비트맵 분할 기능

사용자 정의 영역 추출

HRESULT Direct2DMgr::SplitBitmap(ID2D1Bitmap* bitmap, const wstring& tag,
    const D2D1_POINT_2F& leftTop, const D2D1_POINT_2F& rightBottom)

기술적 특징:

  • 픽셀 단위 정밀한 영역 지정
  • 호환 렌더 타겟을 활용한 부분 이미지 추출
  • 스프라이트 애니메이션 프레임 분리에 활용

 

4. 고품질 텍스트 렌더링 시스템

void Direct2DMgr::RenderTextWithOutline(
    const std::wstring& text,
    const D2D1_RECT_F& layoutRect,
    float fontSize,
    const D2D1_COLOR_F& textColor,
    const D2D1_COLOR_F& outlineColor,
    float outlineThickness,
    int horizontal)

고급 텍스트 렌더링 기능:

  • 외곽선 효과: 다중 패스 렌더링으로 텍스트 가독성 향상
  • 정렬 옵션: 좌측/가운데/우측 정렬 지원
  • DirectWrite 통합: 폰트 매니저와 연동한 타이포그래피 시스템
  • 게임 UI 최적화: 인게임 텍스트, 점수 표시 등에 적합한 시각적 품질

 

설계 패턴 및 아키텍처

싱글톤 패턴 활용

  • 전역적인 그래픽 리소스 관리
  • 메모리 효율성과 일관성 보장

팩토리 패턴 적용

  • Direct2D, WIC 팩토리를 통한 객체 생성 추상화
  • 플랫폼별 구현 세부사항 캡슐화

 

성능 최적화 기법

1. 메모리 최적화

  • 비트맵 중복 로딩 방지로 메모리 사용량 최소화
  • COM 객체의 적절한 참조 카운팅 관리

2. 렌더링 최적화

  • 하드웨어 가속 활용 (D2D1_PRESENT_OPTIONS_IMMEDIATELY)
  • 호환 렌더 타겟을 통한 효율적인 오프스크린 렌더링

3. 캐싱 전략

  • 태그 기반 비트맵 캐싱으로 런타임 성능 향상
  • 분할된 비트맵 재사용으로 CPU 부하 감소

 

프로젝트에서의 활용 사례

게임 개발에서의 적용

  • 2D 게임 엔진: 스프라이트 렌더링, 애니메이션 시스템
  • 타일 기반 맵 시스템: 레벨 에디터, 맵 렌더링
  • UI 시스템: 메뉴, HUD, 인게임 텍스트 렌더링

실제 구현 결과

  • 60fps 이상의 안정적인 렌더링 성능 달성
  • 수백 개의 스프라이트 동시 렌더링 지원
  • 메모리 사용량 최적화로 장시간 실행 안정성 확보

 

(일부 함수가 이전 프로젝트에서 사용된 함수들이 몇개 있는데.... 지웠다가 계속 오류나서 그냥 냅둔상태.....)