
// RGB-HistogramDlg.cpp : 구현 파일
//

#include "stdafx.h"
#include "RGB-Histogram.h"
#include "RGB-HistogramDlg.h"
#include "afxdialogex.h"
#include <map>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 응용 프로그램 정보에 사용되는 CAboutDlg 대화 상자입니다.

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 대화 상자 데이터입니다.
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 지원입니다.

// 구현입니다.
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CRGBHistogramDlg 대화 상자



CRGBHistogramDlg::CRGBHistogramDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CRGBHistogramDlg::IDD, pParent),
	m_hBitMap(NULL)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	memset(m_Red, 0, sizeof(m_Red));
	memset(m_Green, 0, sizeof(m_Green));
	memset(m_Blue, 0, sizeof(m_Blue));
}

void CRGBHistogramDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CRGBHistogramDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CRGBHistogramDlg::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON2, &CRGBHistogramDlg::OnBnClickedButton2)
	ON_BN_CLICKED(IDC_RADIO1, &CRGBHistogramDlg::OnBnClickedRadio1)
	ON_BN_CLICKED(IDC_RADIO2, &CRGBHistogramDlg::OnBnClickedRadio2)
END_MESSAGE_MAP()


// CRGBHistogramDlg 메시지 처리기

BOOL CRGBHistogramDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 시스템 메뉴에 "정보..." 메뉴 항목을 추가합니다.

	// IDM_ABOUTBOX는 시스템 명령 범위에 있어야 합니다.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 이 대화 상자의 아이콘을 설정합니다. 응용 프로그램의 주 창이 대화 상자가 아닐 경우에는
	//  프레임워크가 이 작업을 자동으로 수행합니다.
	SetIcon(m_hIcon, TRUE);			// 큰 아이콘을 설정합니다.
	SetIcon(m_hIcon, FALSE);		// 작은 아이콘을 설정합니다.

	// 여기에 추가 초기화 작업을 추가합니다.
	((CButton*)GetDlgItem(IDC_RADIO2))->SetCheck(TRUE);

	//PrintHistogram();

	return TRUE;  // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.
}

void CRGBHistogramDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 대화 상자에 최소화 단추를 추가할 경우 아이콘을 그리려면
//  아래 코드가 필요합니다. 문서/뷰 모델을 사용하는 MFC 응용 프로그램의 경우에는
//  프레임워크에서 이 작업을 자동으로 수행합니다.

void CRGBHistogramDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다.

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 클라이언트 사각형에서 아이콘을 가운데에 맞춥니다.
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 아이콘을 그립니다.
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

// 사용자가 최소화된 창을 끄는 동안에 커서가 표시되도록 시스템에서
//  이 함수를 호출합니다.
HCURSOR CRGBHistogramDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CRGBHistogramDlg::InitData()
{
	::DeleteObject(m_hBitMap);
	memset(m_Red, 0, sizeof(m_Red));
	memset(m_Green, 0, sizeof(m_Green));
	memset(m_Blue, 0, sizeof(m_Blue));
}

void CRGBHistogramDlg::OnBnClickedButton1()
{
	CFileDialog dlg(TRUE, _T("*.*"), _T("*.bmp;*.png;*.jpg;*.gif"));
	int result = dlg.DoModal();
	if(result == IDOK)
	{
		InitData();
		
		CImage image;
		HRESULT hResult = image.Load(dlg.GetPathName());
		if (FAILED(hResult))
		{
			AfxMessageBox(_T("Failed to load image file"));
			return;
		}

		m_hBitMap = image.Detach();
		DrawThumbnail();
		PrintHistogram();
	}
}

