Cfile23.uf .135D39524D273E3334486D

클립보드 기능 구현

이번에는 자주 사용하는 기능인 C&P(Copy & Paste)를 구현해 보도록 하겠습니다.

C&P를 구현하기 위해서는 이미지를 DIB(장치 독립 비트맵;Device Independent Bitmap)으로 변환하여 클립보드 영역에 넘겨주거나, 클립보드 영역에서 데이터를 받아오면 됩니다.

클립보드 영역으로 데이터를 넘겨주기 위하여 CopyClipBoard맴버함수, CopyToHandle 맴버 함수를 추가하여, ID_EDIT_COPY이벤트 핸들러에 등록합니다.

먼저 CopyClipBoard 맴버 함수를 Doc Class에 작성하겠습니다.

Cfile28.uf .1110DD4D4D273E3214FDCF

 

반환 형식 : void

함수 이름 : CopyClipBoard

매개 변수 형식 : IplImage *

매개 변수 이름 : m_pCopyImage

Cfile23.uf .135D39524D273E3334486D

Cfile10.uf .1113BB474D273E341606F4

다음과 같이 코딩합니다.

HANDLE hDIB = CopyToHandle(m_pCopyImage);

if(::OpenClipboard(AfxGetMainWnd()->GetSafeHwnd())){

        if(::EmptyClipboard()){

                if(NULL == ::SetClipboardData(CF_DIB, hDIB)){

                        AfxMessageBox(TEXT("클립보드에 영상을 저장하는 도중 오류가 발생했습니다."));

                }

        }

}

::CloseClipBoard();

이번에는 아까와 같은 방법으로 CopyToHandle 맴버 함수를 작성합니다.

반환 형식 : HANDLE

함수 이름 : CopyToHandle

매개 변수 형식 : IplImage *

매개 변수 이름 : image

 

Cfile25.uf .1858CF504D273E340666D8

 

다음과 같이 코딩합니다. (출처 : 오픈소스 OpenCV를 이용한 컴퓨터 비전 실무 프로그래밍 p237)

       int nChannels = image->nChannels;  

        int bpp = 8*nChannels;  

        IplImage *flip_image = cvCloneImage( image );  

        cvFlip( image, flip_image, 0 );   

        int bmpDataSize = flip_image->imageSize;  

        // 비트맵 헤더 구성   

        BITMAPINFOHEADER bmpHeader;  

        bmpHeader.biSize = sizeof(BITMAPINFOHEADER); // 이 구조체의 크기  

        bmpHeader.biHeight = image->height; // 영상의 높이  

        bmpHeader.biWidth = image->width;   // 영상의 너비  

        bmpHeader.biPlanes = 1; // 비트 플레인 수 (항상 1임)  

        bmpHeader.biBitCount = bpp; // 한 화소당 비트 개수   

        bmpHeader.biCompression = BI_RGB; // BI_RGB : 압축하지 않음  

        bmpHeader.biSizeImage = bmpDataSize; // 영상의 크기  

        if(8 == bpp) bmpHeader.biClrUsed = bmpHeader.biClrImportant  = 256;   

        else if(24 == bpp) bmpHeader.biClrUsed = bmpHeader.biClrImportant  = 0;

        int bmpAllSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256 + bmpDataSize*sizeof(char);  

        HGLOBAL hDIB = (HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE| GMEM_ZEROINIT,  

                                                                                  bmpAllSize );  

        if(NULL == hDIB) return NULL;  

        LPSTR pDIB = (LPSTR)::GlobalLock((HGLOBAL)hDIB);  

        memcpy( pDIB, &bmpHeader, sizeof(BITMAPINFOHEADER) );  

 

        if(8 == bpp){  

                RGBQUAD palette[256];  

                memcpy( pDIB+sizeof(BITMAPINFOHEADER), palette, sizeof(RGBQUAD)*256 );  

                memcpy( pDIB+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256,   

                                flip_image->imageData, bmpDataSize );  

        }   

        else if(24 == bpp){  

                memcpy( pDIB+sizeof(BITMAPINFOHEADER),   

                                flip_image->imageData, bmpDataSize );  

        } 

        ::GlobalUnlock((HGLOBAL)hDIB);  

        cvReleaseImage( &flip_image );  

   

        return hDIB;

