'Programming/Programming Tip'에 해당되는 글 8건

  1. 2010/03/17 귀차니스트 Textcube 에서 XQuared 에디터를 쓰면서 Hooriza님의 Codehilight 사용
  2. 2009/01/07 귀차니스트 Gradient에 대한 정리 (2)
  3. 2008/07/10 귀차니스트 Com Control에서 Event 제작하기
  4. 2008/07/08 귀차니스트 Com 개체를 Retun in Com Programming (2)
  5. 2008/07/07 귀차니스트 FreeType 사용방법 (2)

  안녕하세요 오늘은 수업이 끝난 후 간단한 삽질을 통하여 아주 예전에 해결하지 못했던 부분을 해결하여 이렇게 올려봅니다. 예전에 Xquared라는 Springnote에서 사용하는 편집기가 Textcube에서 사용이 가능하게 된 플러그인이 올라왔다고 얘기를 드린 적이 있었습니다. 그리하여 그 플러그인을 적용을 시켰었는데 기존에 사용하던 Hooriza님의 Codehilight 플러그인이 제대로 적용되지 않고 깨지는 문제점이 있었다고 알려드렸던 적도 있었죠.

  그런데 오늘 수업을 마치고 약간 삽질을 해봤습니다. 글은 몇 개 없는 블로그이지만 그래도 2년 정도 된 블로그다 보니 차근차근 글을 쓸 예정이고 해서 수정을 시도를 했었습니다. 그리고 기존에 시도했었던 소스를 웹상에 백업폴더로 저장을 해놓았기에 이를 참고하여 제대로 적용을 완료시켰습니다.

 

Hooriza 님의 Codehilight 플러그인의 호출점 수정

  먼저 Hooriza님의 플러그인에서 Editor에 Html코드를 삽입하는 호출점을 수정하여야 합니다.

ch_main.js (Language : html4strict)
  1. success : function(html) {
  2.     var code = "<br />" + html + "<br />";
  3.     try {
  4.         wnd.xed.insertCodeHilightHtml(code);
  5.         setTimeout(function() { window.close(); }, 10);
  6.     } catch(e) {
  7.         alert("객체를 삽입하는 도중에 에러가 발생하였습니다");
  8.         alert(code + ", " + e.description + ", " + e.source);
  9.     }            
  10. }

  위 코드에서 wnd.xed.insertCodeHilightHtml 이라는 부분이 수정된 부분입니다. 기존의 코드는 이와 같이 되어 있지 않고, Editor.InsertObject인가 그런 것으로 적혀져 있었죠. 그러다보니 없는 메소드를 호출하게 되고 try~catch구문에 걸려 에러메시지만 표시되는데 이와 같이 수정할 경우 뒤에서 만들어줄 insertCodeHilightHtml 함수를 호출하게 됩니다.

 

XQuared - Editor의 개체삽입함수 수정

  원래 XQuared에서 제공하는 Editor.js 파일의 에디터는 insertTemplate 이라는 함수를 제공합니다. 그런데 실제로 이 것을 사용하면 이상한 에러가 뜨면서 제대로 실행이 되지 않는 것을 알 수 있는데요. 그리하여 예전에 수정했었던 소스에서는 아래의 함수를 수동으로 추가를 시켜 주었습니다.

Editor.js (Language : html4strict)
  1. /**
  2.  * Inserts HTML template
  3.  * @TODO: Add selenium test
  4.  *
  5.  * @param {String} html Template string. It should have single root element
  6.  * @returns {Element} inserted element
  7.  */
  8. insertTemplate: function(html) {
  9.     return this.rdom.insertHtml(this._processTemplate(html));
  10. },
  11. /**
  12.  * Inserts HTML
  13.  * @TODO: Add selenium test
  14.  *
  15.  * @param {String} html Template string. It should have single root element
  16.  * @returns {Element} inserted element
  17.  */
  18. insertCodeHilightHtml: function(html) {
  19.     return this.rdom.insertCodeHilightHtml(html);
  20. }

  위 함수에서 insertCodeHilightHtml함수를 만듬과 동시에 내부의 함수에서 this.rdom.insertCodeHilightHtml이라는 멤버함수를 불러주는 까닭은 실제 내용을 처리하는 함수가 rdom에 존재하기 때문입니다. insertHtml이 기존에 문제가 있었기 때문에 새로 인터페이스를 만든 것이죠.

