Software/Programming2016. 4. 15. 04:19

응용. singleton 패턴과 volatile---DCL(Double Checking Locking)


간단한 싱글톤 예제를 작성하겠다


public class Singleton{

private static Singleton uniqueInstance;


private Singleton() {}


public static synchronized Singleton getInstance(){

if(uniqueInstance == null){

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

}


위 타입의 인스턴스는 유일하게 하나만 존재해야 한다. 그러나 synchronized가 없을 경우, 다수의 쓰레드에서 인스턴스 생성/접근을 시도했을 때, 다수의 인스턴스가 발생할 가능성이 있다.

ex)

1. Thread1 에서 if(uniqueInstance == null) 에 접근

2. Thread1 이 다은 행을 이행하기 전에 Thread2에서 if(uniqueInstance == null) 실행

3. Thread1, 2에서 각각 new Singleton() 실행


synchronized를 적용하면 한 스레드가 메소드 사용을 끝내기 전까지 다른 스레드는 기다려야 한다.

그러나 이 경우 접근에 너무 제한이 들어 동시에 접근이 일어났을 때 처리가 늦어진다.(비용이 너무 크다)

따럿 아래와 같은 double checking locking 기법을 사용한다.


public class Singleton{

private static Singleton uniqueInstance;


private Singleton() {}


public static Singleton getInstance(){

if(uniqueInstance == null){

synchronized (Singleton.class){

if(uniqueInstance == null){

uniqueInstance = new Singleton();

}

}

}

return uniqueInstance;

}

}


설사 Thread2가 객체생성 전에 첫번째 checking에 진입하여도 synchronized에 걸리게 된다.

이미 객체가 생성되어있을 경우 synchronized에 걸릴것 없이 바로 객체를 받게된다.


그러나 아래와 같은 low-level 문제가 발생할 수 있다.


public class Singleton{

private static Singleton uniqueInstance;


private Singleton() {}


public static Singleton getInstance(){

if(uniqueInstance == null){

synchronized (Singleton.class){

if(uniqueInstance == null){

some_memory_space = aloocate space for Singleton object;

uniqueInstance = some_memory_space;

create a real object in some_memory_space;

}

}

}

return uniqueInstance;

}

}


new를 사용하여 객체를 생성할 때, 메모리 공간의 확보-변수에 메모리 공간 링크-해당 메모리에 오브젝트 생성 순으로 작업이 이루어진다. 이 때 아래와 같은 문제가 발생할 수 있다.


1. Thread1은 '변수에 메모리 공간 링크'만 된 상태

2. Thread2가 첫번째 if문에 걸림. 오브젝트가 생성되었다고 착각

3. Thread2가 getInstance()함수를 탈출, 해당 메모리 공간(오브젝트가 아직 미생성) 으로 작업을 하려고 함.

4. 오브젝트가 생성되지 않았기 때문에 에러 발생

이것이 재배치(reordering)과정에 의해 발생하는 문제이다.


public class Singleton{

private volatile static Singleton uniqueInstance;


private Singleton() {}


public static Singleton getInstance(){

if(uniqueInstance == null){

synchronized (Singleton.class){

if(uniqueInstance == null){

uniqueInstance = new Singleton();

}

}

}

return uniqueInstance;

}

}


volatile이 적용된 코드이다. volatile을 적용시켰기 때문에 오브젝트 생성/메인메모리에 배치까지 바로 업데이트가 되어 재배치 문제가 해소된다.


그러나 1.5 미만의 버전의 경우, 변수 자체(uniqueInstance)에만 업데이트 해주도록 되어있다. uniqueInstance = some_memory_space라는 대입과정이 메인 메모리에 실행만 하면 되기 때문에 오브젝트 생성전에 앞선 문제가 발생할 수 있다.


1.5 이상의 버전의 경우 모든 값(상태)가 업데이트가 된다. 즉, 재배치를 스킵하고 오브젝트 생성까지 진행되고 메인 메모리에 업데이트가 되기 때문에 문제가 발생할 소지가 없다는 것이다.

Posted by 십자성군
Software/Programming2016. 4. 15. 02:56

