Invalid operation while gcnew Image CLI/C++ - c++

I get weird information about invalidOperationException in PresentationCore.dll while constructing an image by gcnew Image().
I attach project and the JPG file (which can be put in C:\) It actually cannot be checked other way because configuration of project (references) took a long time, and just copied code will not work.
http://www.speedyshare.com/Vrr84/Jpg.zip
How can I solve that problem?
// Jpg.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#using <mscorlib.dll> //requires CLI
using namespace System;
using namespace System::IO;
using namespace System::Windows::Media::Imaging;
using namespace System::Windows::Media;
using namespace System::Windows::Controls;
int _tmain(int argc, _TCHAR* argv[])
{
// Open a Stream and decode a JPEG image
Stream^ imageStreamSource = gcnew FileStream("C:/heart.jpg", FileMode::Open, FileAccess::Read, FileShare::Read);
JpegBitmapDecoder^ decoder = gcnew JpegBitmapDecoder(imageStreamSource, BitmapCreateOptions::PreservePixelFormat, BitmapCacheOption::Default);
BitmapSource^ bitmapSource = decoder->Frames[0];//< --mamy bitmape
// Draw the Image
Image^ myImage = gcnew Image();//<----------- ERROR
myImage->Source = bitmapSource;
myImage->Stretch = Stretch::None;
myImage->Margin = System::Windows::Thickness(20);
//
int width = 128;
int height = width;
int stride = width / 8;
array<System::Byte>^ pixels = gcnew array<System::Byte>(height * stride);
// Define the image paletteo
BitmapPalette^ myPalette = BitmapPalettes::Halftone256;
// Creates a new empty image with the pre-defined palette.
BitmapSource^ image = BitmapSource::Create(
width, height,
96, 96,
PixelFormats::Indexed1,
myPalette,
pixels,
stride);
System::IO::FileStream^ stream = gcnew System::IO::FileStream("new.jpg", FileMode::Create);
JpegBitmapEncoder^ encoder = gcnew JpegBitmapEncoder();
TextBlock^ myTextBlock = gcnew System::Windows::Controls::TextBlock();
myTextBlock->Text = "Codec Author is: " + encoder->CodecInfo->Author->ToString();
encoder->FlipHorizontal = true;
encoder->FlipVertical = false;
encoder->QualityLevel = 30;
encoder->Rotation = Rotation::Rotate90;
encoder->Frames->Add(BitmapFrame::Create(image));
encoder->Save(stream);
return 0;
}

The core issue is there:
The calling thread must be STA [...]
Your main thread must be marked as a single-threaded apartment (STA for short) for WPF to function correctly. The fix? Add [System::STAThread] to your _tmain, thus informing the runtime that the main thead has to be STA.
[System::STAThread]
int _tmain(int argc, _TCHAR* argv[])
{
// the rest of your code doesn't change
}

Related

Clip Raster with Polygon with GDAL C++