이제 ID_EDIT_COPY에 대한 이벤트 핸들러만 수정해 주면 끝입니다.

이벤트 핸들러를 수정하기 위해 다음과 같이 합니다.

Cfile29.uf .156138574D273E353B7FC5

void CMFC_OpenCVView::OnEditCopy()

{

        // TODO: 여기에 명령 처리기 코드를 추가합니다.

        CMFC_OpenCVDoc* pDoc = GetDocument();  

        ASSERT_VALID(pDoc);

        pDoc->CopyClipBoard( pDoc->m_CvvImage.GetImage() ); 

}

 

프로그램에서 Copy하여 그림판 같은 곳에 Paste했을 때 정상적으로 동작한다면 성공.

Cfile2.uf .1646014B4D273E361A6057

 

이번에는 Paste기능을 구현해 보도록 하겠습니다. Paste는 Copy의 역순입니다.

CreateFromHandle맴버 함수를 작성하고, ID_EDIT_PASTE에 이벤트 핸들러를 등록하고, 마지막으로 클립보드 영역에 DIB 데이터가 존재할 경우에만 ID_EDIT_PASTE가 동작하도록 하기 위해서 OnUpdateEditPaste 맴버 함수를 작성하겠습니다. 이번에는 모든 함수가 App Class에서 작성될 것이고, 코드만 첨부합니다.

void CMFC_OpenCVApp::OnEditPaste()

{

        // TODO: 여기에 명령 처리기 코드를 추가합니다.

        POSITION pos = GetFirstDocTemplatePosition();  

        CDocTemplate *pTemplate = GetNextDocTemplate(pos);  

        CMFC_OpenCVDoc *pDoc = (CMFC_OpenCVDoc* )pTemplate->OpenDocumentFile(NULL);   

  

        if( pDoc ){  

                HANDLE hDIB = NULL;  

                if (::OpenClipboard(AfxGetMainWnd()->GetSafeHwnd())) hDIB = ::GetClipboardData(CF_DIB);  

  

                if( hDIB ){  

                        IplImage *image = CreateFromHandle( hDIB );  

          

                        pDoc->m_CvvImage.CopyOf( image, image->nChannels*8 );  

  

                        POSITION pos = pDoc->GetFirstViewPosition();  

                        CMFC_OpenCVView *pView = (CMFC_OpenCVView *)pDoc->GetNextView(pos);  

  

                        CSize sizeTotal = CSize(pDoc->m_CvvImage.Width(),  

                        pDoc->m_CvvImage.Height());  

  

                        pView->SetScrollSizes(MM_TEXT, sizeTotal);  

                        pView->ResizeParentToFit(FALSE);  

                }  

  

                ::CloseClipboard();  

        } 

}
IplImage * CMFC_OpenCVApp::CreateFromHandle(HANDLE hDIB)

{

    LPSTR pDIB = (LPSTR)::GlobalLock((HGLOBAL)hDIB);    

    BITMAPINFOHEADER bmpHeader;   

    memcpy( &bmpHeader, pDIB, sizeof(BITMAPINFOHEADER) );    

      

    int height = bmpHeader.biHeight;  

    int width = bmpHeader.biWidth;  

    int nChannels = bmpHeader.biBitCount/8;  

    int bmpDataSize = bmpHeader.biSizeImage;  

    char *imageData = (char *)calloc( bmpDataSize, sizeof(char) );  

  

    if(8 == bmpHeader.biBitCount)

        memcpy( imageData, pDIB+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256,   

                bmpDataSize );

    else if(24 == bmpHeader.biBitCount)

        memcpy( imageData, pDIB+sizeof(BITMAPINFOHEADER), bmpDataSize );  

 

    IplImage *flip_image = cvCreateImage(cvSize(width,height), IPL_DEPTH_8U, nChannels);  

    memcpy( flip_image->imageData, imageData, bmpDataSize );  

 

    IplImage *recover_image = cvCloneImage( flip_image );  

    cvFlip( flip_image, recover_image, 0 );  

  

    ::GlobalUnlock((HGLOBAL)hDIB);    

 

    free(imageData);  

    cvReleaseImage( &flip_image );  

  

    return recover_image;  

}
void CMFC_OpenCVApp::OnUpdateEditPaste(CCmdUI *pCmdUI)