 참고 : http://blog.naver.com/jwmoon74/100157579221


1. volatile 을 사용하지 않은 변수 : 최적화가 될 수 있다. 재배치(reordering) 될 수 있다.

2. volatile 을 사용한 변수(1.5 미만) : 그 변수 자체에 대해서는 최신의 값이 읽히거나 쓰여진다.

3. volatile 을 사용한 변수(1.5 이상) : 변수 접근까지에 대해 모든 변수들의 상황이 업데이트 되고, 변수가 업데이트 된다.




1.

위와 같은 순서로 프로그램이 실행된다고 할 때(ready는 false로 초기화) volatile이 미적용된 상태에서 예상할 수 있는 문제는

1. answer와 ready의 최적화

2. 실행시간에 캐쉬된 값들이 바로 메인 메모리에 업데이트 되지 않을 수 있음


2번 문장이 실행되었는데 캐쉬만 업데이트 한 뒤, mail memory에 적용되지 않고 3번이 실행되었다면 3번에서 ready가 false로 읽어질 수 있다.


2.(1.5미만)

ready에 volatile이 적용될 경우, ready가 읽혀지거나 쓰여질 때마다 바로 업데이트 된다.

따라서 3번 문장에서 문제가 발생하지 않는다.

answer에 대하여 volatile이 적용되지 않았기에 최신의 answer(2)가 4번 문장에 전달되지 않았을 수 있다.


3.(1.5이상)

ready에 volatile이 걸렸을 경우, ready의 값이 읽혀지거나 쓰여질 때마다, 그 때까지의 쓰레드의 '모든 상태'가 업데이트 된다.

즉, thread 1의 ready가 true로 쓰여질 때,

같은 쓰레드에 있던 answer도 메인메모리에 업데이트 된다.

따라서 3번, 4번도 제대로 값이 읽어지게 된다.


Posted by 십자성군
Software/Programming2016. 4. 15. 02:43

본 자료는...


volatile이라는 정의의 의미


1.특정 최적화에 주의해라

2,.멀티 쓰레드 환경에서 주의해라


주로 C,C++이나 MCU의 레지스터를 다룰 때 1번의 의미로 사용되곤 하며

Java에서는 2번의 의미로 사용되곤 한다.


1.

옵티마이즈 기능에 의한 최적화를 시키지 않는다.


ex)


static int foo;
void bar(void)
{
    foo = 0;
 
    while (foo != 255);
}
와 같은 구문에서 최적화 옵션을 켜면, void bar내의 내용을 최적화 되어, while로 갈때는 항상 foo가 255가 아니라 0이기 때문에 

void bar_optimized(void)
{
    foo = 0;
 
    while (true);


위와 같이 된다. foo에 volatile을 적용하면


static volatile int foo;
 
void bar (void)
{
    foo = 0;
 
    while (foo != 255);
}


위와 같이 최적화에 의한 생략과정이 발생하지 않는다.

Posted by 십자성군
Software/Programming2015. 1. 11. 03:10


'Software > Programming' 카테고리의 다른 글

volatile(Java)  (0) 2016.04.15
volatile(C,C++)  (0) 2016.04.15
MFC)버튼을 누르는 동안 지속되는 카운팅 예제  (2) 2014.10.19
비트필드(Bit Field)  (0) 2014.08.11
[비선형 자료구조]트리.그래프 (AVL 트리)  (1) 2013.05.25
Posted by 십자성군
Software/Programming2014. 10. 19. 17:11

주요 개념

1. Sub Classing

2. Thread

3. Counting & Delay


목표

-Button을 꾹~ 누르는 동안 Edit Control의 숫자를 자동으로 상승시키기.

-일반적인 Button Counting도 함께 합니다.

--------------------------------------------------------------

일단 따라해 봅시다.

1. 일반적인 Button Counting


1.MFC 새 프로젝트를 만듭니다. 프로젝트명은 MFCButtonCounting라고 하겠습니다.

2.아래와 같이 오브젝트를 배치합니다.



Edit Control ID : IDC_EDIT1, IDC_EDIT2

Button ID : IDC_BUTTON1, IDC_BUTTON2



누르는 동안의 Counting에 사용할 버튼과 Edit는 상단의 IDC_BUTTON1과 IDC_EDIT1 입니다.

2는 그냥 버튼 Counting에 사용하겠습니다.


MFCButtonCountingDlg.h에 public으로 int EDText1변수를 만들었습니다. 

Dialog 리소스에서 해당 오브젝트에 우클릭 후 변수추가를 해주면 됩니다.


일단 평범한 버튼카운터를 만들어 봅시다. 잘 아시는 분은 넘어가셔도 좋습니다.