I am trying to clip a raster using a polygon an GDAL. At the moment i get an error that there is a read access violation when initializing the WarpOperation. I can access my Shapefile and check the num of features so the access is fine i think. Also i can access my Raster Data (GetProjectionRef).. All files are in the same CRS. Is there a way to use GdalWarp with Cutline?
const char* inputPath = "input.tif";
const char* outputPath = "output.tif";
//clipper Polygon
auto w_read_filenamePoly = "Polygon.shp";
char* read_filenamePoly = new char[w_read_filenamePoly.length() + 1];
wcstombs(read_filenamePoly, w_read_filenamePoly.c_str(), w_read_filenamePoly.length() + 1);
GDALDataset* hSrcDS;
GDALDataset* hDstDS;
GDALAllRegister();
hSrcDS =(GDALDataset *) GDALOpen(inputPath, GA_Update);
hDstDS = (GDALDataset*)GDALOpen(outputPath, GA_Update);
const char* proj = hSrcDS->GetProjectionRef();
const char* proj2 = hDstDS->GetProjectionRef();
//clipper Layer
GDALDataset* poDSClipper;
poDSClipper = (GDALDataset*)GDALOpenEx(read_filenamePoly, GDAL_OF_UPDATE, NULL, NULL, NULL);
Assert::IsNotNull(poDSClipper);
delete[]read_filenamePoly;
OGRLayer* poLayerClipper;
poLayerClipper = poDSClipper->GetLayerByName("Polygon");
int numClip = poLayerClipper->GetFeatureCount();
//setup warp options
GDALWarpOptions* psWarpOptions = GDALCreateWarpOptions();
psWarpOptions->hSrcDS = hSrcDS;
psWarpOptions->hDstDS = hDstDS;
psWarpOptions->nBandCount = 1;
psWarpOptions->panSrcBands = (int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount);
psWarpOptions->panSrcBands[0] = 1;
psWarpOptions->panDstBands = (int*)CPLMalloc(sizeof(int) * psWarpOptions->nBandCount);
psWarpOptions->panDstBands[0] = 1;
psWarpOptions->pfnProgress = GDALTermProgress;
psWarpOptions->hCutline = poLayerClipper;
// Establish reprojection transformer.
psWarpOptions->pTransformerArg = GDALCreateGenImgProjTransformer(hSrcDS,proj, hDstDS, proj2, FALSE, 0.0, 1);
psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
GDALWarpOperation oOperation;
oOperation.Initialize(psWarpOptions);
oOperation.ChunkAndWarpImage(0, 0, GDALGetRasterXSize(hDstDS), GDALGetRasterYSize(hDstDS));
GDALDestroyGenImgProjTransformer(psWarpOptions->pTransformerArg);
GDALDestroyWarpOptions(psWarpOptions);
GDALClose(hDstDS);
GDALClose(hSrcDS);
Your psWarpOptions->hCutline should be a polygon, not a layer.
Also the cutline should be in source pixel/line coordinates.
Check TransformCutlineToSource from gdalwarp_lib.cpp, you can probably simply get the code from there.
This particular GDAL operation, when called from C++, is so full of pitfalls - and there are so many open questions about it here - that I am reproducing a full working example:
Warping (reprojecting) a raster image with a polygon mask (cutline):
#include <gdal/gdal.h>
#include <gdal/gdal_priv.h>
#include <gdal/gdalwarper.h>
#include <gdal/ogrsf_frmts.h>
int main() {
const char *inputPath = "input.tif";
const char *outputPath = "output.tif";
// clipper Polygon
// THIS FILE MUST BE IN PIXEL/LINE COORDINATES or otherwise one should
// copy the function gdalwarp_lib.cpp:TransformCutlineToSource()
// from GDAL's sources
// It is expected that it contains a single polygon feature
const char *read_filenamePoly = "cutline.json";
GDALDataset *hSrcDS;
GDALDataset *hDstDS;
GDALAllRegister();
auto poDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
hSrcDS = (GDALDataset *)GDALOpen(inputPath, GA_ReadOnly);
hDstDS = (GDALDataset *)poDriver->CreateCopy(
outputPath, hSrcDS, 0, nullptr, nullptr, nullptr);
// Without this step the cutline is useless - because the background
// will be carried over from the original image
CPLErr e = hDstDS->GetRasterBand(1)->Fill(0);
const char *src_srs = hSrcDS->GetProjectionRef();
const char *dst_srs = hDstDS->GetProjectionRef();
// clipper Layer
GDALDataset *poDSClipper;
poDSClipper = (GDALDataset *)GDALOpenEx(
read_filenamePoly, GDAL_OF_UPDATE, NULL, NULL, NULL);
auto poLayerClipper = poDSClipper->GetLayer(0);
auto geom = poLayerClipper->GetNextFeature()->GetGeometryRef();
// setup warp options
GDALWarpOptions *psWarpOptions = GDALCreateWarpOptions();
psWarpOptions->hSrcDS = hSrcDS;
psWarpOptions->hDstDS = hDstDS;
psWarpOptions->nBandCount = 1;
psWarpOptions->panSrcBands =
(int *)CPLMalloc(sizeof(int) * psWarpOptions->nBandCount);
psWarpOptions->panSrcBands[0] = 1;
psWarpOptions->panDstBands =
(int *)CPLMalloc(sizeof(int) * psWarpOptions->nBandCount);
psWarpOptions->panDstBands[0] = 1;
psWarpOptions->pfnProgress = GDALTermProgress;
psWarpOptions->hCutline = geom;
// Establish reprojection transformer.
psWarpOptions->pTransformerArg = GDALCreateGenImgProjTransformer(
hSrcDS, src_srs, hDstDS, dst_srs, TRUE, 1000, 1);
psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
GDALWarpOperation oOperation;
oOperation.Initialize(psWarpOptions);
oOperation.ChunkAndWarpImage(
0, 0, GDALGetRasterXSize(hDstDS), GDALGetRasterYSize(hDstDS));
GDALDestroyGenImgProjTransformer(psWarpOptions->pTransformerArg);
GDALDestroyWarpOptions(psWarpOptions);
GDALClose(hDstDS);
GDALClose(hSrcDS);
}

