Path: blob/master/modules/highgui/src/window_winrt_bridge.cpp
16337 views
// highgui to XAML bridge for OpenCV12// Copyright (c) Microsoft Open Technologies, Inc.3// All rights reserved.4//5// (3 - clause BSD License)6//7// Redistribution and use in source and binary forms, with or without modification, are permitted provided that8// the following conditions are met:9//10// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the11// following disclaimer.12// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the13// following disclaimer in the documentation and/or other materials provided with the distribution.14// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or15// promote products derived from this software without specific prior written permission.16//17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED18// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A19// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY20// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING23// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE24// POSSIBILITY OF SUCH DAMAGE.2526#include "precomp.hpp"2728#include "opencv2\highgui\highgui_winrt.hpp"29#include "window_winrt_bridge.hpp"3031#include <collection.h>32#include <Robuffer.h> // Windows::Storage::Streams::IBufferByteAccess3334using namespace Microsoft::WRL; // ComPtr35using namespace Windows::Storage::Streams; // IBuffer36using namespace Windows::UI::Xaml;37using namespace Windows::UI::Xaml::Controls;38using namespace Windows::UI::Xaml::Media::Imaging;3940using namespace ::std;4142/***************************** Constants ****************************************/4344// Default markup for the container content allowing for proper components placement45const Platform::String^ CvWindow::markupContent =46"<Page \n" \47" xmlns = \"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n" \48" xmlns:x = \"http://schemas.microsoft.com/winfx/2006/xaml\" >\n" \49" <StackPanel Name=\"Container\" Orientation=\"Vertical\" Width=\"Auto\" Height=\"Auto\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" Visibility=\"Visible\">\n" \50" <Image Name=\"cvImage\" Width=\"Auto\" Height=\"Auto\" Margin=\"10\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" Visibility=\"Visible\"/>\n" \51" <StackPanel Name=\"cvTrackbar\" Height=\"Auto\" Width=\"Auto\" Orientation=\"Vertical\" Visibility=\"Visible\"/>\n" \52" <StackPanel Name=\"cvButton\" Height=\"Auto\" Width=\"Auto\" Orientation=\"Horizontal\" Visibility=\"Visible\"/>\n" \53" </StackPanel>\n" \54"</Page>";5556const double CvWindow::sliderDefaultWidth = 100;5758/***************************** HighguiBridge class ******************************/5960HighguiBridge& HighguiBridge::getInstance()61{62static HighguiBridge instance;63return instance;64}6566void HighguiBridge::setContainer(Windows::UI::Xaml::Controls::Panel^ container)67{68this->container = container;69}7071CvWindow* HighguiBridge::findWindowByName(cv::String name)72{73auto search = windowsMap->find(name);74if (search != windowsMap->end()) {75return search->second;76}7778return nullptr;79}8081CvTrackbar* HighguiBridge::findTrackbarByName(cv::String trackbar_name, cv::String window_name)82{83CvWindow* window = findWindowByName(window_name);8485if (window)86return window->findTrackbarByName(trackbar_name);8788return nullptr;89}9091Platform::String^ HighguiBridge::convertString(cv::String name)92{93auto data = name.c_str();94int bufferSize = MultiByteToWideChar(CP_UTF8, 0, data, -1, nullptr, 0);95auto wide = std::make_unique<wchar_t[]>(bufferSize);96if (0 == MultiByteToWideChar(CP_UTF8, 0, data, -1, wide.get(), bufferSize))97return nullptr;9899std::wstring* stdStr = new std::wstring(wide.get());100return ref new Platform::String(stdStr->c_str());101}102103void HighguiBridge::cleanContainer()104{105container->Children->Clear();106}107108void HighguiBridge::showWindow(CvWindow* window)109{110currentWindow = window;111cleanContainer();112HighguiBridge::getInstance().container->Children->Append(window->getPage());113}114115CvWindow* HighguiBridge::namedWindow(cv::String name) {116117CvWindow* window = HighguiBridge::getInstance().findWindowByName(name.c_str());118if (!window)119{120window = createWindow(name);121}122123return window;124}125126void HighguiBridge::destroyWindow(cv::String name)127{128auto window = windowsMap->find(name);129if (window != windowsMap->end())130{131// Check if deleted window is the one currently displayed132// and clear container if this is the case133if (window->second == currentWindow)134{135cleanContainer();136}137138windowsMap->erase(window);139}140}141142void HighguiBridge::destroyAllWindows()143{144cleanContainer();145windowsMap->clear();146}147148CvWindow* HighguiBridge::createWindow(cv::String name)149{150CvWindow* window = new CvWindow(name);151windowsMap->insert(std::pair<cv::String, CvWindow*>(name, window));152153return window;154}155156/***************************** CvTrackbar class *********************************/157158CvTrackbar::CvTrackbar(cv::String name, Slider^ slider, CvWindow* parent) : name(name), slider(slider), parent(parent) {}159160CvTrackbar::~CvTrackbar() {}161162void CvTrackbar::setPosition(double pos)163{164if (pos < 0)165pos = 0;166167if (pos > slider->Maximum)168pos = slider->Maximum;169170slider->Value = pos;171}172173void CvTrackbar::setMaxPosition(double pos)174{175//slider->Minimum is initialized with 0176if (pos < slider->Minimum)177pos = slider->Minimum;178179slider->Maximum = pos;180}181182void CvTrackbar::setMinPosition(double pos)183{184if (pos < 0)185pos = 0;186//Min is always less than Max.187if (pos > slider->Maximum)188pos = slider->Maximum;189slider->Minimum = pos;190}191192void CvTrackbar::setSlider(Slider^ slider) {193if (slider)194this->slider = slider;195}196197double CvTrackbar::getPosition()198{199return slider->Value;200}201202double CvTrackbar::getMaxPosition()203{204return slider->Maximum;205}206207double CvTrackbar::getMinPosition()208{209return slider->Minimum;210}211212Slider^ CvTrackbar::getSlider()213{214return slider;215}216217/***************************** CvWindow class ***********************************/218219CvWindow::CvWindow(cv::String name, int flags) : name(name)220{221this->page = (Page^)Windows::UI::Xaml::Markup::XamlReader::Load(const_cast<Platform::String^>(markupContent));222this->sliderMap = new std::map<cv::String, CvTrackbar*>();223224sliderPanel = (Panel^)page->FindName("cvTrackbar");225imageControl = (Image^)page->FindName("cvImage");226buttonPanel = (Panel^)page->FindName("cvButton");227228// Required to adapt controls to the size of the image.229// System calculates image control width first, after that we can230// update other controls231imageControl->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler(232[=](Platform::Object^ sender,233Windows::UI::Xaml::RoutedEventArgs^ e)234{235// Need to update sliders with appropriate width236for (auto iter = sliderMap->begin(); iter != sliderMap->end(); ++iter) {237iter->second->getSlider()->Width = imageControl->ActualWidth;238}239240// Need to update buttons with appropriate width241// TODO: implement when adding buttons242});243244}245246CvWindow::~CvWindow() {}247248void CvWindow::createSlider(cv::String name, int* val, int count, CvTrackbarCallback2 on_notify, void* userdata)249{250CvTrackbar* trackbar = findTrackbarByName(name);251252// Creating slider if name is new or reusing the existing one253Slider^ slider = !trackbar ? ref new Slider() : trackbar->getSlider();254255slider->Header = HighguiBridge::getInstance().convertString(name);256257// Making slider the same size as the image control or setting minimal size.258// This is added to cover potential edge cases because:259// 1. Fist clause will not be true until the second call to any container-updating API260// e.g. cv::createTrackbar, cv:imshow or cv::namedWindow261// 2. Second clause will work but should be immediately overridden by Image->Loaded callback,262// see CvWindow ctor.263if (this->imageControl->ActualWidth > 0) {264// One would use double.NaN for auto-stretching but there is no such constant in C++/CX265// see https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.frameworkelement.width266slider->Width = this->imageControl->ActualWidth;267} else {268// This value would never be used/seen on the screen unless there is something wrong with the image.269// Although this code actually gets called, slider width will be overridden in the callback after270// Image control is loaded. See callback implementation in CvWindow ctor.271slider->Width = sliderDefaultWidth;272}273slider->Value = *val;274slider->Maximum = count;275slider->Visibility = Windows::UI::Xaml::Visibility::Visible;276slider->Margin = Windows::UI::Xaml::ThicknessHelper::FromLengths(10, 10, 10, 0);277slider->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;278279if (!trackbar)280{281if (!sliderPanel) return;282283// Adding slider to the list for current window284CvTrackbar* trackbar = new CvTrackbar(name, slider, this);285trackbar->callback = on_notify;286slider->ValueChanged +=287ref new Controls::Primitives::RangeBaseValueChangedEventHandler(288[=](Platform::Object^ sender,289Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^ e)290{291Slider^ slider = (Slider^)sender;292trackbar->callback(slider->Value, nullptr);293});294this->sliderMap->insert(std::pair<cv::String, CvTrackbar*>(name, trackbar));295296// Adding slider to the window297sliderPanel->Children->Append(slider);298}299}300301CvTrackbar* CvWindow::findTrackbarByName(cv::String name)302{303auto search = sliderMap->find(name);304if (search != sliderMap->end()) {305return search->second;306}307308return nullptr;309}310311void CvWindow::updateImage(CvMat* src)312{313if (!imageControl) return;314315this->imageData = src;316this->imageWidth = src->width;317318// Create the WriteableBitmap319WriteableBitmap^ bitmap = ref new WriteableBitmap(src->cols, src->rows);320321// Get access to the pixels322IBuffer^ buffer = bitmap->PixelBuffer;323unsigned char* dstPixels;324325// Obtain IBufferByteAccess326ComPtr<IBufferByteAccess> pBufferByteAccess;327ComPtr<IInspectable> pBuffer((IInspectable*)buffer);328pBuffer.As(&pBufferByteAccess);329330// Get pointer to pixel bytes331pBufferByteAccess->Buffer(&dstPixels);332memcpy(dstPixels, src->data.ptr, CV_ELEM_SIZE(src->type) * src->cols*src->rows);333334// Set the bitmap to the Image element335imageControl->Source = bitmap;336}337338Page^ CvWindow::getPage()339{340return page;341}342343//TODO: prototype, not in use yet344void CvWindow::createButton(cv::String name)345{346if (!buttonPanel) return;347348Button^ b = ref new Button();349b->Content = HighguiBridge::getInstance().convertString(name);350b->Width = 260;351b->Height = 80;352b->Click += ref new Windows::UI::Xaml::RoutedEventHandler(353[=](Platform::Object^ sender,354Windows::UI::Xaml::RoutedEventArgs^ e)355{356Button^ button = (Button^)sender;357// TODO: more logic here...358});359360buttonPanel->Children->Append(b);361}362363// end364365