Button2의 속성의 컨트롤 이벤트에서 BN_CLICKED를 활성화 해 주었습니다.

우리는 생성된 OnBnClickedButton2를 Overriding해주면 됩니다.



public영역에서 2개의 변수와 하나의 함수를 발견할 수 있습니다. cpp로 가서 OnBnClickedButton2()를 아래와 같이 작성해 줍시다.

EDText2+=1;

UpdateData(FALSE);


실행시키시면 버튼2를 누를때마다 숫자가 증가함을 확인할 수 있습니다.



2. 지속되는 Button Counting

위와같이 일반적인 카운터은 쉽게 만들 수 있으나 버튼을 누르는 동안 지속되는 카운터는 몇가지 고려해야 할 사항이 있습니다.

첫째로 MFC에서는 누르는동안 실행해주기 위한 함수는 제공하지 않습니다.

둘째로 버튼을 누르고 카운팅하는 동안 다른 동작이 정지해서는 안됩니다.


두번째 문제는 Thread를 사용해서 쉽게 해결할 수 있습니다.

첫번째 때문에 저희가 이를 위한 함수를 만들어야 하는거죠.

이를 위한 해결책을 두가지 제시할 수 있는데 첫째가 PreTranslateMessage이고 둘째가 Sub Classing입니다.


PreTranslateMessage를 통해 우리는 소프트웨어에서 발생하는 모든 메시지를 가로채서 이에 대한 처리를 할 수 있습니다.

SubClassing을 사용하면 만들기는 조금 귀찮아질 수 있으나 원하는 메시지에 대해서만 처리시킬 수 있습니다. 여기서는 SubClassing을 사용합니다.


SubClassing을 사용해서 Button1에 대해 종속되는 Class를 하나 생성할 것입니다.


1.

프로젝트->클래스 추가를 통해 MFC클래스를 만들어 줍니다.


위와 같이 만들어 줍니다.


2.

클래스마법사를 이용해서 다음과 같이 메시지 함수를 오버라이딩 합니다.

버튼을 눌렀을때와 뗐을때의 메시지를 만들었습니다.


3.


'GetParent()->'로 부모클래스의 객체를 불러오고

PostMessageW를 사용해서 부모 클래스에 메시지를 보냅니다. GetDlgCtrlID()를 통해 부모객체 여기서는 BUTTON1의 아이디 IDC_BUTTON1을 참조할 수 있습니다. 여기서 +몇을 해 주어도 상관 없습니다.

후에 부모클래스에서 OnCommand를 통해 받는 파라메터값이 됩니다.


Dlg의 헤더에 아래 변수를 추가합니다.


private:

ButtonClass m_TestButton;


m_TestButton : 앞서 작성한 클래스형의 변수입니다.


아래와 같이 초기화를 해줍니다.


이렇게 m_TestButton은 IDC_BUTTON1의 서브클래스가 되었습니다.

이걸로 서브클래스에서 작성할 것은 끝났습니다.


4.

이제 Thread를 만듭니다.


Dlg 헤더파일에 다음을 추가로 작성해 줍시다.


public:

CWinThread* CountingThread;

LRESULT OnUpdateData(WPARAM wParam, LPARAM lParam);


private:

ButtonClass m_TestButton;

virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);


CountingThread : Thread 포인터 변수입니다.

OnCommand : 클래스가 메시지를 받았을 때의 동작을 정하는 함수입니다.

OnUpdateData : 메시지 처리후의 동작입니다. 짧지만 제일 중요한 부분입니다.


OnCommand는 클래스마법사에서 가상함수로 만들 수 있습니다.

OnUpdateData는 직접 작성하시면 됩니다.



위와 같은 함수를 만들어 줍시다.

Delay는 말 그대로 버튼을 누르고 있는동안 딜레이를 주기위한 함수입니다. 이부분은 가져온 부분이라서 관심있으신 분은 스스로 찾아보셔야 하겠습니다.


MyThread는 버튼을 누른동안 Thread로써 기능할 함수입니다. 여기서 pClass는 Dlg클래스가 되겠습니다. 저 두 함수위에 bool threadFlag = false로 변수를 하나 만들어 주었습니다. 버튼을 누른동안 true가 될것이고 떼었을 때 false로 전환될것입니다. 딜레이를 주면서 매 루프마다 변수를 1씩 늘려줍니다.



