/**************************************************************

mv_image.cpp (C-Munipack project)
Display class for CCD images
Copyright (C) 2012 David Motl, dmotl@volny.cz

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <math.h>

#include "mv_image.h"
#include "properties_dlg.h"
#include "ctxhelp.h"

enum {
	CMD_PROPERTIES = 100,
	CMD_COPY,
	CMD_FIT_TO_WINDOW,
	CMD_ZOOM_IN,
	CMD_ZOOM_OUT,
};

#ifndef MAX
#define MAX(a, b) ((a)>(b) ? (a) : (b))
#endif

#ifndef MIN
#define MIN(a, b) ((a)<(b) ? (a) : (b))
#endif

static char *mv_strdup(const char *str)
{
	if (str) {
		size_t len = strlen(str);
		char *buf = (char*)malloc(len+1);
		if (buf)
			strcpy_s(buf, len+1, str);
		return buf;
	}
	return NULL;
}

inline static unsigned char grayscale(double x)
{
	x = 255.0*x;

	if (x<=0.0)
		return 0;
	if (x>=255.0)
		return 255;
	return (unsigned char)(x+0.5);
}

static void *FindDIBits(BITMAPINFO *info)
{
	return (char*)info + info->bmiHeader.biSize + info->bmiHeader.biClrUsed*sizeof(RGBQUAD);
}

// Constructor
TCCD_Image::TCCD_Image(void):m_pDIB(NULL), m_DIBSize(0), m_Title(NULL), m_ScrollX(0), m_ScrollY(0), 
	m_Invert(false), m_CenterImage(false), m_FitToWindow(false), m_FitLargerOnly(false), 
	m_ZoomMin(-5), m_ZoomMax(5), m_ZoomPos(0), m_JulianDate(0), m_FormatName(NULL), 
	m_ImageWidth(0), m_ImageHeight(0), m_Filter(NULL), m_Object(NULL), m_Observer(NULL),
	m_Exposure(0), m_CCDTemperature(-999.9), m_AvgFrames(0), m_SumFrames(0)
{
	m_ZoomBase = pow(100.0, 1.0/10.0);
	memset(&m_DateTime, 0, sizeof(CmpackDateTime));
	memset(&m_CanvasRc, 0, sizeof(RECT));
	HDC hdc = CreateCompatibleDC(0);
	m_hFont = CreateFont(-MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72), 
		0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Lucida Console");
	DeleteDC(hdc);
}

// Destructor
TCCD_Image::~TCCD_Image(void)
{
	if (m_pDIB)
		VirtualFree(m_pDIB, 0, MEM_RELEASE);
	DeleteObject(m_hFont);
	free(m_Title);
	free(m_FormatName);
	free(m_Filter);
	free(m_Object);
	free(m_Observer);
}

// Check if the buffer contains beginning of an image
bool TCCD_Image::Test(const char *buf, int bytes)
{
	return cmpack_ccd_test_buffer(buf, bytes, -1)!=0;
}

// Load image from a file
bool TCCD_Image::Load(const char *filename)
{
	bool res = false;
	CmpackCcdFile *file;
	CmpackCcdParams params;
	CmpackImage *image;

	if (m_pDIB) {
		VirtualFree(m_pDIB, 0, MEM_RELEASE);
		m_pDIB = NULL;
		m_DIBSize = 0;
	}
	if (cmpack_ccd_open(&file, filename, CMPACK_OPEN_READONLY, 0)==0) {
		if (cmpack_ccd_to_image(file, CMPACK_BITPIX_AUTO, &image)==0 &&
			cmpack_ccd_get_params(file, CMPACK_CM_FORMAT | CMPACK_CM_IMAGE | CMPACK_CM_JD | 
			CMPACK_CM_DATETIME | CMPACK_CM_EXPOSURE | CMPACK_CM_CCDTEMP | CMPACK_CM_FILTER | 
			CMPACK_CM_OBJECT | CMPACK_CM_OBSERVER | CMPACK_CM_SUBFRAMES, &params)==0) {
				SetFilePath(filename);
				UpdateParams(params);
				UpdateTitle();
				UpdateDIB(image);
				res = true;
		}
		cmpack_ccd_close(file);
		cmpack_image_destroy(image);
	}
	
	return res;
}

void TCCD_Image::SetShowFlags(int ShowFlags)
{
	m_FitToWindow = (ShowFlags & lcp_fittowindow)!=0;
	m_FitLargerOnly = (ShowFlags & lcp_fitlargeronly)!=0;
	m_CenterImage = (ShowFlags & lcp_center)!=0;
	OnSizeChanged();
	Invalidate();
}

// Update frame properties
void TCCD_Image::UpdateParams(const CmpackCcdParams &params)
{
	m_JulianDate = params.jd;
	m_DateTime = params.date_time;
	free(m_FormatName);
	m_FormatName = mv_strdup(params.format_name);
	m_ImageWidth = params.image_width;
	m_ImageHeight = params.image_height;
	free(m_Filter);
	m_Filter = mv_strdup(params.filter);
	free(m_Object);
	m_Object = mv_strdup(params.object.designation);
	free(m_Observer);
	m_Observer = mv_strdup(params.observer);
	m_Exposure = params.exposure;
	m_CCDTemperature = params.ccdtemp;
	m_AvgFrames = params.subframes_avg;
	m_SumFrames = params.subframes_sum;
}

// Update title
void TCCD_Image::UpdateTitle(void)
{
	char buf[512], aux[64];
	
	sprintf_s(aux, "%04d-%02d-%02d %d:%02d:%02d", m_DateTime.date.year, m_DateTime.date.month, 
		m_DateTime.date.day, m_DateTime.time.hour, m_DateTime.time.minute, m_DateTime.time.second);
	sprintf_s(buf, "%s | %dx%d | %s | Filter: %s", (m_FormatName ? m_FormatName : ""), 
		m_ImageWidth, m_ImageHeight, aux, (m_Filter ? m_Filter : ""));
	SetTitle(buf);
}

// Initialize control
void TCCD_Image::UpdateDIB(CmpackImage *img)
{
	int width, height, rowwidth, bitcount;
	double black, white, scale, offset;

	if (m_FormatName==0 || m_ImageWidth<=0 || m_ImageHeight<=0)
		return;

	cmpack_image_autogb(img, 0, 65535, &black, &white);
	if (white>black) {
		if (!m_Invert) {
			scale = 1.0/(white-black);
			offset = -black/(white-black);
		} else {
			scale = -1.0/(white-black);
			offset = 1.0+black/(white-black);
		}
	} else {
		if (!m_Invert) {
			scale = 1.0;
			offset = -white;
		} else {
			scale = -1.0;
			offset = 1.0+white;
		}
	}

	width = m_ImageWidth;
	height = m_ImageHeight;
	if (!m_pDIB || m_pDIB->bmiHeader.biWidth!=width || m_pDIB->bmiHeader.biHeight) {
		// Release old DIB
		if (m_pDIB)
			VirtualFree(m_pDIB, 0, MEM_RELEASE);
		// Allocate new DIB
		m_DIBSize = sizeof(BITMAPINFOHEADER);
		bitcount = 8;
		m_DIBSize += ((size_t)1<<bitcount)*sizeof(RGBQUAD);
		rowwidth = ((width*(bitcount >> 3)+3)/4)*4;
		m_DIBSize += (size_t)rowwidth*height;
		m_pDIB = (BITMAPINFO*)VirtualAlloc(0, m_DIBSize, MEM_COMMIT, PAGE_READWRITE);
		if (!m_pDIB)
			return;
		// Fill bitmap header
		memset(m_pDIB, 0, sizeof(BITMAPINFOHEADER) + ((size_t)1<<bitcount)*sizeof(RGBQUAD));
		m_pDIB->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		m_pDIB->bmiHeader.biWidth = width;
		m_pDIB->bmiHeader.biHeight = height;
		m_pDIB->bmiHeader.biPlanes = 1;
		m_pDIB->bmiHeader.biBitCount = bitcount;
		m_pDIB->bmiHeader.biClrUsed = 256;
		m_pDIB->bmiHeader.biClrImportant = 256;
		for (int i=0; i<256; i++) {
			m_pDIB->bmiColors[i].rgbBlue=i;
			m_pDIB->bmiColors[i].rgbGreen=i;
			m_pDIB->bmiColors[i].rgbRed=i;
		}
	}
		
	uint8_t *bits = (uint8_t*)(((char*)m_pDIB)+m_pDIB->bmiHeader.biSize+m_pDIB->bmiHeader.biClrUsed*sizeof(RGBQUAD));
	rowwidth = ((m_pDIB->bmiHeader.biWidth*(m_pDIB->bmiHeader.biBitCount >> 3)+3)/4)*4;
	switch (cmpack_image_bitpix(img))
	{
	case CMPACK_BITPIX_SSHORT:
		{
			int16_t *sptr = (int16_t*)cmpack_image_data(img);
			for (int y=0; y<height; y++) {
				uint8_t *dptr = bits + (height-y-1)*rowwidth;
				for (int x=0; x<width; x++) 
					*dptr++ = grayscale(scale*(*sptr++) + offset);
			}
		}
		break;
	case CMPACK_BITPIX_USHORT:
		{
			uint16_t *sptr = (uint16_t*)cmpack_image_data(img);
			for (int y=0; y<height; y++) {
				uint8_t *dptr = bits + (height-y-1)*rowwidth;
				for (int x=0; x<width; x++) 
					*dptr++ = grayscale(scale*(*sptr++) + offset);
			}
		}
		break;
	case CMPACK_BITPIX_SLONG:
		{
			int32_t *sptr = (int32_t*)cmpack_image_data(img);
			for (int y=0; y<height; y++) {
				uint8_t *dptr = bits + (height-y-1)*rowwidth;
				for (int x=0; x<width; x++) 
					*dptr++ = grayscale(scale*(*sptr++) + offset);
			}
		}
		break;
	case CMPACK_BITPIX_ULONG:
		{
			uint32_t *sptr = (uint32_t*)cmpack_image_data(img);
			for (int y=0; y<height; y++) {
				uint8_t *dptr = bits + (height-y-1)*rowwidth;
				for (int x=0; x<width; x++) 
					*dptr++ = grayscale(scale*(*sptr++) + offset);
			}
		}
		break;
	case CMPACK_BITPIX_FLOAT:
		{
			float *sptr = (float*)cmpack_image_data(img);
			for (int y=0; y<height; y++) {
				uint8_t *dptr = bits + (height-y-1)*rowwidth;
				for (int x=0; x<width; x++) 
					*dptr++ = grayscale(scale*(*sptr++) + offset);
			}
		}
		break;
	case CMPACK_BITPIX_DOUBLE:
		{
			double *sptr = (double*)cmpack_image_data(img);
			for (int y=0; y<height; y++) {
				uint8_t *dptr = bits + (height-y-1)*rowwidth;
				for (int x=0; x<width; x++) 
					*dptr++ = grayscale(scale*(*sptr++) + offset);
			}
		}
		break;

	default:
		break;
	}
}
	
void TCCD_Image::OnRender(HDC hDC)
{
	int		img_width, img_height;
	int		cvs_left, cvs_top, cvs_width, cvs_height;
	int		dst_left, dst_top, dst_width, dst_height;
	int		src_left, src_top, src_width, src_height;
	double	zoom, kx, ky;
	RECT	rc;

	img_width = m_ImageWidth;
	img_height = m_ImageHeight;
	if (img_width<=0 || img_height<=0)
		return;

	// Fit image to client rectangle
	cvs_left = m_CanvasRc.left;
	cvs_top = m_CanvasRc.top;
	cvs_width = m_CanvasRc.right-m_CanvasRc.left;
	cvs_height = m_CanvasRc.bottom-m_CanvasRc.top;
	if (m_FitToWindow && cvs_width>0 && cvs_height>0) {
		kx = (double)cvs_width / img_width;
		ky = (double)cvs_height / img_height;
		zoom = MIN(kx, ky);
		if (m_FitLargerOnly && zoom>1.0)
			zoom = 1.0;
		src_left = src_top = 0;
		src_width = img_width;
		src_height = img_height;
		dst_width = (int)(zoom*img_width);
		dst_height = (int)(zoom*img_height);
		dst_left = cvs_left + (m_CenterImage && (cvs_width>dst_width) ? (cvs_width - dst_width)/2 : 0);
		dst_top = cvs_top + (m_CenterImage && (cvs_height>dst_height) ? (cvs_height - dst_height)/2 : 0);
	} else {
		zoom = pow(m_ZoomBase, m_ZoomPos);
		src_left = src_top = 0;
		src_width = img_width;
		src_height = img_height;
		dst_left = cvs_left - m_ScrollX;
		dst_top = cvs_top - m_ScrollY;
		dst_width = RoundToInt(zoom*img_width);
		dst_height = RoundToInt(zoom*img_height);
	}

	HBRUSH bg = GetSysColorBrush(COLOR_WINDOW);
	if (dst_top>0) {
		SetRect(&rc, 0, 0, dst_left+dst_width, dst_top);
		FillRect(hDC, &rc, bg);
	} 
	if (dst_left>0) {
		SetRect(&rc, 0, dst_top, dst_left, m_CanvasRc.bottom);
		FillRect(hDC, &rc, bg);
	}
	if (dst_left+dst_width < m_CanvasRc.right) {
		SetRect(&rc, dst_left+dst_width, 0, m_CanvasRc.right, dst_top+dst_height);
		FillRect(hDC, &rc, bg);
	}
	if (dst_top+dst_height < m_CanvasRc.bottom) {
		SetRect(&rc, dst_left, dst_top+dst_height, m_CanvasRc.right, m_CanvasRc.bottom);
		FillRect(hDC, &rc, bg);
	}

	if (src_width>0 && src_height>0 && dst_width>0 && dst_height>0) {
		SetStretchBltMode(hDC, HALFTONE);
		SetBrushOrgEx(hDC, 0, 0, 0);
		StretchDIBits(hDC, dst_left, dst_top, dst_width, dst_height, src_left, src_top, 
			src_width, src_height, FindDIBits(m_pDIB), m_pDIB, DIB_RGB_COLORS, SRCCOPY);
	}

	// Draw title
	if (m_Title) {
		HFONT oldfont = (HFONT)SelectObject(hDC, m_hFont);
		HBRUSH oldbrush = (HBRUSH)SelectObject(hDC, GetSysColorBrush(COLOR_WINDOW));
		SetBkMode(hDC, OPAQUE);
		SetRect(&rc, 0, 0, cvs_left+cvs_width, cvs_top);
		FillRect(hDC, &rc, GetSysColorBrush(COLOR_WINDOW));
		TextOut(hDC, 4, 2, m_Title, (int)strlen(m_Title));
		SelectObject(hDC, oldfont);
		SelectObject(hDC, oldbrush);
	}
}

// Makes preview and returns the HBITMAP
HBITMAP TCCD_Image::MakePreview(int max_width, int max_height)
{
	int w, h;

	if (!m_pDIB)
		return NULL;

	int img_width = m_pDIB->bmiHeader.biWidth, img_height = m_pDIB->bmiHeader.biHeight;
	if (img_width<=0 || img_height<=0)
		return NULL;

	// Compute thumbnail size
	if (img_width>max_width || img_height>max_height) {
		int stretchy = MulDiv(max_width, img_height, img_width);
		if (stretchy <= max_height) {
			w = max_width;
			h = MAX(1, stretchy);
		} else {
			int stretchx = MulDiv(max_height, img_width, img_height);
			w = MAX(1, stretchx);
			h = max_height;
		}
	} else {
		w = img_width;
		h = img_height;
	}
	
	HDC dc_main = GetDC(GetDesktopWindow());
	HDC dc_thumbnail = CreateCompatibleDC(dc_main);
	HBITMAP bmp_thumbnail = CreateCompatibleBitmap(dc_main, w, h);
	ReleaseDC(GetDesktopWindow(), dc_main);
	HBITMAP old_bitmap = (HBITMAP)SelectObject(dc_thumbnail, bmp_thumbnail);
	if (w!=img_width || h!=img_height) {
		SetStretchBltMode(dc_thumbnail, HALFTONE);
		SetBrushOrgEx(dc_thumbnail, 0, 0, NULL);
		StretchDIBits(dc_thumbnail, 0, 0, w, h, 0, 0, img_width, img_height, FindDIBits(m_pDIB), 
			m_pDIB, DIB_RGB_COLORS, SRCCOPY);
	} else {
		SetDIBitsToDevice(dc_thumbnail, 0, 0, w, h, 0, 0, 0, img_height, FindDIBits(m_pDIB), 
			m_pDIB, DIB_RGB_COLORS);
	}
	SelectObject(dc_thumbnail, old_bitmap);
	DeleteDC(dc_thumbnail);
	return bmp_thumbnail;
}

void TCCD_Image::OnSizeChanged(void)
{
	int			sb_width, sb_height;
    int			img_width, img_height;
	double		kx, ky, zoom;
	RECT	    rc2;

	if (!m_hWnd)
		return;

	GetWindowRect(m_hWnd, &rc2);
	ScreenToClient(m_hWnd, (POINT*)(&rc2.right));
	rc2.left = rc2.top = 0;

	sb_height	= GetSystemMetrics(SM_CYHSCROLL);
	sb_width	= GetSystemMetrics(SM_CXVSCROLL);

	HDC hDC = CreateCompatibleDC(0);
	HFONT oldfont = (HFONT)SelectObject(hDC, m_hFont);
	if (m_Title) 
		rc2.top += text_height(hDC, m_Title) + 4;
	SelectObject(hDC, oldfont);
	DeleteDC(hDC);

	// Reserve space for scroll bars
	if (m_ImageWidth>0 && m_ImageHeight>0) {
		if (m_FitToWindow) {
			kx = (double)(rc2.right-rc2.left) / m_ImageWidth;
			ky = (double)(rc2.bottom-rc2.top) / m_ImageHeight;
			zoom = MIN(kx, ky);
			if (m_FitLargerOnly && zoom>1.0)
				zoom = 1.0;
			m_ZoomPos = log(zoom) / log(m_ZoomBase);
			m_CanvasRc = rc2;
			UpdateHSB(0, 0, 0, 0);
			UpdateVSB(0, 0, 0, 0);
		} else {
			double zoom = pow(m_ZoomBase, m_ZoomPos);
			img_width  = RoundToInt(zoom * m_ImageWidth);
			img_height = RoundToInt(zoom * m_ImageHeight);

			if ((rc2.right-rc2.left)<img_width) {
				rc2.bottom -= sb_height;
				if ((rc2.bottom-rc2.top)<img_height)
					rc2.right -= sb_width;
			} else {
				if ((rc2.bottom-rc2.top)<img_height) {
					rc2.right -= sb_width;
					if ((rc2.right-rc2.left)<img_width)
						rc2.bottom -= sb_height;
				}
			}
			m_CanvasRc = rc2;

			int cvs_w = MAX(0, m_CanvasRc.right - m_CanvasRc.left);
			if (img_width>cvs_w)
				m_ScrollX = LimitValue(m_ScrollX, 0, img_width - cvs_w);
			else
				m_ScrollX = 0;
			UpdateHSB(0, img_width - 1, cvs_w, m_ScrollX);

			int cvs_h = MAX(0, m_CanvasRc.bottom - m_CanvasRc.top);
			if (img_height>cvs_h)
				m_ScrollY = LimitValue(m_ScrollY, 0, img_height - cvs_h);
			else
				m_ScrollY = 0;
			UpdateVSB(0, img_height - 1, cvs_h, m_ScrollY);
		}
	}
}

// HSB changed
void TCCD_Image::OnHSBChanged(int nPos)
{
	m_ScrollX = nPos;
	Invalidate();
}
		
// VSB changed
void TCCD_Image::OnVSBChanged(int nPos)
{
	m_ScrollY = nPos;
	Invalidate();
}

// Keyboard event handler
void TCCD_Image::OnKeyDown(int vKey)
{
	switch (vKey) 
	{
	case VK_ADD:
		ZoomIn();
		break;
	case VK_SUBTRACT:
		ZoomOut();
		break;
	default:
		TCCD_Interface::OnKeyDown(vKey);
		break;
	}
}

// Zoom in
void TCCD_Image::ZoomIn(void)
{
	m_ZoomPos = MIN(m_ZoomPos+1, m_ZoomMax);
	m_FitToWindow = false;
	OnSizeChanged();
	SendMessage(GetParent(m_hWnd), WM_COMMAND, MAKELONG(0, itm_fit), (LPARAM)m_hWnd);
	Invalidate();
}

// Zoom out
void TCCD_Image::ZoomOut(void)
{
	m_ZoomPos = MAX(m_ZoomPos-1, m_ZoomMin);
	m_FitToWindow = false;
	OnSizeChanged();
	SendMessage(GetParent(m_hWnd), WM_COMMAND, MAKELONG(0, itm_fit), (LPARAM)m_hWnd);
	Invalidate();
}

// Fit image to window
void TCCD_Image::FitToWindow(void)
{
	m_FitToWindow = true;
	OnSizeChanged();
	m_FitToWindow = false;
	SendMessage(GetParent(m_hWnd), WM_COMMAND, MAKELONG(0, itm_fit), (LPARAM)m_hWnd);
	Invalidate();
}

// Set title
void TCCD_Image::SetTitle(const char *caption)
{
	free(m_Title);
	m_Title = StrDup(caption);
	OnSizeChanged();
	Invalidate();
}

void TCCD_Image::text_extents(HDC hDC, const char *buf, SIZE *size)
{
	if (buf) 
		GetTextExtentPoint(hDC, buf, (int)strlen(buf), size);
	else
		size->cx = size->cy = 0;
}

int TCCD_Image::text_height(HDC hDC, const char *buf)
{
	SIZE size;
	text_extents(hDC, buf, &size);
	return size.cy;
}

// Create base menu
HMENU TCCD_Image::CreateCtxMenu(void)
{
	HMENU hMenu = TCCD_Interface::CreateCtxMenu(), hPopup = GetSubMenu(hMenu, 0);
	InsertMenu(hPopup, 0, MF_BYPOSITION | MF_STRING, CMD_PROPERTIES, "Properties...");
	InsertMenu(hPopup, 1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
	InsertMenu(hPopup, 2, MF_BYPOSITION | MF_STRING, CMD_FIT_TO_WINDOW, "Fit to window");
	InsertMenu(hPopup, 3, MF_BYPOSITION | MF_STRING, CMD_ZOOM_IN, "Zoom in");
	InsertMenu(hPopup, 4, MF_BYPOSITION | MF_STRING, CMD_ZOOM_OUT, "Zoom out");
	InsertMenu(hPopup, 5, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
	InsertMenu(hPopup, 6, MF_BYPOSITION | MF_STRING, CMD_COPY, "Copy to clipboard");
	InsertMenu(hPopup, 7, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
	return hMenu;
}

// Context menu commands
void TCCD_Image::OnMenuCommand(int iCmd)
{
	switch (iCmd)
	{
	case CMD_PROPERTIES:
		ShowProperties();
		break;
	case CMD_COPY:
		CopyToClipboard();
		break;
	case CMD_FIT_TO_WINDOW:
		FitToWindow();
		break;
	case CMD_ZOOM_IN:
		ZoomIn();
		break;
	case CMD_ZOOM_OUT:
		ZoomOut();
		break;
	default:
		TCCD_Interface::OnMenuCommand(iCmd);
	}
}

// Context menu commands
void TCCD_Image::ShowProperties(void)
{
	CImageProperties dlg(this);
	dlg.ShowModal(m_hWnd);
}

// Copy image to clipboard
void TCCD_Image::CopyToClipboard(void)
{
	if (OpenClipboard(GetParent(m_hWnd))) {
		EmptyClipboard();
		if (m_pDIB) {
			HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, m_DIBSize);
			void *buf = GlobalLock(hMem);
			if (buf)
				memcpy(buf, m_pDIB, m_DIBSize);
			GlobalUnlock(hMem);
			SetClipboardData(CF_DIB, hMem);
		}
		CloseClipboard();
	}
}

// Get topic id (help page opened)
int TCCD_Image::GetTopicId(void)
{
	return IDH_IMAGE_VIEW;
}