rdom/base.js (Language : html4strict)
  1. /**
  2.  * Inserts given html into current caret position
  3.  *
  4.  * @param {String} html HTML string
  5.  * @returns {Node} Inserted node. It could be different with given node.
  6.  */
  7. insertHtml: function(html) {
  8.     return this.insertNode(this.createElementFromHtml(html));
  9. },
  10. /**
  11.  * Inserts given html into current caret position
  12.  *
  13.  * @param {String} html HTML string
  14.  * @returns {Node} Inserted node. It could be different with given node.
  15.  */
  16. insertCodeHilightHtml: function(html) {
  17.     return this.insertNode(this.createCodeHilightHtml(html));
  18. }
rdom/base.js (Language : html4strict)
  1. /**
  2.  * Creates element from HTML string
  3.  *
  4.  * @param {String} html HTML string
  5.  * @returns {Element} Created element
  6.  */
  7. createElementFromHtml: function(html) {
  8.     var node = this.createElement("div");
  9.     node.innerHTML = html;
  10.     if(node.childNodes.length < 1) {
  11.         throw "Illegal HTML fragment";
  12.     }
  13.     return this.getFirstChild(node);
  14. },
  15. /**
  16.  * Creates element from HTML string
  17.  *
  18.  * @param {String} html HTML string
  19.  * @returns {Element} Created element
  20.  */
  21. createCodeHilightHtml: function(html) {
  22.     var node = this.createElement("div");
  23.     node.innerHTML = html;
  24.     return node;
  25. }

 두 번째 블럭에서 CodeHilightHtml을 수정함과 밑의 체크 구문을 제거한 것으로 createCodeHilightHtml함수를 만들었습니다. 이렇게 하면 제대로 JavaScript 함수 단에서의 호출문제는 제거가된 것인데요. 이렇게 하여 Textcube 에서 Hooriza 님 플러그인을 사용하게 된다면 FIELDSET, LEGEND가 제거되어 글을 작성 후 제대로 표시되지 않는 문제점이 존재합니다. 

 

Whitelist에 Html태그 추가

  Javascript함수를 하나씩 디버깅하여 추적을 해본 결과 해결방법을 찾게 되었습니다. 위 까지는 예전에 진행했었던 단계였던지라 이 번에 적을 것이 점심시간 후 시도했던 방법이라고 보시면 됩니다. 여기서는 xquared.js 파일을 열어서 해당 부분을 찾아봅시다.

xqurared.js (Language : html4strict)
  1. /**
  2.  * Pre-defined whitelist
  3.  */
  4. xq.predefinedWhitelist = {
  5. .
  6. .
  7. .

  이 부분을 찾으셨으면 아래로 내린 뒤, 아래 코드와 같이 해당 tag를 추가하면 됩니다.

xqurared.js (Language : html4strict)
  1. .
  2. .
  3. .
  4.    'var':         xq.commonAttrs.concat(),
  5.    'fieldset':      xq.commonAttrs.concat(),
  6.    'legend':            xq.commonAttrs.concat()
  7. };

  이렇게 추가하신 뒤, 사용하시면 짜잔 제대로 표시가 됩니다. 물론 var는 기존에 존재하던 것이라 fieldset, legend 부분만 추가하시면 되구요^^. 지금 남기는 이 글 또한 문제를 해결한 Editor로 글을 남기는 것이라 감회가 남다르네요.

크리에이티브 커먼즈 라이센스
Creative Commons License
2010/03/17 14:54 2010/03/17 14:54

댓글을 달아 주세요

Gradient에 대한 정리

Programming/Programming Tip 2009/01/07 10:17 귀차니스트

  일전에 Gradient에 대한 기능을 구현하였는데, 최근 몸 상태도 그렇고 마무리되지 않은 다른 부분도 있어 포스팅을 차일피일 미루어 왔습니다. 일단 간단한 Gradient는 색이 차츰차츰 변해가는 그런 것을 지칭하게 되는 데요. 이 기능에 대해서는 여러가지 방법이 존재할 수 있습니다.

  먼저 그리는 방법에 대해서 알아보자면 간단하게 하나의 방향을 향해가면서 색이 점점 변하는 경우를 볼 수 있습니다. 다른 경우는 원형의 경우도 존재할 수 있겠고, 하나의 방향을 향해가면서 절반 정도에 이르러 색이 완전히 다 변하였다 다시 복구 되는 경우도 존재할 수 있겠죠.

  가장 간단한 기울기가 없는 방향성 Gradient를 정리하면 다음과 같습니다.( 예로써 Left 방향을 선택해 보죠. )