여기서 PostMessageW는 잠시 보류하고 대신 UpdateData(FALSE); 구문을 넣어줍시다.


그 아래 PostMessageW(THREAD_UPDATE)가 중요합니다.


주의할 것은 두 함수 다 클래스 밖에 있는 전역함수라는 것입니다.Thread는 클래스 밖에서 동작하고 있습니다.


5.

Thread를 동작시켜 봅시다.

OnCommand의 내용입니다. DestroyThread는 아직 놔둡시다. 다른 파일로 해서 클래스명이 약간 다릅니다. 조심해 주세요.

서브클래스로부터 받은 값에 따라 동작하게 하였습니다.

여기서 중요한 구문은 CountingThread = AfxBeginThread(MyThread,this) 입니다.

AfxBeginThread에 대해서 조사바랍니다. MFC에서 사용하는 Thread관련 함수이며 대상 함수가 정적함수여야 인수로 받을 수 있습니다.


만들어진 소프트를 실행시켜 봅시다. 에러가 발생합니다. UpdateData함수는 메인 클래스 밖의 함수에서 사용할 수 없습니다. 우선은 MyThread의 UpdateData구문을 지워줍시다. 그러면 값의 갱신이 실시간으로는 보이지 않지만 갱신되고 있음을 알 수 있습니다.

꾹~ 누르는 동안은 아무변화 없다가 버튼을 떼면 값이 갱신됩니다.


6.

클래스 밖에서 UpdateData구문을 실행할 수 없기 때문에 메시지를 보내 실행하게 해야  합니다.


Dlg의 헤더파일에 아래와 같은 구문을 넣습니다.


클래스 내에 아래 구문을 추가합니다.


public:

LRESULT OnUpdateData(WPARAM wParam, LPARAM lParam);


OnUpdateData : 메시지 처리후의 동작입니다. 짧지만 제일 중요한 부분입니다.

OnUpdateData는 직접 작성하시면 됩니다.


Dlg의 cpp의 메시지맵에 ON_MESSAGE구문을 넣습니다.


Dlg의 cpp에 아래 함수를 추가합니다.




위 MyThread 함수에 있는 PostMessageW 를 활성화 시킵시다.

실시간으로 업데이트되는 것을 확인할 수 있습니다.

'Software > Programming' 카테고리의 다른 글

volatile(C,C++)  (0) 2016.04.15
구조체와 공용체. 공용체의 패딩  (0) 2015.01.11
비트필드(Bit Field)  (0) 2014.08.11
[비선형 자료구조]트리.그래프 (AVL 트리)  (1) 2013.05.25
전위표기. 후위표기  (0) 2013.05.25
Posted by 십자성군
Software/Programming2014. 8. 11. 12:19

프로그래밍 목적이라기 보다는 DSP에서 한번 나왔던 개념이기에 혹시 궁금하신 분들은 참고하길 바랍니다~

 

사전적인 의미

:구조체의 구성 요소(member)의 선언에서 ':'을 함께하는 정수식을 지정하는 방법

 

목적

:메모리의 낭비를 줄이기 위함

 

요즘 컴퓨터는 성능이 매우 좋아서 별로 쓰이지 않지만, DSP와 같이 자원이 한정적인 기기들에는 여전히 유용하다.

예를들어 Int형 정수를 사용하는데 그 값은 0아니면 1만 나온다고 하자.(가령 GPIOA의 MUX는 0아니면 1이면 충분하다.) Uint16이라고 해도 16비트 즉 2바이트다. 1비트면 충분한데 16비트로 사용한다면 정말 큰 메모리 낭비이지 않겠는가? 이때, 선언은 16비트라도 메모리를 1비트로 사용할 수 있다.

 

아래 구조체를 보자

 

struct BitFieldTest    {

unsinged int a : 2;

unsinged int b : 3;

};

 

이 구조체에 대한 size는 32비트(16X2)로 잡히고 하위 두 비트(0,1 번째)와 그 위의 세 비트(2,3,4 번째)가 사용되고 나머지 비트들은 사용되지 않는다. 따라서 메모리를 아낄 수 있다.

 

단점을 들자면 이 32비트 메모리에서 특정 비트들만 값을 변경할 때, 예를 들어 BitFieldTest.a = 1을 하면 단순히 a에 1을 대입하는 것이 아니라 (BitFieldTest & 3 | 1)로 처리되어 비트 연산을 수행하기 때문에 메모리는 아끼지만 속도가 느려지게 된다. MCU에서 MUX레지스터를 자주 바꾸지는 않을테니 큰 문제는 되지 않을것이라 본다.