void CRGBHistogramDlg::DrawGraph(int x, int y, unsigned int data[], const COLORREF rgb)
{
	CClientDC dc(this);
	unsigned int width = 257;
	unsigned int height = 110;

	unsigned int data_print[256] = {0,};

	// Draw Background
	CPen pen;
	pen.CreatePen(PS_SOLID, 1, RGB(150,150,150));
	CPen *oldPen = dc.SelectObject(&pen);
	dc.Rectangle(x, y, x + width, y + height);
	dc.SelectObject(oldPen);

	// Normalize
	unsigned int max_n = 0;
	for (int i = 0; i < 256; ++i)
		if (data[i] > max_n) max_n = data[i];
	double ratio = max_n ? (1.0 * (height - 1) / max_n) : 0.0;

	// Adjust Scale
	double adjust = 1.0;
	double min_threshold = 0.24; // 24%
	unsigned int min_threshold_cnt = (unsigned int)(height * width * min_threshold);
	unsigned int cnt = 0;
	
	while (1)
	{
		cnt = 0;
		for (int i = 0; i < 256; ++i)
			cnt += (unsigned int)((data[i] * adjust) * ratio);

		if (cnt == 0 || cnt > min_threshold_cnt)
			break;

		adjust += 0.1;
	}

	if (((CButton*)GetDlgItem(IDC_RADIO1))->GetCheck() == TRUE)
		adjust = 1.0;
	
	// Set data
	for (int i = 0; i < 256; ++i)
		data_print[i] = (unsigned int)((data[i] * adjust) * ratio);

	// Draw Graph
	CPen penData;
	penData.CreatePen(PS_SOLID, 1, rgb);
	oldPen = dc.SelectObject(&penData);

	for (int i = 0; i < 256; ++i)
	{
		if (data_print[i] ==  0) continue;

		dc.MoveTo(x + i, y + height - 2);
		dc.LineTo(x + i, y + height - (data_print[i] > height ? height : data_print[i]) - 1);
	}
	dc.SelectObject(oldPen);
}

void CRGBHistogramDlg::DrawThumbnail()
{
	CClientDC dc(this);
	HDC memDC = CreateCompatibleDC(dc.GetSafeHdc());

	HBITMAP hold_bm = (HBITMAP)SelectObject(memDC, m_hBitMap);
	BITMAP bm;
	GetObject(m_hBitMap, sizeof(BITMAP), &bm);

	RECT rect;
	GetDlgItem(IDC_STATIC_PIC1)->GetWindowRect(&rect);
	ScreenToClient(&rect);

	SetStretchBltMode(dc.GetSafeHdc(), HALFTONE);
	StretchBlt(dc.GetSafeHdc(), rect.left, rect.top, 200, 200, memDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);

	SelectObject(memDC, hold_bm);
	DeleteDC(memDC);
}

void CRGBHistogramDlg::PrintHistogram()
{
	CBitmap bitmap;
	if (m_hBitMap)
	{
		// Set Data
		bitmap.Attach(m_hBitMap);

		BITMAP bm_info;
		bitmap.GetBitmap(&bm_info);

		int bm_bps = bm_info.bmBitsPixel / 8;
		int bm_size = bm_info.bmWidth * bm_info.bmHeight * bm_bps;
		BYTE *p_bit_pattern = new BYTE[bm_size];
		bitmap.GetBitmapBits(bm_size, p_bit_pattern);

		for (int pos = 0; pos < bm_size; pos += bm_bps)
		{
			++m_Red[p_bit_pattern[2 + pos]];
			++m_Green[p_bit_pattern[1 + pos]];
			++m_Blue[p_bit_pattern[pos]];
		}

		free(p_bit_pattern);
	}

	// Red
	DrawGraph(20, 10, m_Red, RGB(255, 0, 0));

	// Green
	DrawGraph(20, 130, m_Green, RGB(0, 255, 0));

	// Blue
	DrawGraph(20, 250, m_Blue, RGB(0, 0, 255));
}

void CRGBHistogramDlg::Reload()
{
	// Red
	DrawGraph(20, 10, m_Red, RGB(255, 0, 0));

	// Green
	DrawGraph(20, 130, m_Green, RGB(0, 255, 0));

	// Blue
	DrawGraph(20, 250, m_Blue, RGB(0, 0, 255));
}

void CRGBHistogramDlg::OnBnClickedButton2()
{
	Reload();
}

void CRGBHistogramDlg::OnBnClickedRadio1()
{
	Reload();
}

void CRGBHistogramDlg::OnBnClickedRadio2()
{
	Reload();
}