VTK: View doesn't update until after user interaction

TL;DR
I have a pipeline which reads an image and displays a mesh using VTK; upon changing the input to the image reader and updating the pipeline, the mesh doesn't update until I interact with the window.
The Long Version
I have a directory with a sequence of segmentation files (i.e., 3D image volumes where pixel values correspond to structures in a corresponding image) which show how a structure changes over time. I've written a utility in VTK which allows me to load the first image in the directory, visualize a label as a mesh, and "step" forwards or backwards using the arrow keys by changing the file name of the input image and updating the pipeline. This very nearly works--the only issue is that, after updating the pipeline, the meshes don't update until I interact with the window (simply clicking anywhere in the window causes the mesh to update, for example).
What I've Tried:
Calling Update() on the reader:
this->reader->Update();
Calling Modified() on the actors:
const auto actors = this->GetCurrentRenderer()->GetActors();
actors->InitTraversal();
for (vtkIdType i = 0; i < actors->GetNumberOfItems(); ++i)
{
actors->GetNextActor()->Modified();
}
Calling Render() on the current render window:
this->GetCurrentRenderer()->Render();
MCVE:
NOTE: the reader is updated in KeyPressInteractorStyle::UpdateReader().
// VTK
#include <vtkSmartPointer.h>
#include <vtkNIFTIImageReader.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCamera.h>
#include <vtkDiscreteMarchingCubes.h>
#include <vtkProperty.h>
#include <vtkInteractorStyleTrackballCamera.h>
// Define interaction style
class KeyPressInteractorStyle : public vtkInteractorStyleTrackballCamera
{
public:
static KeyPressInteractorStyle* New();
vtkTypeMacro(KeyPressInteractorStyle, vtkInteractorStyleTrackballCamera);
virtual void OnKeyPress()
{
// Get the keypress
vtkRenderWindowInteractor *rwi = this->Interactor;
std::string key = rwi->GetKeySym();
// Output the key that was pressed
std::cout << "Pressed " << key << std::endl;
// Handle an arrow key
if(key == "Down" || key == "Right")
{
this->index += 1;
this->UpdateReader();
}
// Handle an arrow key
if(key == "Up" || key == "Left")
{
this->index -= 1;
this->UpdateReader();
}
// Forward events
vtkInteractorStyleTrackballCamera::OnKeyPress();
}
void UpdateReader()
{
std::cout << "Frame: " << this->index << std::endl;
const auto fn = this->directory + std::to_string(this->index) + ".nii.gz";
std::cout << fn << std::endl;
this->reader->SetFileName( fn.c_str() );
this->reader->Update();
const auto actors = this->GetCurrentRenderer()->GetActors();
actors->InitTraversal();
for (vtkIdType i = 0; i < actors->GetNumberOfItems(); ++i)
{
actors->GetNextActor()->Modified();
}
this->GetCurrentRenderer()->Render();
}
unsigned int index = 0;
std::string directory;
vtkSmartPointer<vtkNIFTIImageReader> reader;
};
vtkStandardNewMacro(KeyPressInteractorStyle);
int
main( int argc, char ** argv )
{
std::string dn = argv[1];
const auto renderer = vtkSmartPointer<vtkRenderer>::New();
unsigned int frameid = 0;
const auto reader = vtkSmartPointer<vtkNIFTIImageReader>::New();
reader->SetFileName( (dn + std::to_string(frameid) + ".nii.gz").c_str() );
const auto cubes = vtkSmartPointer<vtkDiscreteMarchingCubes>::New();
cubes->SetInputConnection( reader->GetOutputPort() );
cubes->SetValue( 0, 1 );
const auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection( cubes->GetOutputPort() );
mapper->ScalarVisibilityOff();
const auto actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper( mapper );
renderer->AddActor( actor );
const auto window = vtkSmartPointer<vtkRenderWindow>::New();
window->AddRenderer( renderer );
const auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
const auto style = vtkSmartPointer<KeyPressInteractorStyle>::New();
style->reader = reader;
style->directory = dn;
interactor->SetInteractorStyle( style );
style->SetCurrentRenderer( renderer );
interactor->SetRenderWindow( window );
window->Render();
interactor->Start();
return EXIT_SUCCESS;
}
You have actually not tried to call Render() on the current render window, you are calling it on the current renderer, which are two different things. As the documentation of vtkRenderer::Renderer() states,
CALLED BY vtkRenderWindow ONLY.
End-user pass your way and call vtkRenderWindow::Render(). Create an
image. This is a superclass method which will in turn call the
DeviceRender method of Subclasses of vtkRenderer.
So change this->GetCurrentRenderer()->Render(); to this->GetCurrentRenderer()->GetRenderWindow()->Render();

