본문 바로가기

Developement/C/C++

[FLTK] fl_imgtk 0.3.26.x 에서 Aero glass 효과 내기.

이 글을 읽기 앞서 이해가 먼저 필요한 사항


 FLTK 는 여러 platform 에서 사용하기 좋은 GUI library 이나, FLTK 에서 이미지를 처리 하기 위해서는 뭐 아무것도 없는게 사실 입니다. 그래서 따로 fl_imgtk 란 것을 만들고, 이것을 통해 CPU 로만 여러 이미지 처리를 할 수 있도록 Open Source 로 제공 하고 있습니다. Public open 이므로 누구나 가져다 빌드 해 쓸 수 있으면 마음대로 쓸 수 있는 MIT License 를 적용하여 배포 중이나, 그리 유명세가 없다 보니 혼자 쓰는 단점이 생기는 것 같아 조금씩 이를 홍보해야 겠다는 생각이 들어 글을 쓰기 시작 했습니다.


 일단 fl_imgtk 의 경우 상업용으로 사용된 경우는, 얼마전 Essel-T 사의 Removu S1 Frimware Upgrade app Mac OS X 용을 native llvm 으로 빌드하여 올릴수 있도록 만든 것이 있습니다 (관련 영문 블로그 글).



위의 GIF 은 Mac OS X 가 아닌 Linux Mint 에서 개발 중 캡쳐 한 것이나, 사실 동일한 C++ code 로 target 만 바꿔 빌드 하면 될 수 있도록 만든지라 거의 같은 모습으로 구동 됩니다. 그리고 이 프로그램은 제가 만든 fl_imgtk 와 fltk-1.3.4-1-ts 를 이용하여 Mac OS X 에서 동일하게 구동 되도록 만들어 졌었습니다.


 그럼 이번에 설명 하고자 하는 fl_imgtk 의 Aero glass 효과란 무엇인가 ? 를 알아 보면 아래 이미지를 먼저 확인 해 볼 필요가 있습니다. (샘플 프로그램은 글의 제일 마지막에 추가 하겠습니다)



 하나의 윈도우 내에 이미지가 배경에 깔리고, 창 내 영역 상단 50px 부분이 검은색 반투명 유리 같은걸로 뿌옇게 보이는 것을 확인할 수 있습니다. 이는 Windows 의 DWM 을 통해 GPU blur 를 처리 하여 얻을수도 있겠지만, 다른 OS 에서 모두 동일한 효과를 얻기 위해서는 역시 같은 알고리즘으로 도는 뭔가로 만들어 내야 할 필요가 있었고, 이를 fl_imgtk 는 제공하고 있습니다.


 이를 사용하기 위해서는 fl_imgtk 0.3.25 이상의 버젼이 필요 하며, 이를 확인 하는 방법은 fl_imgtk.h 내에 상단에 있는 define 문을 확인하는 것이 좋습니다.


#ifndef __FL_IMGTOOLKIT_H__
#define __FL_IMGTOOLKIT_H__

⁄*******************************************************************************
* fl_imgtk.H , version 2017-10-13-1
* =============================================================================
* A tool kit for basic FLTK image processing.
* (C) 2016-2017 Raphael Kim, Raph.K. ( rageworx or rage.kim @gmail.com )
* All rights reserved for MIT license.
*
* [ Disclaimer ]
* - Some codes belong to FreeImage 3 library, and modified to FLTK and fl_imgtk.
* - It follows FreeImage license and open as free.
*******************************************************************************⁄

#include <FL⁄Fl.H>
#include <FL⁄Fl_Image.H>
#include <FL⁄Fl_RGB_Image.H>

⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄

#define FL_IMGTK

#define FL_IMGTK_VER_MJR    0
#define FL_IMGTK_VER_MNR    3
#define FL_IMGTK_VER_BLD    26
#define FL_IMGTK_VER_REV    1

#define FL_IMGTK_VERSION    ( FL_IMGTK_VER_MJR * 100000000 + \
                              FL_IMGTK_VER_MNR * 100000 + \
                              FL_IMGTK_VER_BLD * 1000 + \
                              FL_IMGTK_VER_REV )


 위 이미지에서 이미지의 일부를 Blur 한 형태로 얻기 위해서 fl_imgtk 는 여러 API 들을 제공 하고 있습니다만, 이번에 빌드 25 이상에 도로 추가된 함수가 다음과 같이 존재 합니다.


Fl_RGB_Image* draw_currentwindow( void* w = NULL );


 특정 Fl_Window 를 지시해서 해당 Window 의 off-screen buffer 를 Fl_RGB_Image 로 얻어 내거나, () 또는 NULL 지시로 현재 활성화 되어 있는 첫번째 Fl_Window 를 캡쳐 하는 기능을 하게 됩니다.

 그래서 다른 Fl_Window 나, Fl_Widget 의 ::draw() 함수내 또는 timer 로 호출되는 상태, 또는 Fl_Callback 으로 호출된 함수내에서 다음과 같은 형태로 약간의 멋을 부릴수 있게 됩니다.


