This video builds on the previous tutorials by adding functionality to our C#/C++ Forms app. We will discuss: Why we are using Windows Forms; using Windows Messages and how our app subscribes to user actions to trigger events; Implementing libraries; and how to configure Visual Studio for OpenCV.


Form1.cs

public Form1()
{
	InitializeComponent();
	fitPictureBox1();
	Cyprus.init(pictureBox1.Width, pictureBox1.Height);
	updateImage();
}
private void fitPictureBox1()
{
	//images can only have a width with interger mulitples of 4.
	int hPadding = 6;
	double _picWidth = Math.Floor((panel1.Width - hPadding) / (double)4) * 4;
	pictureBox1.Width = (int)_picWidth;
	//show user changes
	status.Text = "Panel Size: " + panel1.Width + " x " + panel1.Height;
	status.Text += ", bitMap size: " + _picWidth;
	status.Text += ", width Difference " + (panel1.Width - pictureBox1.Width - hPadding);
}
private void groupBox1_Paint(object sender, PaintEventArgs e)
{
	GroupBox box = sender as GroupBox;
	DrawGroupBox(box, e.Graphics, Color.Lime, Color.Lime);
}
private void DrawGroupBox(GroupBox box, Graphics g, Color textColor, Color borderColor)
{
	if (box != null)
	{
		Brush textBrush = new SolidBrush(textColor);
		Brush borderBrush = new SolidBrush(borderColor);
		Pen borderPen = new Pen(borderBrush);
		SizeF strSize = g.MeasureString(box.Text, box.Font);
		Rectangle rect = new Rectangle(box.ClientRectangle.X,
									   box.ClientRectangle.Y + (int)(strSize.Height / 2),
									   box.ClientRectangle.Width - 1,
									   box.ClientRectangle.Height - (int)(strSize.Height / 2) - 1);

		// Clear text and border
		g.Clear(this.BackColor);

		// Draw text
		g.DrawString(box.Text, box.Font, textBrush, box.Padding.Left, 0);

		// Drawing Border
		//Left
		g.DrawLine(borderPen, rect.Location, new Point(rect.X, rect.Y + rect.Height));
		//Right
		g.DrawLine(borderPen, new Point(rect.X + rect.Width, rect.Y), new Point(rect.X + rect.Width, rect.Y + rect.Height));
		//Bottom
		g.DrawLine(borderPen, new Point(rect.X, rect.Y + rect.Height), new Point(rect.X + rect.Width, rect.Y + rect.Height));
		//Top1
		g.DrawLine(borderPen, new Point(rect.X, rect.Y), new Point(rect.X + box.Padding.Left, rect.Y));
		//Top2
		g.DrawLine(borderPen, new Point(rect.X + box.Padding.Left + (int)(strSize.Width), rect.Y), new Point(rect.X + rect.Width, rect.Y));
	}
}
private void panel1_Resize(object sender, EventArgs e)
{
	if ((pictureBox1.Width > 0) && (pictureBox1.Height > 0))
	{
		fitPictureBox1();
		Cyprus.updateScreenSize(pictureBox1.Width, pictureBox1.Height);
		updateImage();
	}
}
private void updateImage()
{
	IntPtr _imgPtr = Cyprus.getScreenPtr();
	Bitmap bm = new Bitmap(
		pictureBox1.Width,
		pictureBox1.Height,
		pictureBox1.Width * 3, //Stride = 24 bit = 3 bytes per pixel
		System.Drawing.Imaging.PixelFormat.Format24bppRgb,
		_imgPtr);
	pictureBox1.Image = bm;
	pictureBox1.Refresh();
}


Cyprus.cs

class Cyprus
{
	[DllImport("CyprusDll", CallingConvention = CallingConvention.Cdecl)]
	public static extern void init(int _screenWidth, int _screenHeight);

	[DllImport("ChimeraCpp", CallingConvention = CallingConvention.Cdecl)]
	public static extern void release();

	[DllImport("CyprusDll", CallingConvention = CallingConvention.Cdecl)]
	public static extern void updateScreenSize(int _width, int _height);

	[DllImport("CyprusDll", CallingConvention = CallingConvention.Cdecl)]
	public static extern IntPtr getScreenPtr();
}


CyprusDll.cpp

#include "stdafx.h"
#include <iostream>
#include "alx_system_csDisplay.h"

alx::system::csDisplay display;

extern "C" __declspec(dllexport) void __cdecl init(int width, int height) {
	//load spash screen images
	display.setBackground("bkgImage.jpg");
	display.setData("sm.png");
	display.setSize(width, height);
	display.render();
}
extern "C" __declspec(dllexport) void __cdecl release() {}
extern "C" __declspec(dllexport) void __cdecl updateScreenSize(int width, int height) {
	display.setSize(width, height);
	display.render();
}
extern "C" __declspec(dllexport) unsigned char*  __cdecl getScreenPtr() {
	return display.getImagePtr();
}