위의 (BitFieldTest & 3 | 1)이 무슨 의미냐 하면 3이란 숫자는 0b11로 2비트를 의미하기 때문에 BitField의 2비트를 &로 추려내고 여기에 or연산자로 1을 대입하는 것이다. 따라서 한번 연산할 것을 2번연산하게 되는 것이다.

 

struct GPAMUX_BITS    {

Uint16 PWM1_GPIOA0 : 1;

Uint16 PWM2_GPIOA1 : 1;

...

};

 

DSP 28x의 GPAMUX_BITS이다 단지 ON, OFF 만 사용한다면 0,1이면 되기에 1비트만으로도 충분하다.

 

signed 변수를 사용할 때 오버플로우 또는 언더플로우가 발생하지 않도록 값의 대입을 조심하도록 하자.

또한 당연하겠지만 비트수에 따른 표현 가능한 수의 갯수는 2의 n승이다.

Posted by 십자성군
Software/Programming2013. 5. 25. 16:46

다룰것.


이진트리

최적 이진 탐색트리

AVL 트리

2-3 트리

2-3-4 트리

레드블랙 트리(rb트리)

M-원 트리

B-트리

etc




일반적인 이진트리이다. 그러나 마구잡이로 넣어버리면 한쪽방향으로만 뻗어가는 트리가 생길 수도 있겠지?

트리를 만드는 목적은 무엇이냐?

배열이나 단순한 링크드리스트같은 선형구조는 원하는 자료 탐색에 있어서 난항을 겪게된다.

여기에서 탐색을 위한 규칙성이 있다면 탐색하기 편하겠다. 그런데 이진트리에서 한쪽 방향으로만 뻗어버리면 배열과 다를게 없을것이다.




다음과 같은 이진탐색트리에서 알 수 있는 규칙성은?

한 노드에서 그 왼쪽으로 뻗은 자식은 부모보다 작은 숫자이다.

반면 오른쪽으로 뻗은 자식은 부모보다 큰 숫자이다.




트리는 탐색, 접근시간이 짧아야 한다. 여기서 AVL트리의 차례. 트리의 회전을 이용하자.

AVL Tree 개념 : http://blog.naver.com/ryutuna?Redirect=Log&logNo=100122795293



균형치(Balance Factor)가 +-2 미만이어야 한다. 2 이상일 경우 LL, RR, LR, RL 회전을 통하여 균형치를 맞춰준다.



LL회전


RR회전


LR회전


RL회전


용어 정리

1. 균형치(Balance factor)

- 균형치 = 왼쪽 서브트리의 높이 – 오른쪽 서브트리의 높이

2. 트리의 높이(Height)

- 높이 혹은 깊이(Depth)라고 하며 그 트리에 속한 노드의 최대 레벨로 정해진다.

3. 레벨(Level)

- 루트를 레벨1로 정의하기도 하고 0으로 정의하기도 하지만 본 문서에선 1로 정의하도록 하겠다. 만약 한 노드의 레벨이 L이라면 그 자식의 레벨은 L+1이 된다.


B+트리 설명은 다음 링크에서...

http://blog.naver.com/hytgogo?Redirect=Log&logNo=30121130366

'Software > Programming' 카테고리의 다른 글

MFC)버튼을 누르는 동안 지속되는 카운팅 예제  (2) 2014.10.19
비트필드(Bit Field)  (0) 2014.08.11
전위표기. 후위표기  (0) 2013.05.25
MFC 새로운 가상함수 추가  (0) 2012.08.12
[MFC] bmp파일 열기  (3) 2012.08.11
Posted by 십자성군
Software/Programming2013. 5. 25. 16:32

ex)전위표기

4*5-14/7*3 이 있다고 하자


괄호를 적절하게 넣어서 표현하면

(4*5)-((14/7)*3)이다.


제일 안쪽에서 먼저 계산할것부터 표현하면

1. 4*5

2. 14/7

3. (2)*3

4. (1)-(3)


4번부터 순서대로 표기하면

1. -(1)(3)

2. -(*4 5)(3)

3. -*4 5 (*(2) 3)

4. -*4 5 (*/14 7 3)


결과 : -*4 5 */ 14 7 3        :전위표기

ex)후위표기

