🧼C, C++/WinAPI, UX(UI)

이미지 픽셀정보 직렬화 - C++ / GDI+

Mawile 2021. 4. 15.
728x90

 

개발환경 >> Visual Studio

언어 >> C++17이상

운영체제 >> Windows10

 

 


 

 

안녕하세요!!!!!ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

저가 왜이렇게 기분이좋으면서 슬프냐면,

오늘하루종일 이미지파일 직렬화와 역직렬화를 연구하고, 직접 모두 구현했습니다.

바로 알려드리겠습니다!!!!!

+추가로 이것만있으면 소켓통신으로도 활용하여 이미지전송과 받아오기가 자유롭게 가능합니다요!!ㅠㅠㅠ

파이썬이랑 C#으로는 옛날에 구현해봤는데, C++로는 처음구현해보는거라서 재밌더라구요 ㅋㅋbb

--소스코드 설계하고 코딩하는데 총 약7시간정도 썼네요

 

 


C++ / GDI+ / 이미지파일 (역)직렬화하기

#pragma once
#pragma comment(linker, "/SUBSYSTEM:CONSOLE") //콘솔사용 명시
#pragma comment(lib, "gdiplus.lib") //라이브러리사용

#include <windows.h> //윈도우API
#include <gdiplus.h> //GDI+
#include <conio.h> //_getch()

struct BITBYTE { //이미지정보를 담을
	INT height;
	INT width;
	INT stride;
	BYTE* bytes = nullptr;
	Gdiplus::PixelFormat format;
};

void PrintBitmap(Gdiplus::Bitmap* bmp) { //비트맵을 콘솔창에 출력하는 함수
	if (bmp == nullptr) return; //만약 비트맵이 비었다면 출력하지마~~ㅠㅠ

	HWND hwnd = GetConsoleWindow(); //콘솔핸들얻기
	HDC hdc = GetDC(hwnd); //핸들값으로 DC얻기
	HDC MemoryDC = CreateCompatibleDC(hdc); //상용가능한 DC얻기 
	HBITMAP hBit;
	bmp->GetHBITMAP(Gdiplus::Color(0, 0, 0, 0), &hBit); //hBitmap얻기
	HGDIOBJ obj = SelectObject(MemoryDC, hBit); //오브젝트 정하기
	BITMAP bm;
	GetObject(hBit, sizeof BITMAP, &bm); //비트맵의 크기구하기
	
    //출력하기
	StretchBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, MemoryDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);

	SelectObject(MemoryDC, obj); //메모리누수방지
	DeleteObject(hBit);
	DeleteDC(MemoryDC);
	ReleaseDC(hwnd, hdc);
}

Gdiplus::Bitmap* ByteToBitmap(BITBYTE bitBytes) { //바이트를 비트맵으로 전환하는 함수!
	Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(bitBytes.width, bitBytes.height, bitBytes.stride, bitBytes.format, bitBytes.bytes);
	return bmp;
}

void BitmapToByte(Gdiplus::Bitmap* bmp, BITBYTE& bitBytes) { //비트맵을 바이트로 전환하는 함수!
	if (bitBytes.bytes != nullptr) { //구조체안에 바이트가있으면 비워 ㅠㅠ
		delete[] bitBytes.bytes;
		bitBytes.bytes = nullptr;
	}

	Gdiplus::Rect rect(0, 0, bmp->GetWidth(), bmp->GetHeight()); //크기구하기
	Gdiplus::BitmapData bmpData; //비트맵데이터 객체

	if (bmp->LockBits(&rect,
		Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite,
		bmp->GetPixelFormat(), &bmpData) == Gdiplus::Ok) { //픽셀포맷형식에따라 이미지접근권한 취득

		int len = bmpData.Height * std::abs(bmpData.Stride); //이미지 전체크기
		bitBytes.bytes = new BYTE[len]; //할당
		memcpy(bitBytes.bytes, bmpData.Scan0, len); //복사
		bitBytes.height = bmpData.Height;
		bitBytes.width = bmpData.Width;
		bitBytes.stride = bmpData.Stride;
		bitBytes.format = bmp->GetPixelFormat();
		bmp->UnlockBits(&bmpData); //락 풀기
	}
}