{

        // TODO: 여기에 명령 업데이트 UI 처리기 코드를 추가합니다.

        if( !IsClipboardFormatAvailable(CF_DIB) ) pCmdUI->Enable( FALSE );

}

 

App Class의 헤더 파일에 #include 를 추가해 줘야 합니다.

 

Cfile5.uf .114B54504D273E372AE244

cfile25.uf.19311C444D273F25031758.pdfcfile7.uf.1227C1444D273F261C4D3D.zip

Similar Posts

  • 데이터 종속성과 정규화

    함수종속 어떤 릴레이션 R에서 X와 Y를 의 애트리뷰트 집합의 부분집합이라 하자.  X의 값 각각에 대해 시간에 관계 없이 Y의 값이 오직 하나만 연관되어 있을 때 Y는 X에 함수종속이라 하고 X →Y로 표기한다. 이 때, X를 결정자, Y를 종속자라고 한다. 제 1 정규형(1NF) 어떤 릴레이션 R에 속한 모든 도메인이 원자값만으로 되어 있다면 제 1 정규형이라고 한다. 제 2…

  • |

    2계층과 4계층의 오류제어, 흐름제어

    오류제어 및 흐름제어 기능은 계층 2와 4에도 있다. 이들의 차잇점을 쓰시오. 2계층과 4계층의 차이점.2계층은 서로 연결된 두 노드간의 제어이며, 4계층은 단대단 제어이다.따라서 4계층의 오류제어와 흐름제어는 단말과 네트워크를 포함한 제어 기술이다.2계층의 오류 제어는 오류제어를 담당한다. 4계층의 오류제어는 송신측과 수신측 단대단 오류 제어이다.2계층의 흐름제어는 역시 인접노드간의 흐름 제어이고, 4계층의 흐름제어는 단대단 흐름제어이다. 관련된 글: 고속 네트워크 프로토콜의…

  • |

    TCP 폭주(혼잡)제어

    TCP의 폭주제어에서 예약방식을 못 쓰고 피드백 기반 방식을 사용하여야 하는 이유를 설명하시오. 예약 방식의 경우 데이터 전송 중 경로상의 라우터에서 폭주 혹은 에러가 발생했을 경우 송신자는 계속 데이터를 보내게 되어 혼잡이 가중된다. 이를 해결하기 위해 피드백 방식을 사용한다.송신자는 약간의 데이터만 전송하여 도착 여부를 확인 후 점점 전송량을 늘려간다. 이처럼 네트워크의 상태에 따라 송신측에서 전송량을 조절하여야 한다….

  • |

    고속 네트워크에서의 메모리 병목 현상

    메모리가 미래 통신에 있어서 병목 지점이 되는 이유를 설명하고 해결 방안을 제시하시오. 1) 메모리는 코드와 데이터를 함께 저장한다. 표현 계층이 개입한다면 메모리 연산이 증가하게 되어 병목 현상이 발생한다. -> 표현계층을 적게 사용한다.2) 각 계층 이동시 메모리 연산 증가. OSI-7-layer에서 각 계층 이동시 메모리 복사가 빈번하게 일어나게 된다. -> shared memory, page remapping, 계층 통합 처리3)…

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다