Gradient (Language : cpp)
  1. 색상1 = Red;
  2. 색상2 = Blue;
  3. for( int i = 0; i < Width; ++i )  {
  4.    double Percent = static_cast<double>(i) / Width;
  5.    색상Put = RGB( 색상1.Red * ( 1.0 - Percent ) + 색상2.Red * Percent ),
  6.                           색상1.Green * ( 1.0 - Percent ) + 색상2.Green * Percent )
  7.                           색상1.Blue * ( 1.0 - Percent ) + 색상2.Blue * Percent )
  8. }

  와 같이 구현할 수 있습니다. 색상Put는 Width 에 해당하는 i 에 대한 출력 값이 되는 셈이죠. 결국 0 ~ Width를 0% ~ 100%에 대한 Blend 비율로써 생각을 하면 됩니다.

  그럼 기울기가 존재하는 경우를 한 번 생각해 봅시다. 속도를 위해서는 4가지의 경우를 먼저 생각을 해봐야 합니다. 어떤 경우냐면 기울기가 - 인지 + 인지에 따라서 2분류가 먼저 나눠지게 되고, 기울기에 대한 값이 0.5 보다 큰지 작은지에 따라서 각각 또 나눠주어야 합니다.

  왜냐하면 기울기가 한 쪽에 대해서만 처리를 해야 되는 경우를 생각해보면 0.5 이하일때와 0.5 이상일때를 먼저 살펴보도록 하겠습니다. 0.5 이하일때는 선을 하나 그릴 경우 가로로 무척 길게 뻗어나가게 됩니다. Windows GDI 상에서는 화면 좌표의 원점이 좌측 상단이라는 것을 먼저 생각하고 보면,

  위 두 가지의 경우를 살펴보면 (X, 0)좌표에서 (X + 기울기, 높이)로 선을 긋는게 한 쪽에 비효율적이라는 것을 알 수 있습니다. 밑의 경우는 X 좌표를 증가시켜가면서 선을 그려도 많아봐야 2X 만큼의 선을 그리게 되는 반면 위의 경우는 기울기가 0에 가까워 질수록 거의 무한대로 선을 그려야 하기 때문이죠.
  그리하여 위의 경우는 Y 축의 좌표를 증가시켜가며 선을 그리면 됩니다. (0, Y)좌표에서 (너비, Y + 기울기) 처럼 말이죠. 물론 - 기울기의 경우도 동일하게 처리하면 됩니다.

  그리고 저의 경우 MoveToEx 와 LineTo 를 이용하여 Gradient를 구현하였는데, 실제 이 부분에 대해서 처음 고민을 약간 하였습니다. 혹시나 선을 그리는 DDA 알고리즘이 처리하지 못하는 빈 픽셀이 생기면 어떻게 하지 하고 말이죠. 하지만 이 경우도 사실 고민할 경우는 없습니다.
  처음에 기울기에 대해서 먼저 계산을 하여 높이 또는 너비에 대한 X, Y 축 증가량을 미리 구한 뒤 천편일률적으로 목적지에 + 를 하면 되게 됩니다. 왜냐면 선을 그리는 알고리즘에 있어서 기울기가 완전 동일 하다면 위치가 약간 좌우 혹은 상하로 이동했을 뿐 그리는 순서 또한 동일하기 때문입니다.

  그리고 원형 Gradient의 경우도 존재합니다. 이 경우는 상당히 까다로운 경우라고 할 수 있습니다. 피타고라스의 정리를 사용하여 목적지와 원점의 거리를 계산하여 색상을 Blend하면 되긴 하지만 그 것 보다는 원을 그리는 함수로 원을 그리면 됩니다. 물론 Pen에 대해서는 Width가 2로 지정이 되어야 합니다. 1로 지정이 되었을 경우 빈 공간이 차츰차츰 크기가 작아져가면서 그리는 원에 대해서 빈 공간이 사라지고 Gradient가 작성되기 때문이죠.

Radial (Language : cpp)
  1. for( int i = 0; i < Radius; ++i )   {
  2.     double Percent = static_cast<double>(i) / Radius;
  3.     Ellipse(hDC, i, i, 2*Radius - i, 2*Radius - i);
  4. }

  간단하게 적는다면 위와 같은 코드가 작성될 수 있겠군요^^. 타원에 대해서는 Ellipse 함수가 Rect 크기에 대해서 Drawing 이 가능하므로 연구를 개인적으로 하면 가능할 듯 합니다. 실제 구현은 했지만 올리는 것은 금전적 문제의 여지가 많은 듯 합니다^^.

  이 Gradient를 작성하면서 논문을 무척 많이 찾아본것 같습니다. 결국 찾기는 찾았지만 적용은 하지 못하고 그냥 구현하게 되었지만요. 그런 것을 보면서 참 많은 노력이 있었고 그로 인한 속도의 발전이 있지 않았나하는 생각이 많이 들더랍니다. 다른 분야도 마찬가지겠지요. 저도 그런 사람의 하나가 되길 기원하면서 이만 글을 줄이도록 하겠습니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2009/01/07 10:17 2009/01/07 10:17