void WMain::OnDrawCompleted()
{
    boxControl->hide();
    boxControl->deimage();

    if ( imgControlBG != NULL )
    {
        fl_imgtk::discard_user_rgb_image( imgControlBG );
    }

    uchar* widgetbuff = new uchar[ boxControl->w() * boxControl->h() * 3 + 1 ];
    if ( widgetbuff != NULL )
    {
        ⁄⁄ Read current window pixels to buffer for create a new Fl_RGB_Image.
        fl_read_image( widgetbuff, boxControl->x(), boxControl->y(),
                                   boxControl->w(), boxControl->h() );

        Fl_RGB_Image* imgcntl = new Fl_RGB_Image( widgetbuff,
                                                  boxControl->w(),
                                                  boxControl->h(),
                                                  3 );

        if ( imgcntl != NULL )
        {
            fl_imgtk::brightness_ex( imgcntl, -50 );
            fl_imgtk::draw_smooth_line( imgcntl,
                                        0, 0,
                                        boxControl->w(), 0,
                                        0xFFFFFFFF );
            fl_imgtk::draw_smooth_line( imgcntl,
                                        0, 2,
                                        boxControl->w(), 2,
                                        0xFFFFFFFF );
            fl_imgtk::draw_smooth_line( imgcntl,
                                        0, boxControl->h()-1,
                                        boxControl->w(), boxControl->h()-1,
                                        0xFFFFFFFF );
            fl_imgtk::blurredimage_ex( imgcntl, 8 );
            fl_imgtk::draw_smooth_line( imgcntl,
                                        0, boxControl->h()-1,
                                        boxControl->w(), boxControl->h()-1,
                                        0xFFFFFF1F );

            imgControlBG = imgcntl;


            boxControl->image( imgControlBG );
        }
        else
        {
            delete[] widgetbuff;
        }
    }

    boxControl->show();
}


 위 코드는 실제 예제로 있는 프로그램에서 사용된 코드로서, boxControl 이라는 Fl_Box 를 상속 받은 Widget 에서 ::draw() 함수 내에서 interface 로 등록한 void OnDrawCompleted(); 함수를 호출 될 때 구동 되도록 만들어 져 있습니다.

 이때 기존에 있는 draw_widgetimage() 를 호출 하지 않고 draw_currentwindow() 와 동일한 코드를 사용 한 것은, draw_widgetimage() 함수 내에서 draw() 함수를 Off-screen 용으로 다시 호출 하여, Call-stack 이 꼬이거나, 무한으로 재귀호출 되어 결국 프로그램이 강제 종료 되기 때문 입니다.

 또한 draw_currentwindow() 와 동일한 코드는 위에서 fl_read_image() 함수를 사용한 것이고, 이는 실제 draw_currentwindow() 함수와 구현된 동일한 형태를 비교 해 볼 수 있습니다.


Fl_RGB_Image* fl_imgtk::draw_currentwindow( void* w )
{
    Fl_Window* cwin = (Fl_Window*)w;
    
    if ( cwin == NULL )
    {
        cwin = Fl::first_window();
    }
    
    if ( cwin != NULL )
    {
        unsigned cwin_x = cwin->x();
        unsigned cwin_y = cwin->y();
        unsigned cwin_w = cwin->w();
        unsigned cwin_h = cwin->h();

        uchar* widgetbuff = new uchar[ cwin_w * cwin_h * 3 + 1 ];
        if ( widgetbuff != NULL )
        {
            ⁄⁄ Read current window pixels to buffer for create a new Fl_RGB_Image.
            fl_read_image( widgetbuff, cwin_x, cwin_y,
                                       cwin_w, cwin_h );

            Fl_RGB_Image* retimg =  new Fl_RGB_Image( widgetbuff,
                                                      cwin_w,
                                                      cwin_h,
                                                      3 );
                                                      
            if ( retimg == NULL )
            {
                delete[] widgetbuff;
            }
            
            return retimg;
        }
    }
    
    return NULL;
}


 fl_read_image() 를 통해 얻어진 버퍼를 통해 Fl_RGB_Image() 를 얻어 낸 다음엔, 하얀색 선을 긋고, blurred image 를 얻기 위해 간단히 fl_imgtk() 의 API 몇개를 사용하면 됩니다. 또한 fl_smoothline() 은 컬러값에 alpha channel 을 지원하므로 반투명한 선을 그을수 있기 때문에 위의 코드대로 밝기와 선 몇개를 버퍼에 그린다음 blurred 를 얻어 내면 ..



 검은 반투명 유리 상하단에 약간의 밝은 느낌과, 제일 하단에 경계선을 만들수 있게 됩니다. 이런 효과를 FLTK 로 만든 윈도우 내에 각 Widget 의 배경으로 채우도록 하면 다음에 첨부한 실행 파일 (Windows 64bit) 처럼 실시간으로 미려한 효과를 만들어 낼 수 있게 됩니다.


makeimgoblurred_win64_avx_omp.7z


 위 압축파일을 내려 받은 다음, 실행파일을 실행 하면 빈 윈도우 하나가 떠 있는데, 여기에 JPG 나 PNG 또는 BMP 를 끌어다 놓으면 이미지가 바로 표시 됩니다. 그리고 프로그램 윈도우 크기를 변경 하거나 하면 실시간으로 어두은 색의 반투명 aero glass 효과를 직접 확인 할 수 있습니다.

 단, 위 프로그램 파일은 Intel 또는 AMD 사의 AVX 명령을 가진 CPU 에서만 정상적으로 구동 할 수 있으므로, 혹시 실행이 안될 경우 AVX 명령어가 없는 CPU 에서 구동한 경우임을 먼저 인지 해 주시기 바랍니다.