WPF에서 애니메이션을 사용하기 위한 방법은 다양하다.

이번에는 WPF에서 애니메이션을 사용하기 위해 Lottie를 사용하는 방법을 알아보도록 하자.

 

https://www.nuget.org/packages/LottieSharp/

'Programming > C#, WPF' 카테고리의 다른 글

[C# / WinForm] HWP to IMAGE, 한글문서 이미지 변환  (0) 2019.07.30
마샬링(Marshalling)  (0) 2019.06.12
[WPF] Bitmap to Image Converter  (0) 2019.05.26

hwp의 확장자를 갖고있는 한글문서를 이미지로 변환하는 작업을 시도해보려고 합니다.

저는 WPF를 주로 사용하기 때문에 WinForm은 익숙하지 않지만, 해당 프로젝트는 WinForm으로 작업했습니다.

그 이유에 대해서는 아래에서 이야기 하도록 하겠습니다.

 

우선은 기능이 정상적으로 동작하는지 확인하는 것이 목표이기 때문에 WinForm프로젝트로 간단하게 구현해봤습니다.

추후 WPF나 다양한 용도로 사용할 수 있도록 라이브러리 프로젝트로 만들수 있는지 시도해보겠습니다.

 

 

작업순서

작업순서는 다음과 같습니다.

  1. 한글 설치 확인
  2. WinForm프로젝트 생성
  3. 한글Control 추가 (COM 구성 요소)
  4. 코드작성
  5. 확인

 

한글 설치 확인

프로젝트 진행에 앞서 개발PC에 정품 라이센스가 인증된 한글 프로그램이 설치되어있는지 확인이 필요합니다.

그 이유는 WinForm프로젝트에서 한글에서 제공하는 Control을 사용하기 때문입니다.

한글 라이센스와 관련된 내용은 정확한 정보를 얻게되면 다시 작성하도록 하겠습니다.

 

 

WinForm프로젝트 생성

WinForm 프로젝트를 생성합니다.

프로젝트 최초 생성

 

WinForm프로젝트를 생성하면 다음과 같이 프로젝트가 생성됩니다.

디자인은 신경쓰지말고 한글문서를 이미지로 변환하는 기능에 포커스를 맞추도록 합니다.

 

한글 Control 추가

한글을 이미지로 변환하기 위해서는 한글 Control이 필요합니다.

앞서 말씀드렸듯이 저는 WinForm에 익숙하지 않기 때문에 [COM 구성 요소]에 있는 한글 Control을 추가하는 방법은 여러가지가 있을 수 있지만 제가 하는 방식을 사용하겠습니다.

 

순서

[도구상자] 메뉴 선택 -> 마우스 오른쪽 버튼 -> 메뉴에서 [항목 선택] 선택 -> [도구 상자 항목 선택]창에서 [COM 구성 요소] 탭 선택 -> 'HwpCtrl Control'의 이름을 가진 또는 비슷한 이름을 가진 Control을 선택 후 [확인]

도구 상자 항목 선택 화면 띄우기
HwpCtrl Control 추가하기

 

또 다른 방법

다른 방식으로는 화면 상단 탭에서 [도구] -> [도구 상자 항목 선택] 메뉴 선택 -> [도구 상자 항목 선택]창에서 [COM 구성 요소] 탭 선택 -> 'HwpCtrl Control'의 이름을 가진 또는 비슷한 이름을 가진 Control을 선택 후 [확인] 하시면 됩니다.

 

이제 WinForm의 윈도우에 클릭 or 드래그를 하면 'HwpCtrl Control'이 추가됩니다.

이렇게 'HwpCtrl Control'이라는 한글을 이미지로 변환하기 위해 필요한 Control이 추가되었습니다.

해당 컨트롤은 아마 자동으로 axHwpCtrl1 이라는 이름으로 생성될것입니다.

HwpCtrl Control 추가

해당 컨트롤에는 아마 한글 문서가 이미지화 되어 나타날 것 입니다. 하지만 목표는 문서를 이미지 파일로 만드는 것이기 때문에 해당 컨트롤의 위치나 크기는 중요하지 않습니다.

 

 

코드작성

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System;
using System.Windows.Forms;
 
namespace HwpToImageSample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
 
            Convert("C:\\Test\\test.hwp""C:\\Test\\Output"100);
        }
 
        private void Convert(string filePath, string outputPath, int dpi)
        {
            // 해당 경로에 파일이 존재하는지 확인
            if (System.IO.File.Exists(filePath))
            {
                string HNCRoot = @"HKEY_Current_User\Software\HNC\HwpCtrl\Modules";
 
                try
                {
                    // 보안모듈 레지스트리에 등록되어 있는지 확인
                    if (Microsoft.Win32.Registry.GetValue(HNCRoot, "FilePathCheckerModuleExample""Not Exist").Equals("Not Exist"))
                    {
                        // 등록되어 있지 않을경우 레지스트리에 등록
                        Microsoft.Win32.Registry.SetValue(HNCRoot, "FilePathCheckerModuleExample", Environment.CurrentDirectory + "\\FilePathCheckerModuleExample.dll");
                    }
 
                    // 불러옵니다.
                    axHwpCtrl1.RegisterModule("FilePathCheckDLL""FilePathCheckerModuleExample");
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
 
                bool isHwpOpen = axHwpCtrl1.Open(filePath, "HWP""forceopen:true;versionwarning:false");
                if (isHwpOpen == false)
                {
                    // 한글 문서 열기 실패
                    return;
                }
 
                int totalPageCount = axHwpCtrl1.PageCount;
                for (int i = 0; i < totalPageCount; i++)
                {
                    string imageFilePath = System.IO.Path.Combine(outputPath, String.Format("page{0:0000}.gif", i + 1));
                    // 이미지 생성
                    axHwpCtrl1.CreatePageImage(imageFilePath, i.ToString(), dpi.ToString(), "24""gif");
                }
            }
        }
    }
}
cs

 

 