TAG

댓글을 달아 주세요

  1. kkamagui 2009/04/29 01:30  댓글주소  수정/삭제  댓글쓰기

    와우~ 후배~ 요즘은 블로깅 안하냐?
    너무 뜸해서 함 와본다 ㅋㅋ
    잘 지내고 있지?

    • 귀차니스트 2009/05/21 17:33  댓글주소  수정/삭제

      ㅋㅋ 넹.. MSN 에서 뵈었잖아용 ㅋㅋ 이캄서..
      음.. 뭐 한 1월 부터 갑자기 뭐 쫌 그렇게 됐죠 ㅋㅋ

      슬슬 다시 시작을 해야 되는데 ㅜㅜ 너무 오래 끊은듯 해용

  Com개체를 이용한 프로그래밍을 하다보면 Com Control을 이용하게 될 수도 있습니다. 바로 지금 보고 계시는 IE조차도 Com으로 이루어진 Browser Com 입니다. IE라는 것이 뭐 기능적으로 추가를 시켜 실제로 보는 익스플로어가 되는 것이죠. 그래서 웹마 같은 IE기반의 웹 브라우저가 나올 수 있는 까닭이 이 때문입니다.
  그런데 Control을 보면 예를 들어서 어떠한 이벤트가 끝났을 때 해당 클라이언트에 링크된 함수를 호출시켜주는 경우가 있죠. 컨트롤이 다 그려졌을 때, 뭐 대충 이런것이라고 할까요? 이 부분을 정리해보기로 했습니다.

  일단 ATL 프로젝트를 시작하여 프로젝트를 만든 다음 Control Class 를 추가할 때 Options에서 필수적으로 Connection Points를 체크해야 합니다. 이 것을 해야 이벤트를 구현할 수 있습니다.



  Interface, Apperance, Stock Properties같은 부분에서 설정할 부분은 모두 다 설정한 뒤, "Finish" 혹은 "마침" 버튼을 눌러 해당 컨트롤을 생성하는 과정을 마칩니다. 그럼 Class View에 해당 Control에 대한 클래스와 Event에 해당하는 클래스가 생성됩니다. 그럼 이 Event에 해당하는 Interface에 메소드를 추가해야 합니다.



  겉으로는 드러나지 않고 TEventLib 안으로 한 레벨 들어가면 _ITestControlEvents 라고 있습니다. 여기서 마우스 오른쪽 버튼을 누른 뒤, Add Method를 선택하여 기존 Com Class에 Method를 추가하듯이 해당 메소를 추가합니다. 다만 여기서 하나 생각해줘야 할 점은 Event 라는 것이 해당 Com Control이 클라이언트에게 호출 하는 것이다보니 [in]파라메터로 넘기는게 좋다는 것이죠. 그럼 일단 파라메터가 없는 빈 AfterDraw이벤트를 추가해보도록 하겠습니다.
  그런 다음 CTestControl 에서 마우스 오른쪽 버튼을 눌러 Add Connection Point를 선택합니다. 그 후엔 당연히 _ITestControlEvents를 선택하시면 됩니다. 혹시 CProxy_ITestControlEvents를 더블클릭하여 해당 소스를 봤는데 아무런 차이가 나지 않고 "Fire_메소드명" 이 보이지 않는다면 한 번 빌드를 하신 뒤 다시 해보시기 바랍니다. 만약 제대로 된다면 다음과 같은 소스가 추가됩니다.

Event.cpp (Language : cpp)
  1. HRESULT Fire_AfterDraw()
  2. {
  3.     HRESULT hr = S_OK;
  4.     T * pThis = static_cast<T *>(this);
  5.     int cConnections = m_vec.GetSize();
  6.     for (int iConnection = 0; iConnection < cConnections; iConnection++)
  7.     {
  8.         pThis->Lock();
  9.         CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
  10.         pThis->Unlock();
  11.         IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);
  12.         if (pConnection)
  13.         {
  14.             CComVariant varResult;
  15.             DISPPARAMS params = { NULL, NULL, 0, 0 };
  16.             hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);
  17.         }
  18.     }
  19.     return hr;
  20. }

  이렇게 생성이 된 메소드를 어떻게 호출을 하냐면 일단 목표가 Control이 다 그려지고 난 후에 호출하자고 정했으니 CTestControl 부분의 소스로 건너갑니다. 그 다음 소스를 아래로 주욱 내리시면 OnDraw라는 함수가 보이실 겁니다. 빙고@0@. 드디어 찾았습니다. 그럼 삽입을 해야 겠죠?