BitmapSource copyPixels correct arguments

Used stride by me generates exception. I don't know what stride is correct. The input image is 32 bit JPG. Please tell me what values(I tried many things but they where generating exceptions or corrupted JPG) i should put into:
array<System::Byte>^ pixels = gcnew array<System::Byte>(WHAT VALUE);
bitmapSource->CopyPixels(pixels, WHAT VALUE, 0);
// Jpg.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#using <mscorlib.dll> //requires CLI
using namespace System;
using namespace System::IO;
using namespace System::Windows::Media::Imaging;
using namespace System::Windows::Media;
using namespace System::Windows::Controls;
using namespace std;
[System::STAThread]
int _tmain(int argc, _TCHAR* argv[])
{
// Open a Stream and decode a JPEG image
Stream^ imageStreamSource = gcnew FileStream("C:/heart2.jpg",
FileMode::Open, FileAccess::Read, FileShare::Read);
JpegBitmapDecoder^ decoder = gcnew JpegBitmapDecoder(
imageStreamSource, BitmapCreateOptions::PreservePixelFormat,
BitmapCacheOption::Default);
BitmapSource^ bitmapSource = decoder->Frames[0];//< --mamy bitmape
// Draw the Image
Image^ myImage = gcnew Image();
myImage->Source = bitmapSource;
myImage->Stretch = Stretch::None;
myImage->Margin = System::Windows::Thickness(20);
int width = bitmapSource->PixelWidth;
int height = bitmapSource->PixelHeight;
int stride = (width * bitmapSource->Format.BitsPerPixel + 31) / 32;
array<System::Byte>^ pixels
= gcnew array<System::Byte>(height * width * stride);
bitmapSource->CopyPixels(pixels, stride, 0);
int x;
cin >> x;
return 0;
}
Google
http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.stride.aspx
The stride is the width of a single row of pixels (a scan line), rounded up to a four-byte boundary.
So the correct value depends on how many bits per pixel you have in your image.

How to include JpegBitmapDecoder^ in C++/CLI project