alx_system_csDisplay.h

#pragma once
#include "opencv/cv.hpp"

namespace alx {
	namespace system {
		class csDisplay
		{
			int m_height;
			int m_width;
			cv::Mat m_background;		//background image for screen
			cv::Mat m_data;				//data to show, full size
			cv::Mat m_rendered;			//rendered image that matches screen size
		public:
			csDisplay();
			~csDisplay();
			void setSize(int width, int height);
			void setBackground(char* filePath);
			void setBackground(const char* filePath);
			void setBackground(unsigned char* dataPtr, int width, int height);
			void setData(char* filePath);
			void setData(const char* filePath);
			void setData(unsigned char* dataPtr, int width, int height);
			void render();
			unsigned char* getImagePtr();
		};
	}
}


alx_system_csDisplay.cpp

#include "stdafx.h"
#include <iostream>
#include "alx_system_csDisplay.h"


namespace alx {
	namespace system {
		csDisplay::csDisplay()
		{
			//load default images
			m_background = cv::Mat(100, 100, CV_8UC3, cv::Scalar(0, 25, 0)); //BGR
			m_data = cv::Mat(100, 100, CV_8UC3, cv::Scalar(0, 51, 0)); //BGR
		}
		csDisplay::~csDisplay() {}
		void csDisplay::setSize(int width, int height) {
			m_width = width;
			m_height = height;
			m_rendered = cv::Mat(m_height, m_width, CV_8UC3);
			render();
		}
		void csDisplay::setBackground(char* filePath) {
			try {
				m_background = cv::imread(filePath, CV_LOAD_IMAGE_COLOR);
				render();
			}
			catch (...) {
				std::cout << "ERROR: loading background image..." << std::endl;
			}
		}
		void csDisplay::setBackground(const char* filePath) {
			try {
				m_background = cv::imread(filePath, CV_LOAD_IMAGE_COLOR);
				render();
			}
			catch (...) {
				std::cout << "ERROR: loading background image..." << std::endl;
			}
		}
		void csDisplay::setBackground(unsigned char* dataPtr, int width, int height) {
			//creates a Mat header for existing data
			try {
				m_background = cv::Mat(height, width, CV_8UC3, dataPtr);
				render();
			}
			catch (...) {
				std::cout << "ERROR: creating header for image..." << std::endl;
			}
		}
		void csDisplay::setData(char* filePath) {
			try {
				m_data = cv::imread(filePath, CV_LOAD_IMAGE_COLOR);
				render();
			}
			catch (...) {
				std::cout << "ERROR: loading image..." << std::endl;
			}
		}
		void csDisplay::setData(const char* filePath) {
			try {
				m_data = cv::imread(filePath, CV_LOAD_IMAGE_COLOR);
				render();
			}
			catch (...) {
				std::cout << "ERROR: loading image..." << std::endl;
			}
		}
		void csDisplay::setData(unsigned char* dataPtr, int width, int height) {
			//creates a Mat header for existing data
			try {
				m_data = cv::Mat(height, width, CV_8UC3, dataPtr);
				render();

			}
			catch (...) {
				std::cout << "ERROR: creating header for image..." << std::endl;
			}
		}
		void csDisplay::render() {
			//Strategy:
			//	-1. m_rendered should already be the same size as the display
			//	-1. m_data is updated elsewhere so it is most current
			//	 1. place background into m_rendered
			//	 2. resize screenTile to fit into rendered
			//   3. copy screenTile into m_rendered (using ROI)
			double _aspectRatioData = m_data.cols / ((double)m_data.rows);
			double _aspectRatioScreen = m_rendered.cols / ((double)m_rendered.rows);
			int _tileWidth = 0;
			int _tileHeight = 0;
			int _tileOffsetX = 0;
			int _tileOffsetY = 0;
			if (_aspectRatioScreen >= _aspectRatioData) {
				//set heights equal, scale width
				_tileWidth = (int)(m_height * _aspectRatioData);
				_tileHeight = m_height;
				_tileOffsetX = (m_width - _tileWidth) / 2;
			}
			else {
				_tileWidth = m_width;
				_tileHeight = (int)(m_width / _aspectRatioData);
				_tileOffsetY = (m_height - _tileHeight) / 2;
			}
			cv::Mat _tile = cv::Mat(_tileHeight, _tileWidth, CV_8UC3);
			cv::resize(m_background, m_rendered, m_rendered.size(), 0, 0, cv::INTER_LINEAR);
			cv::resize(m_data, _tile, _tile.size(), 0, 0, cv::INTER_LINEAR);
			_tile.copyTo(
				m_rendered(
					cv::Rect(
						_tileOffsetX,
						_tileOffsetY,
						_tile.cols,
						_tile.rows
					)
				)
			);
		}
		unsigned char* csDisplay::getImagePtr() {
			return m_rendered.data;
		}

	}
}