OnDraw.cpp (Language : cpp)
  1. HRESULT OnDraw(ATL_DRAWINFO& di)
  2. {
  3.     RECT& rc = *(RECT*)di.prcBounds;
  4.     // Set Clip region to the rectangle specified by di.prcBounds
  5.     HRGN hRgnOld = NULL;
  6.     if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
  7.         hRgnOld = NULL;
  8.     bool bSelectOldRgn = false;
  9.     HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
  10.     if (hRgnNew != NULL)
  11.     {
  12.         bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
  13.     }
  14.     Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
  15.     SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
  16.     LPCTSTR pszText = _T("ATL 8.0 : TestControl");
  17. #ifndef _WIN32_WCE
  18.     TextOut(di.hdcDraw,
  19.         (rc.left + rc.right) / 2,
  20.         (rc.top + rc.bottom) / 2,
  21.         pszText,
  22.         lstrlen(pszText));
  23. #else
  24.     ExtTextOut(di.hdcDraw,
  25.         (rc.left + rc.right) / 2,
  26.         (rc.top + rc.bottom) / 2,
  27.         ETO_OPAQUE,
  28.         NULL,
  29.         pszText,
  30.         ATL::lstrlen(pszText),
  31.         NULL);
  32. #endif
  33.     if (bSelectOldRgn)
  34.         SelectClipRgn(di.hdcDraw, hRgnOld);
  35.     Fire_AfterDraw();
  36.     return S_OK;
  37. }

  단 한 줄 이지만 무지 중요합니다 ㅠㅠ. 이 것을 추가하지 않는다면 제대로 작동하지 않죠~. 추가도 되었으니 이제 소스를 빌드하고 regsvr32 로 등록을 합니다. 그런 다음 C#에서 이제 테스트를 해보는 겁니다. @0@ 컨트롤을 추가하니 AfterDraw 이벤트가 보이는군요!! 당장 추가하고 메시지 박스를 띄워봅니다.



Message.cs (Language : csharp)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. namespace WindowsFormsApplication6
  10. {
  11.     public partial class Form1 : Form
  12.     {
  13.         public Form1()
  14.         {
  15.             InitializeComponent();
  16.         }
  17.         private void axTestControl1_AfterDraw(object sender, EventArgs e)
  18.         {
  19.             MessageBox.Show("다 그렸어!!");
  20.         }
  21.     }
  22. }

  실행을 하면 메시지 박스가 하나 띄워집니다. 그리고 컨트롤을 살짝 가렸다가 다시 보여주니 메시지 박스가 띄워지는 군요^^. 이벤트 방식으로 컨트롤이나 소스를 처리하는 방식도 있는데, 이를 이용하면 아주 유용하게 사용할 수 있을듯 합니다. 한 번 시도해보시는건 어떨까요??



크리에이티브 커먼즈 라이센스
Creative Commons License
2008/07/10 11:58 2008/07/10 11:58
TAG

댓글을 달아 주세요

  어떻게 하다보니 오늘 Com Dll을 만드는 일을 하게 되었습니다. 원인 인즉, .Net 1.1로 이미 배포되어있던 프로그램에 추가했던 Ftp 모듈이 약간 신뢰성이 안좋은 부분이 있어 그 부분을 수정하기 위해서 였습니다. 당시 %진행도가 필요한 Ftp 다운로드 모듈이 필요하여 웹상에 공개되어 있던 소스를 하나 받아 구성하긴 했는데, 그 마저도 바로 적용하지는 못하고 수정을 거쳐 적용을 했었습니다.
  그런데 이번에 부족한 부분이 없도록 수정을 해야 했는데, .Net 1.1에서 지원하지도 않는 것 소켓을 이용해서 통째로 짜기에는 너무 시간이 부족하더군요. 그래서 일단 WinAPI를 땡겨쓴 dll을 만들기로 했습니다. 제가 짧은 시간에 만든 것 보다야 MS에서 오래전에 공개된 API가 신뢰성이 높다고 판단하기 때문이었습니다.
  조금씩 만들어가다 보니 디자인이 영 별로더군요. 그래서 CFtpProgress 라는 클래스에서 Download, Upload 함수를 파일명과 함께 호출을 하면 CProgressObject라는 하나의 Com 개체를 리턴하는 쪽으로 수정하였습니다. 그런데 이게 또 아주 잘 알지 못하는 부분이다 보니 삽질을 하게 되더군요. 일단 삽질을 거쳐 수정을 하긴 했습니다. 방법이 대충 아래와 같더군요.( 테스트 설명 과정이다 보니 개체를 대충 작성 했습니다. )