As in the subject how to include JpegBitmapDecoder^ in C++/CLI project. I have tried inlude namespace but I get:
Error 1 error C3083: 'Windows': the symbol to the left of a '::' must be a type C:\Users\Duke\Documents\Visual Studio 2010\Projects\Jpg\Jpg\Jpg.cpp 8 1 Jpg
Error 2 error C3083: 'Media': the symbol to the left of a '::' must be a type C:\Users\Duke\Documents\Visual Studio 2010\Projects\Jpg\Jpg\Jpg.cpp 8 1 Jpg
Error 3 error C2039: 'Imaging' : is not a member of 'System' C:\Users\Duke\Documents\Visual Studio 2010\Projects\Jpg\Jpg\Jpg.cpp 8 1 Jpg
Error 4 error C2871: 'Imaging' : a namespace with this name does not exist C:\Users\Duke\Documents\Visual Studio 2010\Projects\Jpg\Jpg\Jpg.cpp 8 1 Jpg
Jpg
// Jpg.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#using <mscorlib.dll> //requires CLI
using namespace System;
using namespace System::IO;
using namespace System::Windows::Media::Imaging;
int _tmain(int argc, _TCHAR* argv[])
{
// Open a Stream and decode a JPEG image
Stream^ imageStreamSource = gcnew FileStream("C:\Users\Duke\Desktop\heart.jpg", FileMode::Open, FileAccess::Read, FileShare::Read);
JpegBitmapDecoder^ decoder = gcnew JpegBitmapDecoder(imageStreamSource, BitmapCreateOptions::PreservePixelFormat, BitmapCacheOption::Default);//i want that decoder
BitmapSource^ bitmapSource = decoder->Frames[0];//< --mamy bitmape
// Draw the Image
Image^ myImage = gcnew Image();
myImage->Source = bitmapSource;
myImage->Stretch = Stretch::None;
myImage->Margin = System::Windows::Thickness(20);
//
int width = 128;
int height = width;
int stride = width / 8;
array<System::Byte>^ pixels = gcnew array<System::Byte>(height * stride);
// Define the image palette
BitmapPalette^ myPalette = BitmapPalettes::Halftone256;
// Creates a new empty image with the pre-defined palette.
BitmapSource^ image = BitmapSource::Create(
width, height,
96, 96,
PixelFormats::Indexed1,
myPalette,
pixels,
stride);
System::IO::FileStream^ stream = gcnew System::IO::FileStream("new.jpg", FileMode::Create);
JpegBitmapEncoder^ encoder = gcnew JpegBitmapEncoder();
TextBlock^ myTextBlock = gcnew System::Windows::Controls::TextBlock();
myTextBlock->Text = "Codec Author is: " + encoder->CodecInfo->Author->ToString();
encoder->FlipHorizontal = true;
encoder->FlipVertical = false;
encoder->QualityLevel = 30;
encoder->Rotation = Rotation::Rotate90;
encoder->Frames->Add(BitmapFrame::Create(image));
encoder->Save(stream);
return 0;
}
Use this instead:
using namespace System::Windows::Media::Imaging;
Clearly, the line above that should have hinted that in C++/CLI, you use :: rather than ..

Programmatically grab screenshots in OSX