'Programming > C#, WPF' 카테고리의 다른 글

[WPF] WPF에서 Lottie 사용하기  (0) 2019.08.02
마샬링(Marshalling)  (0) 2019.06.12
[WPF] Bitmap to Image Converter  (0) 2019.05.26

 

 

마샬링

 

Converter에서의 이미지 변환후 메모리 해제는 자동으로 이루어지지 않습니다.

꼭 GC.WaitForPendingFinalizers(); 를 호출하여 메모리 해제를 해줘야 합니다.

GC.Collect 만으로는 제대로 해제되지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[System.Runtime.InteropServices.DllImport("gdi32.dll")] 
private static extern bool DeleteObject(IntPtr hObject); 
 
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    if (value == null
        return null
 
    try 
    { 
        IntPtr hBitmap = ((System.Drawing.Bitmap)value).GetHbitmap(); 
 
        BitmapSource retval = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, 
            System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); 
 
        DeleteObject(hBitmap); 
        GC.Collect(System.GC.MaxGeneration, System.GCCollectionMode.Forced); 
        GC.WaitForPendingFinalizers();
 
        return retval; 
    } 
    catch (Exception ex) 
    { 
        Console.WriteLine(ex.ToString()); 
        return null
    }
}
cs

'Programming > C#, WPF' 카테고리의 다른 글