4번부터 순서대로 표기하면

1. (1)(3)-

2. (4 5 *)(3)-

3. 4 5 * ((2) 3 *)-

4. 4 5 * 14 7 / 3 * -


결과 : 4 5 * 14 7 / 3 * -


Stack을 이용하여 전위표기를 후위표기로 표기하는 알고리즘을 구현할 수 있다

Posted by 십자성군
Software/Programming2012. 8. 12. 00:24

클래스 마법사를 이용

클래스 속성의 이벤트 오픈


가상함수는 원래 존재하는 함수의 기능을 바꾸어 구현할때 사용

Posted by 십자성군
Software/Programming2012. 8. 11. 19:33

목표.

1. bmp파일을 연다

2. 8비트와 24비트 컬러를 출력한다.


프로젝트명 : BmpReading


BmpReading.h

==========================================================================================

// BmpReadingDoc.h : CBmpReadingDoc 클래스의 인터페이스

//


#pragma once



class CBmpReadingDoc : public CDocument

{

protected: // serialization에서만 만들어집니다.

CBmpReadingDoc();

DECLARE_DYNCREATE(CBmpReadingDoc)


// 특성입니다.

public:


// 작업입니다.

public:


// 재정의입니다.

public:

virtual BOOL OnNewDocument();

virtual void Serialize(CArchive& ar);

#ifdef SHARED_HANDLERS

virtual void InitializeSearchContent();

virtual void OnDrawThumbnail(CDC& dc, LPRECT lprcBounds);

#endif // SHARED_HANDLERS


// 구현입니다.

public:

unsigned char *m_InImg;       // bmp파일의 이미지 내용 저장

unsigned char *m_OutImg;     // bmp이미지 출력용 배열

BITMAPFILEHEADER dibHf;    // 비트맵 파일헤드 구조체

BITMAPINFOHEADER dibHi;    // 비트맵 영상헤드 구조체

RGBQUAD palRGB[256];        // 팔레트 정보 구조체 배열

virtual ~CBmpReadingDoc();   // 이들을 가상변수로 구현화 한다

#ifdef _DEBUG

virtual void AssertValid() const;

virtual void Dump(CDumpContext& dc) const;

#endif


protected:


// 생성된 메시지 맵 함수

protected:

DECLARE_MESSAGE_MAP()


#ifdef SHARED_HANDLERS

// 검색 처리기에 대한 검색 콘텐츠를 설정하는 도우미 함수

void SetSearchContent(const CString& value);

#endif // SHARED_HANDLERS

public:

virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);

virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);

};


=======================================================================================

BmpReadingDoc.cpp    (일부)

=======================================================================================


#define WIDTHBYTES(bits) (((bits)+31)/32*4) //추가, 영상의 가로줄은 4바이트의 배수


//새로 정의한 가상함수 OnOpenDocument

BOOL CBmpReadingDoc::OnOpenDocument(LPCTSTR lpszPathName)