int main(void) {
	Gdiplus::GdiplusStartupInput stin;
	ULONG_PTR tk;
	Gdiplus::GdiplusStartup(&tk, &stin, 0); //gdi시~~작!

	Gdiplus::Bitmap* bmp = Gdiplus::Bitmap::FromFile(L"1.bmp"); //파일로부터 비트맵정보 취득
	Gdiplus::Bitmap* copyBmp = nullptr; //비우고~
	BITBYTE bitBytes;

	BitmapToByte(bmp, bitBytes);
	copyBmp = ByteToBitmap(bitBytes);
	PrintBitmap(copyBmp);

	delete bmp, copyBmp;
	if (_getch()) return 1;
}

와우~~~~~~

우선은 저가 구조체를 따로만들었습니다.

BITBYTE라는 구조체인데 이미지의 width, height, stride, 이미지의 실제데이터, format이 들어가 있습니다.

여기서 실제 이미지의 바이트는 BITBYTE내에 있는 bytes입니다.

 

bmp로 얻은 이미지정보를 BitmapToByte라는 함수로 직렬화한다음,

거기서얻은 구조체정보를 통하여 ByteToBitmap이라는 함수내에서 역직렬화하고,

거기서나온 이미지정보를 출력하도록 설계되었습니다.

 

한번 테스트해보겠습니다!

와우~~~~

콘솔커서때문에 좀 거슬리기는 하지만, 정상적으로 출력이됩니다!!

그러면,,,

이것을 이용하여 소켓통신에도 적용하여 이미지통신을 구현해보겠습니다!!!!!!!!!!!!!

 

 

C++ / GDI+ / 이미지전송하기 [ 헤더 ]

#pragma once
#pragma comment(linker, "/SUBSYSTEM:CONSOLE")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "ws2_32.lib") //위에서 이미 설명한 부분은 제외하겠습니다.

#define LENGTHBUF (0x20) //패킷전송전 패킷의 크기를 전송해야하는데 그것을 전송하기위한 버퍼의 크기

#include <iostream>
#include <ws2tcpip.h> //inet_pton만 쓸거에요 ㅠㅠ
#include <winsock2.h> //winsock추가 ㅠㅠ!!
#include <windows.h>
#include <gdiplus.h>
#include <conio.h>

SOCKET StartServer() { //서버시작함수 -이 메소드에 대한 설명은 저의 블로그에서 소켓카테고리가보시면 들으실수있습니다.
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);

	SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	SOCKADDR_IN addr = { 0 };
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(8080);
	addr.sin_family = AF_INET;

	bind(s, (SOCKADDR*)&addr, sizeof addr);
	listen(s, SOMAXCONN);

	SOCKADDR_IN clientaddr = { 0 };
	int clientsize = sizeof clientaddr;
	return accept(s, (SOCKADDR*)&clientaddr, &clientsize);
}

SOCKET StartClient() { //클라이언트시작함수 -이것도 위와같습니다.
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);

	SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	SOCKADDR_IN addr = { 0 };
	addr.sin_port = htons(8080);
	addr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);

	while (connect(s, (SOCKADDR*)&addr, sizeof addr));

	return s;
}

void StartGDI() { //gdi시작~~!
	Gdiplus::GdiplusStartupInput stin;
	ULONG_PTR tk;
	Gdiplus::GdiplusStartup(&tk, &stin, 0);
}

void ClsCursor() { //커서지우기 ㅠㅠ
	CONSOLE_CURSOR_INFO curinfo;
	GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curinfo);
	curinfo.bVisible = FALSE;
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curinfo);
}

struct BITBYTE { //이미지정보 구조체 (len추가)
	INT height;
	INT width;
	INT stride;
	INT len;
	BYTE* bytes = nullptr;
	Gdiplus::PixelFormat format;
};

void PrintBitmap(Gdiplus::Bitmap* bmp) { //설명생략(위에서 설명함)
	if (bmp == nullptr) return;

	HWND hwnd = GetConsoleWindow();
	HDC hdc = GetDC(hwnd);
	HDC MemoryDC = CreateCompatibleDC(hdc);
	HBITMAP hBit;
	bmp->GetHBITMAP(Gdiplus::Color(0, 0, 0, 0), &hBit);
	HGDIOBJ obj = SelectObject(MemoryDC, hBit);
	BITMAP bm;
	GetObject(hBit, sizeof BITMAP, &bm);

	StretchBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, MemoryDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);

	SelectObject(MemoryDC, obj);
	DeleteObject(hBit);
	DeleteDC(MemoryDC);
	ReleaseDC(hwnd, hdc);
}