[WPF] WPF에서 Lottie 사용하기  (0) 2019.08.02
[C# / WinForm] HWP to IMAGE, 한글문서 이미지 변환  (0) 2019.07.30
마샬링(Marshalling)  (0) 2019.06.12

http://java.elex.pe.kr/2014/01/blog-post.html

동적할당을 사용하는 이유는 무엇일까?

c언어의 메모리 구조에 대해서 알고 있다면 동적할당을 사용하는 이유를 쉽게 알 것이다.


2014/06/26 - [Programming/C언어] - [C] 스택(Stack), 힙(Heap), 데이터(Data)영역



malloc 함수

 - 동적으로 메모리를 할당하는 함수 (힙 영역에 메모리를 할당)


#include <stdlib.h>

void* malloc(size_t size)	// malloc 함수의 원형

함수 호출시 할당하고자 하는 메모리의 크기를 바이트 단위로 전달하면 그 크기만큼 메모리를 할당하게 된다.

그리고 할당한 메모리의 주소(첫 번째 바이트의 주소)를 리턴한다.

메모리 할당에 실패하면 NULL이 리턴된다.


리턴형이 void*(void 포인터) ??


malloc은 단순히 메모리만 할당하는 함수이기 때문에 개발자가 어떠한 데이터 형을 저장하는지 예측할 수 없다.

예를들어 4바이트를 할당하였을 경우 int형 데이터를 저장하기 위해서 사용하는지, float형 데이터를 사용하는지 예측할 수 없기 때문에 void포인터를 반환하여 개발자가 알맞은 용도로 변환하여 사용할 수 있도록 만든것이다.


예를들어 int형 데이터를 저장하기 위해서는 리턴되는 void*을 int*로 변환해야 한다.

int *i = (int*) malloc (sizeof(int));



위의 그림은 포인터 변수 i에 4바이트를 할당하는 그림이다.


1. sizeof(int)의 값은 4이다. 4라는 값을 전달하면서 malloc 함수를 호출한다.

2. 할당된 메모리의 주소가 void*형으로 리턴된다. 리턴되는 void*를 사용하려는 int*형으로 변환한다.

3. 포인터 변수 i에 대입한다.


malloc함수 사용 예

동적 할당을 사용하여 arr_1의 배열의 값을 대입하는 소스를 보며 malloc함수 사용법을 이해해보자.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	return 0;
}



free 함수

 - 힙 영역에 할당된 메모리를 해제하는 함수


메모리를 할당만 하고 해제해 주지 않는다면, 언젠가는 메모리가 부족한 현상이 발생 할 것이다.

할당된 메모리가 더 이상 필요하지 않을경우 free함수를 이용하여 메모리를 해제시켜 줘야한다.


#include <stdlib.h>

void free(void* ptr)	// free 함수의 원형


free함수 사용 예

위의 예제에서 free함수만 추가시켰다.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}



calloc 함수

 - calloc함수는 malloc함수와 같은 기능을 지니고 있다. 다만 사용하는 형태가 조금 다를 뿐이다.


#include <stdlib.h>

void* calloc(size_t elt_count, size_t elt_size)	// calloc 함수 원형

calloc 함수는 elt_size 크기의 변수를 elt_count 개 만큼 저장할 수 있는 메모리 공간을 할당하라는 의미를 갖는다.


calloc함수 사용 예

위의 예제에서 malloc 함수대신 calloc 함수를 사용하였다.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	//arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함
	arr_2 = (int*) calloc(5, sizeof(int));	// sizoe(int)크기의 변수를 5개 저장할 수 있는 공간할당

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}

malloc함수와 calloc함수의 차이점!

malloc은 할당된 공간의 값을은 바꾸지 않는다.

calloc은 할당된 공간의 값을 모두 0으로 바꾼다.

배열을 할당하고 모두 0으로 초기화할 필요가 있을경우에는 calloc을 쓰면 편하다.




realloc 함수

 - 이미 할당한 공간의 크기를 바꿀 때 realloc 함수를 사용한다.


#include <stdlib.h>

void* realloc(void* memblock, size_t size);	// realloc 함수의 원형

이미 할당한 포인터 변수를 memblock에 넣고, 바꾸고 싶은 공간의 크기를 size에 입력하여 사용한다.


realloc함수 사용 예

malloc함수를 사용한 예제에서 realloc 함수를 사용하여 변경하였다.