{

if (!CDocument::OnOpenDocument(lpszPathName))

return FALSE;


// TODO:  여기에 특수화된 작성 코드를 추가합니다.

//CFile에서 처리

CFile hFile;        //파일을 읽어들이면 이 클래스 형식으로 저장.


int height;        //이미지의 높이를 저장할 변수

int width;        //이미지의 가로길이를 저장할 변수



hFile.Open(lpszPathName,CFile::modeRead | CFile::typeBinary);    //파일 열기

hFile.Read(&dibHf,sizeof(BITMAPFILEHEADER));                          //dibHf에 파일헤더를 읽어들인다.

//이 파일이 BMP파일인지 검사 

if(dibHf.bfType!=0x4D42

{

AfxMessageBox(L"Not BMP file!!");                                        //프로젝트 생성시 유니코드를 사용하게 할 경우

return FALSE;                                                                      //L을 붙여준다

}

hFile.Read(&dibHi,sizeof(BITMAPINFOHEADER));                             //영상정보의 header를 읽기

if(dibHi.biBitCount != 8 && dibHi.biBitCount != 24)                                //8,24비트가 아닐경우

{

AfxMessageBox(L"Gray/True Color Possible!!");

return FALSE;

}

if(dibHi.biBitCount==8)

//8비트의 경우 팔레트를 생성해 주어야 한다. 총 256가지 색이므로 그 길이만큼 읽어들인다

hFile.Read(palRGB,sizeof(RGBQUAD)*256);

//메모리 할당

int ImgSize;

if(dibHi.biBitCount == 8)

{

ImgSize = hFile.GetLength()-sizeof(BITMAPFILEHEADER)-sizeof(BITMAPINFOHEADER)-256*sizeof(RGBQUAD);    //이미지의 크기는 파일 총 길이에서, 두 헤드와 팔레트의 사이즈를 

}                                                                       //제외한다.

else if(dibHi.biBitCount == 24) //컬러영상

{

ImgSize = hFile.GetLength()-sizeof(BITMAPFILEHEADER)-sizeof(BITMAPINFOHEADER);

}


//이미지를 저장, 출력할 배열생성.

m_InImg = new unsigned char [ImgSize];

m_OutImg = new unsigned char [ImgSize];

hFile.Read(m_InImg, ImgSize);

hFile.Close();


//이미지의 길이정보

height = dibHi.biHeight;

width = dibHi.biWidth;


return TRUE;

}



BOOL CBmpReadingDoc::OnSaveDocument(LPCTSTR lpszPathName)

{

// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.

CFile hFile;

if(!hFile.Open(lpszPathName,CFile::modeCreate | CFile::modeWrite | CFile::typeBinary))

return FALSE;

//정보저장

hFile.Write(&dibHf,sizeof(BITMAPFILEHEADER));

hFile.Write(&dibHi,sizeof(BITMAPINFOHEADER));

if(dibHi.biBitCount==8)

hFile.Write(palRGB,sizeof(RGBQUAD)*256);

hFile.Write(m_InImg,dibHi.biSizeImage);

hFile.Close();

return TRUE;

}


===============================================================================

BmpReadingView.h    (일부)

===============================================================================

// 구현입니다.

public:

BITMAPINFO* BmInfo;            //비트맵 영상헤드를 선언

virtual ~CBmpReadingView();


===============================================================================

BmpReadingView.cpp    (일부)

===============================================================================

CBmpReadingView::CBmpReadingView()

{

// TODO: 여기에 생성 코드를 추가합니다.

BmInfo = (BITMAPINFO*)malloc(sizeof(BITMAPINFO)+256*sizeof(RGBQUAD));

for(int i=0; i<256; i++)

{

BmInfo->bmiColors[i] .rgbRed= BmInfo->bmiColors[i] .rgbGreen = BmInfo->bmiColors[i] .rgbReserved = 0;

}

}


CBmpReadingView::~CBmpReadingView()

{

if(BmInfo) delete BmInfo;

}


void CBmpReadingView::OnDraw(CDC* pDC)

{

#define WIDTHBYTES(bits) (((bits)+31)/32*4);        //이미지 가로 바이트 길이는 4바이트의 배수

//BmInfo;

int height;

int width;

int rwsize;


CBmpReadingDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if (!pDoc)

return;

// TODO: 여기에 원시 데이터에 대한 그리기 코드를 추가합니다.

if(pDoc->m_InImg==NULL)

return;


//24비트 비트맵 파일의 영상출력

if(pDoc->dibHi.biBitCount==24){

height=pDoc->dibHi.biHeight;

width=pDoc->dibHi.biWidth;

rwsize=WIDTHBYTES(pDoc->dibHi.biBitCount*pDoc->dibHi.biWidth);

BmInfo -> bmiHeader = pDoc->dibHi;

SetDIBitsToDevice(pDC->GetSafeHdc(),0,0,width,height,0,0,0,height,pDoc->m_InImg,BmInfo,DIB_RGB_COLORS);

}

else //8비트 컬러일 경우

{

int index;

rwsize=WIDTHBYTES(pDoc->dibHi.biBitCount*pDoc->dibHi.biWidth);


   //팔레트를 읽어들이며 반복출력

for(int i=0; i<pDoc->dibHi.biHeight;i++)

for(int j=0; j<pDoc->dibHi.biWidth;j++){

index=pDoc->m_InImg[i*rwsize+j];

BYTE R=pDoc->palRGB[index].rgbRed;

BYTE G=pDoc->palRGB[index].rgbGreen;

BYTE B=pDoc->palRGB[index].rgbBlue;


//pDC->SetPixel(j,i,RGB(R,G,B));

 //영상 반전출력

pDC->SetPixel(j,pDoc->dibHi.biHeight-i-1,RGB(R,G,B));


}

}

}

Posted by 십자성군