TestDll.cpp (Language : cpp)
  1. // TestCom.idl : IDL source for TestCom
  2. //
  3. // This file will be processed by the MIDL tool to
  4. // produce the type library (TestCom.tlb) and marshalling code.
  5. import "oaidl.idl";
  6. import "ocidl.idl";
  7. [
  8.     object,
  9.     uuid(4F6057F5-8CED-464E-B0D7-6DC45F50B87B),
  10.     dual,
  11.     nonextensible,
  12.     helpstring("IMain Interface"),
  13.     pointer_default(unique)
  14. ]
  15. interface IMain : IDispatch{
  16.     [id(1), helpstring("method GetSub")] HRESULT GetSub(IDispatch** RetVal);
  17. };
  18. [
  19.     object,
  20.     uuid(E77D270C-26F8-4E31-A7CB-F3BCEDC153A2),
  21.     dual,
  22.     nonextensible,
  23.     helpstring("ISub Interface"),
  24.     pointer_default(unique)
  25. ]
  26. interface ISub : IDispatch{
  27.     [id(1), helpstring("method ShowMessageBox")] HRESULT ShowMessageBox(void);
  28. };
  29. [
  30.     uuid(EAB88B2E-5DD1-4EF0-9354-6C009D79C40E),
  31.     version(1.0),
  32.     helpstring("TestCom 1.0 Type Library")
  33. ]
  34. library TestComLib
  35. {
  36.     importlib("stdole2.tlb");
  37.     [
  38.         uuid(54146920-3926-44F5-9260-7F5A11276E6B),
  39.         helpstring("Main Class")
  40.     ]
  41.     coclass Main
  42.     {
  43.         [default] interface IMain;
  44.     };
  45.     [
  46.         uuid(0FA43AE1-6482-48BA-A096-937DFC03D057),
  47.         helpstring("Sub Class")
  48.     ]
  49.     coclass Sub
  50.     {
  51.         [default] interface ISub;
  52.     };
  53. };

  위와 같이 대충 IMain, ISub 인터페이스를 정의 해봤습니다. http://www.filewiki.net/tc/entry/ATL-Inherit-Implement-ATL으로-COM-라이브러리-작성시-상속-구현하는-방법 에 적어놓은 방법으로 Com Class를 생성 하시면 됩니다. 일단 그냥 Com Class 추가만 하다보니 IDispatch** RetVal로 정의 해놓은 부분이 있습니다. 이 부분이 수정 포인트라고 할 수 있겠군요.

Main.cpp (Language : cpp)
  1. STDMETHOD(GetSub)(ISub** RetVal);
Main.cpp (Language : cpp)
  1. STDMETHODIMP CMain::GetSub(ISub** RetVal)
  2. {
  3.     // TODO: Add your implementation code here
  4.     return S_OK;
  5. }

  일단 해당 Main 클래스를 정의한 C++ 소스에서 위와 같이 IDispatch를 ISub로 바꾸어 줍니다. 그리고 하나 더바꿔주어야 하는 부분이 있습니다. 바로 IDL 파일 부분입니다.

Main.idl (Language : cpp)
  1. [
  2.     object,
  3.     uuid(E77D270C-26F8-4E31-A7CB-F3BCEDC153A2),
  4.     dual,
  5.     nonextensible,
  6.     helpstring("ISub Interface"),
  7.     pointer_default(unique)
  8. ]
  9. interface ISub : IDispatch{
  10.     [id(1), helpstring("method ShowMessageBox")] HRESULT ShowMessageBox(void);
  11. };
  12. [
  13.     object,
  14.     uuid(4F6057F5-8CED-464E-B0D7-6DC45F50B87B),
  15.     dual,
  16.     nonextensible,
  17.     helpstring("IMain Interface"),
  18.     pointer_default(unique)
  19. ]
  20. interface IMain : IDispatch{
  21.     [id(1), helpstring("method GetSub")] HRESULT GetSub([out,retval]ISub** RetVal);
  22. };

  IMain, ISub의 순서를 제대로 바꿔주어야 합니다. 아래에 있는 정의를 위에서 판단하지는 못하겠죠? 일단 ISub를 위로 순서를 수정한 다음, IMain을 아래로 바꾸어 주고, IDispatch역시 ISub로 바꾸어 줍니다. 그리고 마지막 남은 소스 부분에서 정의 해야 합니다.