Gdiplus::Bitmap* ByteToBitmap(BITBYTE bitBytes) { //설명생략(위에서 설명함)
	Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(bitBytes.width, bitBytes.height, bitBytes.stride, bitBytes.format, bitBytes.bytes);
	return bmp;
}

void BitmapToByte(Gdiplus::Bitmap* bmp, BITBYTE& bitBytes) { //여기는 살짝 바꿨습니다.
	if (bitBytes.bytes != nullptr) {
		delete[] bitBytes.bytes;
		bitBytes.bytes = nullptr;
	}

	Gdiplus::Rect rect(0, 0, bmp->GetWidth(), bmp->GetHeight());
	Gdiplus::BitmapData bmpData;

	if (bmp->LockBits(&rect,
		Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite,
		bmp->GetPixelFormat(), &bmpData) == Gdiplus::Ok) {

		int len = bmpData.Height * std::abs(bmpData.Stride);
		bitBytes.bytes = new BYTE[len];
		memcpy(bitBytes.bytes, bmpData.Scan0, len);
		bitBytes.height = bmpData.Height;
		bitBytes.width = bmpData.Width;
		bitBytes.stride = bmpData.Stride;
		bitBytes.format = bmp->GetPixelFormat();
		bitBytes.len = len;
		bmp->UnlockBits(&bmpData);
	}
}

 

C++ / GDI+ / 이미지전송하기 [ 서버 ]

#include "header.h"

int main() {
	StartGDI();
	ClsCursor();

	char buf[LENGTHBUF];
	SOCKET s = StartServer();
	BITBYTE bitBytes = { 0 };
	Gdiplus::Bitmap* bmp = Gdiplus::Bitmap::FromFile(L"1.bmp"); //여기다가 클라이언트로 전송할 이미지경로를 입력하세요~~
	BitmapToByte(bmp, bitBytes);

	ZeroMemory(buf, sizeof buf); //메모리비우고
	_itoa_s(sizeof(bitBytes), buf, 10); //구조체크기 복사하고
	send(s, buf, LENGTHBUF, 0); //구조체크기 전송하고
	send(s, (const char*)&bitBytes, atoi(buf), 0); //구조체전송하기

	ZeroMemory(buf, sizeof buf); //메모리비우고
	_itoa_s(bitBytes.len, buf, 10); //이미지정보크기 복사하고
	send(s, buf, LENGTHBUF, 0); //이미지정보크기 전송하고
	send(s, (const char*)bitBytes.bytes, atoi(buf), 0); //이미지정보 전송

	_getch();
	delete bmp;
	delete[] bitBytes.bytes;
}

 

C++ / GDI+ / 이미지전송하기 [ 클라이언트 ]

#include "../ImageTransfer/header.h"

int main() {
	StartGDI();
	ClsCursor();

	char buf[LENGTHBUF];
	BITBYTE bitBytes = { 0 };
	SOCKET s = StartClient();

	recv(s, buf, LENGTHBUF, 0); //구조체크기받고
	recv(s, (char*)&bitBytes, atoi(buf), 0); //구조체받기

	recv(s, buf, LENGTHBUF, 0); //이미지정보크기 받고
	bitBytes.bytes = new BYTE[atoi(buf)]; //크기만큼 할당하고
	recv(s, (char*)bitBytes.bytes, atoi(buf), 0); //이미지정보 받고
	Gdiplus::Bitmap* bmp = nullptr; //비트맵객체 만들고
	bmp = ByteToBitmap(bitBytes); //구조체를 비트맵으로 전환하고
	PrintBitmap(bmp); //비트맵 출력
	
	_getch();
	delete bmp;
	delete[] bitBytes.bytes;
}

 

 

 

와 이번꺼는 소스가 워낙 길어서 주석으로

설명다는데 엄청 힘드네 ㅠㅠㅠㅠㅠㅠ

 

 

 


글 봐주셔서 감사합니다~~~~~~~~~!!!!!!!!!!!!!!!!!11

 

 

 

 

 

728x90

'🧼C, C++ > WinAPI, UX(UI)' 카테고리의 다른 글

HDC를 이미지로 저장 (HDC -> HBITMAP) - C++ / GDI+  (0) 2021.04.29
GDI+ 강좌 총정리  (0) 2021.04.22
png파일 출력하기 WINAPI C++  (0) 2021.04.13
파일 자가복제 C++ #2  (0) 2020.11.23
WINAPI C++ #2  (0) 2020.11.07

댓글