#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[10];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 10; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	printf("\n");

	// sizeof(int) = 4바이트
	realloc(arr_2, sizeof(int)*10);	// arr_2의 메모리를 40바이트로 재 할당
	// arr_2의 메모리 크기 : 20바이트 -> 40바이트

	for(i = 0; i < 10; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}



C언어의 메모리 구조


프로그램을 실행시키면 운영체제는 우리가 실행시킨 프로그램을 위해 메모리 공간을 할당해준다. 

할당되는 메모리 공간은 크게 스택(Stack), 힙(Heap), 데이터(Data)영역으로 나뉘어진다. 

이러한 메모리 공간이 어떠한 용도로 언제, 어디서 할당되는지 알아보도록 하자.


할당 시기 : 프로그램이 실행될 때마다

할당 장소 : 메인 메모리(RAM)

할당 용도 : 프로그램 실행 시 필요한 메모리 공간(지역변수, 전역변수 선언을 위해) 할당




데이터(Data) 영역


 - 전역 변수와 static 변수가 할당되는 영역

 - 프로그램의 시작과 동시에 할당되고, 프로그램이 종료되어야 메모리에서 소멸됨

 

#include <stdio.h>

int a = 10;	// 데이터 영역에 할당
int b = 20;	// 데이터 영역에 할당

int main() {

	...

	return 0;
}

위와 같은 코드에서 int형 변수 a, b는 프로그램 실행시, main 함수가 호출되기 전에 데이터 영역에 할당된다.

그렇기 때문에 프로그램이 종료될 때까지 메모리상에 존재한다.

(전역변수가 프로그램이 종료될 때 까지 존재하는 이유)



스택(Stack) 영역


 - 함수 호출 시 생성되는 지역 변수와 매개 변수가 저장되는 영역

 - 함수 호출이 완료되면 사라짐

 

#include <stdio.h>

void fct1(int);
void fct2(int);

int a = 10;	// 데이터 영역에 할당
int b = 20;	// 데이터 영역에 할당

int main() {

	int i = 100;	// 지역변수 i가 스택 영역에 할당

	fct1(i);
	fct2(i);

	return 0;
}

void fct1(int c) {
	int d = 30;	// 매개변수 c와 지역변수 d가 스택영역에 할당
}

void fct2(int e) {
	int f = 40;	// 매개변수 e와 지역변수 f가 스택영역에 할당
}

main함수fct1, fct2라는 함수를 추가하였다. 

a, b를 데이터 영역에 할당한 뒤에 main함수를 호출하면서 int형 변수 i는 지역변수로서 스택영역에 할당된다.

그 뒤에 fct1()이라는 함수를 호출하면서 fct1함수의 매개변수인 cd가 스택영역에 할당된다.

fct1()이라는 함수호출이 끝나면 cd는 스택영역에서 삭제되며, 

그 뒤 fct2()라는 함수를 호출하면서 매개변수 e와 지역변수 f가 스택영역에 할당된다.

스택영역은 그 이름그대로 스택의 성질을 띄고있다.


 

힙(Heap) 영역


 - 필요에 의해 동적으로 메모리를 할당 할 때 사용


지금까지 데이터영역과 스택영역을 알아보았는데, 저 두가지 영역만 있으면 코드를 문제없이 짤 수 있을것 처럼 보인다.

그럼 힙영역은 왜 필요한 것일까?


힙 영역은 왜 필요할까?

제일 첫번째 그림을 보면 힙 영역은 프로그래머가 할당한다고 되어있다. 

그럼 언제 할당을 할까? 

배열을 예를들어서 설명을 하겠다.


우리는 배열을 선언할때 상수로 선언을 한다.

int main() {

	// 정상적인 배열선언
	int arr[10];

	// 비 정상적인 배열선언
	int i = 0;
	scanf("%d", &i);
	int arr[i];

	return 0;
}

배열의 길이를 사용자가 입력한 숫자로 잡아주는 것은 비 정상적인 배열선언이다. 왜 비 정상적일까?