I am going to port some screenshot grabbing code (C++) for linux to osx. The current solution run graphical applications in xvfb and then use xlib to grab screenshots from the display. (That will also support if we are running without xvfb).
So as I understood osx is moving away from X11 so my question is what to use besides xlib to implement it now ? I have found Quartz Display Services. Is that what makes sense to use now ? Will that work with xvfb ?
Yes, you will be able to call functions like CGDisplayCreateImage (documentation linked for you) by linking the Application Services framework to your C++ tool.
I have written an example for capturing the pc display screen and convert to opencv Mat.
#include <iostream>
#include <opencv2/opencv.hpp>
#include <unistd.h>
#include <stdio.h>
#include <ApplicationServices/ApplicationServices.h>
using namespace std;
using namespace cv;
int main (int argc, char * const argv[])
{
size_t width = CGDisplayPixelsWide(CGMainDisplayID());
size_t height = CGDisplayPixelsHigh(CGMainDisplayID());
Mat im(cv::Size(width,height), CV_8UC4);
Mat bgrim(cv::Size(width,height), CV_8UC3);
Mat resizedim(cv::Size(width,height), CV_8UC3);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef contextRef = CGBitmapContextCreate(
im.data, im.cols, im.rows,
8, im.step[0],
colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);
while (true)
{
CGImageRef imageRef = CGDisplayCreateImage(CGMainDisplayID());
CGContextDrawImage(contextRef,
CGRectMake(0, 0, width, height),
imageRef);
cvtColor(im, bgrim, CV_RGBA2BGR);
resize(bgrim, resizedim,cv::Size(),0.5,0.5);
imshow("test", resizedim);
cvWaitKey(10);
CGImageRelease(imageRef);
}
// CGContextRelease(contextRef);
// CGColorSpaceRelease(colorSpace);
return 0;
}
and then, the result is here.
I had expected my current display would be captured, but only the back wallpaper was captured actually.
What the CGMainDisplayID() refers would be a hint to this problem.
Anyway, I hope this may approach your goal a bit.
void captureScreen(){
CGImageRef image_ref = CGDisplayCreateImage(CGMainDisplayID());
CGDataProviderRef provider = CGImageGetDataProvider(image_ref);
CFDataRef dataref = CGDataProviderCopyData(provider);
size_t width, height; width = CGImageGetWidth(image_ref);
height = CGImageGetHeight(image_ref);
size_t bpp = CGImageGetBitsPerPixel(image_ref) / 8;
uint8 *pixels = malloc(width * height * bpp);
memcpy(pixels, CFDataGetBytePtr(dataref), width * height * bpp);
CFRelease(dataref);
CGImageRelease(image_ref);
FILE *stream = fopen("/Users/username/Desktop/screencap.raw", "w+");
fwrite(pixels, bpp, width * height, stream);
fclose(stream);
free(pixels);
}
or in C#:
// https://stackoverflow.com/questions/1537587/capture-screen-image-in-c-on-osx
// https://github.com/Acollie/C-Screenshot-OSX/blob/master/C%2B%2B-screenshot/C%2B%2B-screenshot/main.cpp
// https://github.com/ScreenshotMonitor/ScreenshotCapture/blob/master/src/Pranas.ScreenshotCapture/ScreenshotCapture.cs
// https://screenshotmonitor.com/blog/capturing-screenshots-in-net-and-mono/
namespace rtaStreamingServer
{
// https://github.com/xamarin/xamarin-macios
// https://qiita.com/shimshimkaz/items/18bcf4767143ea5897c7
public static class OSxScreenshot
{
private const string LIBCOREGRAPHICS = "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics";
[System.Runtime.InteropServices.DllImport(LIBCOREGRAPHICS)]
private static extern System.IntPtr CGDisplayCreateImage(System.UInt32 displayId);
[System.Runtime.InteropServices.DllImport(LIBCOREGRAPHICS)]
private static extern void CFRelease(System.IntPtr handle);
public static void TestCapture()
{
Foundation.NSNumber mainScreen = (Foundation.NSNumber)AppKit.NSScreen.MainScreen.DeviceDescription["NSScreenNumber"];
using (CoreGraphics.CGImage cgImage = CreateImage(mainScreen.UInt32Value))
{
// https://stackoverflow.com/questions/17334786/get-pixel-from-the-screen-screenshot-in-max-osx/17343305#17343305
// Get byte-array from CGImage
// https://gist.github.com/zhangao0086/5fafb1e1c0b5d629eb76
AppKit.NSBitmapImageRep bitmapRep = new AppKit.NSBitmapImageRep(cgImage);
// var imageData = bitmapRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
Foundation.NSData imageData = bitmapRep.RepresentationUsingTypeProperties(AppKit.NSBitmapImageFileType.Png);
long len = imageData.Length;
byte[] bytes = new byte[len];
System.Runtime.InteropServices.GCHandle pinnedArray = System.Runtime.InteropServices.GCHandle.Alloc(bytes, System.Runtime.InteropServices.GCHandleType.Pinned);
System.IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
imageData.GetBytes(pointer, new System.IntPtr(len));
pinnedArray.Free();
using (AppKit.NSImage nsImage = new AppKit.NSImage(cgImage, new System.Drawing.SizeF(cgImage.Width, cgImage.Height)))
{
// ImageView.Image = nsImage;
// And now ? How to get the image bytes ?
// https://theconfuzedsourcecode.wordpress.com/2016/02/24/convert-android-bitmap-image-and-ios-uiimage-to-byte-array-in-xamarin/
// https://stackoverflow.com/questions/5645157/nsimage-from-byte-array
// https://stackoverflow.com/questions/53060723/nsimage-source-from-byte-array-cocoa-app-xamarin-c-sharp
// https://gist.github.com/zhangao0086/5fafb1e1c0b5d629eb76
// https://www.quora.com/What-is-a-way-to-convert-UIImage-to-a-byte-array-in-Swift?share=1
// https://stackoverflow.com/questions/17112314/converting-uiimage-to-byte-array
} // End Using nsImage
} // End Using cgImage
} // End Sub TestCapture
public static CoreGraphics.CGImage CreateImage(System.UInt32 displayId)
{
System.IntPtr handle = System.IntPtr.Zero;
try
{
handle = CGDisplayCreateImage(displayId);
return new CoreGraphics.CGImage(handle);
}
finally
{
if (handle != System.IntPtr.Zero)
{
CFRelease(handle);
}
}
} // End Sub CreateImage
} // End Class OSxScreenshot
} // End Namespace rtaStreamingServer