Main.cpp (Language : cpp)
  1. STDMETHODIMP CMain::GetSub(ISub** RetVal)
  2. {
  3.     // TODO: Add your implementation code here
  4.     CComQIPtr< ISub > ComPtr;
  5.     ComPtr.CoCreateInstance( CLSID_Sub );
  6.     ComPtr.QueryInterface( RetVal );
  7.     return S_OK;
  8. }

  바로 위와같이 해당 기능을 구현합니다. CComQIPtr SmartPointer 클래스를 통하여 ISub 인터페이스에 대한 관리를 쉽게 처리하기로 했습니다. 그냥 그저 ISub 자체만으로도 처리는 가능합니다. 하지만 여기서 다루지는 않겠습니다. 그리고 CoCreateInstance( CLSID_Sub );를 통한 SmartPointer에 실질적인 Com 개체를 생성합니다.
  그런 다음 바로 중요한 것이 QueryInterface인데 Com개체가 Addref, Release와 같은 함수를 통하여 Reference Counting을 수행한다는 것을 알고 계신분들이 계실겁니다. 그래서 그냥 그러 *RetVal = ComPtr;과 같은 대입식으로 Com개체를 리턴한다면 해당 ComPtr이 Scope를 벗어났을때 해당 Com개체는 메모리상에서 삭제되게 됩니다.
  그래서 QueryInterface기본 구현에서 Addref를 호출해주므로 이를 통한 Interface를 얻으면서 대입합니다. 그런 다음 Sub에서는 ShowMessageBox같은 함수를 만들어 테스트를 해보도록 하겠습니다.

Sub.cpp (Language : cpp)
  1. STDMETHODIMP CSub::ShowMessageBox(void)
  2. {
  3.     // TODO: Add your implementation code here
  4.     MessageBox(NULL, L"aaa", L"", MB_OK);
  5.     return S_OK;
  6. }
Test.cs (Language : csharp)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. namespace WindowsFormsApplication5
  10. {
  11.     public partial class Form1 : Form
  12.     {
  13.         public Form1()
  14.         {
  15.             InitializeComponent();
  16.         }
  17.         private void button1_Click(object sender, EventArgs e)
  18.         {
  19.             TestComLib.IMain MainObj = new TestComLib.MainClass();
  20.             TestComLib.ISub SubObj = MainObj.GetSub();
  21.             SubObj.ShowMessageBox();
  22.         }
  23.     }
  24. }

  이렇게 테스트를 해보시길 바랍니다. 이렇게 하면 ShowMessageBox에서 처리한대로 MessageBox 함수를 통한 메시지박스가 표시됩니다. 어떻게 생각하면 약간 복잡할 수도 있지만 의외로 간단할 수 있습니다. 그럼 이를 이용한 좋은 라이브러리를 만들어 보세요^^.



크리에이티브 커먼즈 라이센스
Creative Commons License
2008/07/08 22:21 2008/07/08 22:21
TAG , ,

댓글을 달아 주세요

  1. kkamagui 2008/07/09 07:53  댓글주소  수정/삭제  댓글쓰기

    와우 멋진데 >ㅁ<;)-b

    COM 소스는 언제봐도 적응이 안된다야 ㅜ_ㅜ

    드라이버 소스가 더 쉬운거 같오... ㅜ_ㅜ)-b

    • 귀차니스트 2008/07/09 21:48  댓글주소  수정/삭제

      헐 ㅋㅋ 형 드라이버가 더 어려운거잖아요 ㅠㅠ
      이건 드라이버에 비하면 ㅠㅠ

FreeType 사용방법

Programming/Programming Tip 2008/07/07 23:57 귀차니스트

  요샌 텍스트 편집 및 관리 엔진을 만든다고 천천히 시간에 여유가 있을때 마다 진행하고 있지만 이상한 부분이 FreeType에 있었던것 같아 정리해 두는 편이 좋겠더군요. 그래서 간단한 문제점을 정리해보기로 했습니다.
  먼저 FreeType은 http://freetype.sourceforge.net/freetype2/documentation.html에서 사용법을 볼 수 있고 라이브러리를 다운 받을 수 있습니다. Part I같은 경우 일반적으로 출력하는 방법에 대해서 설명하고 있으며, Part II같은 경우 글자를 위치와 글자간격을 파악하여 출력하기 위한 방법을 사용하고 있습니다.
  그런데 이러한 설명으로는 쉽게 생각하여 간과하고 지나칠 수 있습니다.