메모리 구조에 대해서 잘 파악하고 있다면 당연한 이야기다.


제일 첫번째 그림을 다시보자, 스택 영역에 할당될 메모리의 크기는 컴파일 타임(컴파일 하는 동안)에 결정된다고 되어있다.

정상적인 배열 선언의 경우 arr이라는 배열의 크기가 40바이트 라는것을 알 수 있다.

하지만 비 정상적인 배열선언의 경우 i의 크기가 4바이트 라는 것을 알 수 는 있으나, arr이라는 배열의 크기는 알 수 없다.


그렇다면 다음과 같이 배열을 선언할 때는 문제가 없을까?

int main() {
	
	int i = 10;
	int arr[i];

	return 0;
}

i 라는 변수가 10이기 때문에 arr이라는 배열의 크기가 10이라는 것을 알 수 있지 않을까?

결과는 아니다.


컴파일을 하는 동안 i4바이트의 크기라는 것을 알 수는 있으나, 그 값이 10으로 초기화 되었다는 사실은 무시하고 넘어간다. 값이 10으로 초기화 되었다는 사실은 실행되는 동안, 즉 런타임에 결정된다.

그렇기 때문에 컴파일러는 arr의 크기가 40바이트가 된다는 사실을 알 수 없다. 


사용자의 요구에 맞게 메모리를 할당해 주기 위해서는(런타임에 메모리 크기를 결정하고 싶을 때) 메모리 동적 할당을 통해 힙 영역에 메모리를 할당해야 한다.


힙 영역 : 할당해야 할 메모리의 크기를 프로그램이 실행되는 동안 결정해야 하는 경우(런 타임때) 유용하게 사용되는 공간


힙 영역을 사용하기 위해서는 동적할당에 대해서 공부하여야 한다.


2014/06/26 - [Programming/C언어] - [C] malloc, calloc, realloc을 이용한 메모리 동적 할당


컴퓨터의 모니터 화면을 캡쳐하여 출력


CDC memDC;
CBitmap bitmap;
	
int cx, cy;	// 화면의 크기를 구하기 위해서
// 주 모니터 화면
cx = GetSystemMetrics(SM_CXSCREEN);
cy = GetSystemMetrics(SM_CYSCREEN);
// 듀얼 모니터 사용시 전체화면
//cx = GetSystemMetrics(SM_CXVIRTUALSCREEN);
//cx = GetSystemMetrics(SM_CYVIRTUALSCREEN);

CClientDC dc(this);		// dc선언해주고 (this)에 화면을 출력하기 위해선언
CClientDC ScreenDC(GetDesktopWindow());	// 스크린DC 선언
memDC.CreateCompatibleDC(&ScreenDC);	// 스크린DC와 호환되는 DC생성
bitmap.CreateCompatibleBitmap(&ScreenDC, cx, cy);	// 스크린DC와 호환되는 비트맵 생성
CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);	// Bitmap 포인터를 넘겨줌
memDC.StretchBlt(0, 0, cx, cy, &ScreenDC, 0, 0, cx, cy, SRCCOPY);	// 스크린DC에 저장된화면을 memDC에 copy, bitmap에도 기록됨

// 출력
dc.BitBlt(0, 0, cx, cy, &memDC, 0, 0, SRCCOPY);	// 0,0 부터 해상도크기까지 memDC가 가리키는 참조값 0, 0부터 복사해서 출력함
	
memDC.SelectObject(pOldBitmap);	// 본래의 비트맵 복구??


위 소스를 이용하여 컴퓨터 화면을 캡쳐할 수 있다.

화면을 동영상처럼 캡쳐를 원할경우 Timer를 사용해서 1초에 약 30번 주기로 캡쳐를 하면 된다.


주석이 많기때문에 설명은 생략, 에뮬로 실행한 스마트폰 화면을 찍고싶어서 만들어봤다.

스마트폰 화면을 캡쳐해서 영상처리를 해봐야겠다.


+ Recent posts