1. Glyph Index사용에 주의

TTF 파일을 읽어 처리하는 라이브러리이니 만큼 일단 TTF 문서에 대해서 간략하게 알고 계셔야 문제에 대해서 무척 쉽게 대처할 수 있겠죠? 저도 저번에 시간이 났을 때 잠시 검색하여 읽어보았던 TTF, OTF 문서의 기억으로 많은 도움을 받았습니다.
  이 TTF 파일을 보자면 단 하나의 문자집합으로 이루어져 있지는 않습니다. 확장아스키를 저장하는 방법으로 WANSUNG, JOHAB, SJIS, BIG5등이 있으며, Unicode도 존재합니다. 물론 이 방법에 대해서 출력하는 것이 약간 고려해야 할 부분이 있습니다. 각각의 문자 코드에 대한 것을 다르게 처리 해주어야한 다는 점이죠.

2. Johab은 일단 먼서 선택을..

  제가 이상하게 테스트를 해봐서 그런 것일지는 모르겠습니다만 별도로 Vmware를 깔아 깨끗한 환경을 마련하고 개발을 진행했는데, Wansung, Unicode형 출력과는 다르게 Johab형의 출력은 CharMap을 미리 선택해야 하더군요.
  기본적으로 하나의 TTF 폰트 안에 여러개의 문자집합 형식이 있을 수 있으므로 그 중 하나를 선택하여 출력이 가능하지만 Unicode, Wansung형은 자동으로 0번 집합으로 선택이 되더군요. 내부 소스를 깊숙히 파악하지는 못해 원인은 모르겠지만 아마도 주류가 되는 인코딩은 미리 선택이 되는 것 같습니다.

3. Glyph Index 전달에 있어..

  일반적으로 Glyph의 Index를 얻어오거나, Index를 통해 Glyph를 얻어오는 함수의 전달인자는 FT_UInt 형입니다. 내부적으로 unsigned int 형으로 재정의 되어있습니다. 그런데 Windows의 Unicode및 확장 아스키형의 형태가 2바이트 크기이고 Unicode는 보통 wchar_t형으로 사용합니다. 그런데 Posix에서는 wchar_t가 4바이트 이므로 이를 유의해야 합니다. 자칫 잘못하면 데이터의 값이 변환되는 과정에 -값으로 되어 Glyph를 제대로 못 얻을 수 있습니다. 물론 이 것은 데이터 취급의 문제 이니만큼 신경을 쓴다면 해결할 수 있습니다.

4. Part I에서의 FT_Face속 FT_Glyph는..

  Part I에서 Load_Glyph인가 그 함수를 사용하여 FT_Face멤버로 데이터를 적용시키는 부분이 있습니다. 그런데 이 부분은 짐작하기에 쉬우시겠지만 만약 이 해당 멤버를 문자 유닛별로 보관하려는 것은 하시면 안더군요. 물론 제가 이리저리 테스트를 하는 도중에 나왔던 결과라 적은 것이지만 Render_Glyph를 사용하여 호출하니 해당 Bitmap의 정보가 계속하여 덮어씌여지더군요.
  개개별적으로 처리되는 정보가 아니라는 말이겠죠. 어찌보면 당연한 얘기겠지만 혹시나 하더군요.


  아직 FreeType에 대한 깊은 부분까지 사용하지는 않아서 많은 고려점을 발견하지는 못했지만 만약 앞으로 더 사용하게 된다면 많은 문제점과 맞닥뜨릴지도 모르겠군요. 그나저나 Bold나 Italic같은 부분은 대충 생각하여 아주 간단하게 구현을 할 수 있을 것 같습니다. 물론 미리 준비된 함수로 말이죠. 일단 Text 엔진을 구현하여 FreeTypeMng 클래스들과 연결하여 완성이 되면 추가적으로 테스트 해보고 올려보겠습니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2008/07/07 23:57 2008/07/07 23:57
TAG

댓글을 달아 주세요

  1. 비밀방문자 2010/12/30 05:29  댓글주소  수정/삭제  댓글쓰기

    관리자만 볼 수 있는 댓글입니다.

    • 귀차니스트 2010/12/30 10:14  댓글주소  수정/삭제

      음..
      본문에 Document 주소 링크를 들어가시면 Part 1,2,3에 사용방법에 대한 튜토리얼이 존재합니다. 물론 함수 형태다 보니 C로 사용할 수 있구요^^