Есть картинка с экрана и картинка с камеры. Во второй некая область сделана прозрачной. Под эту прозрачную область требуется поместить картинку с экрана. Сложность в том, что подкладываемую картинку надо предварительно повернуть.
На css соответствующий поворот легко делается 3d-трансформацией:
transform: matrix3d(.615,0,0,.00015,0,.615,0,-.000005,0,0,1,0,-151,-120,0,1);
Мне нужно сделать такую же трансформацию, но на VB.NET или C#. Я нашёл, как сделать 2d-трансформацию, но вот про 3d что-то ничего не нахожу.
Как осуществить такую 3d-трансформацию в .NET?
Ниже помещён сниппет для наглядной (стоит развернуть его на всю страницу) демонстрации желаемого результата. В нём при наведении мышки на проекцию экрана, она перемещается на передний план, а её граница подсвечивается. Скрипт для масштабирования используется исключительно для наглядности и не влияет на результат.
~function(){var $window=$(window),$body=$("body");var ie= document.documentMode;function updateSizes(){var width=$window.width(),height=$window.height(),scale=Math.min(width/1920,height/1080,1);var style=$body[0].style;style.msZoom=ie===8||ie===9?scale:1;style.zoom=ie===10||ie===11?1:scale;style.mozTransform="scale("+scale+")";style.oTransform="scale("+scale+")";style.transform="scale("+scale+")";}$window.resize(updateSizes);updateSizes();}();
html{width:100%;overflow:hidden;}body{margin:0;position:relative;transform-origin:top left;}@supports(transform:scale(1)){body{-ms-zoom:1!important;zoom:1!important;}}
.camera{pointer-events:none;}.screen:hover{z-index:2;outline:10px solid rgba(255,0,0,.5);}
body {
width: 1920px;
height: 1080px;
}
img {
position: absolute;
}
.camera {
width: 1920px;
height: 1080px;
}
.screen {
width: 1600px;
height: 900px;
transform: matrix3d(.615,0,0,.00015,0,.615,0,-.000005,0,0,1,0,-151,-120,0,1);
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img class="screen" src="//i.stack.imgur.com/ThRWY.png">
<img class="camera" src="//i.stack.imgur.com/Ygb1O.png">
Способ 1 - С использованием WPF
Можно создать такой класс на основе Viewport3D из WPF:
//References: PresentationCore, PresentationFramework, WindowsBase, System.XAML
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Media.Imaging;
namespace WindowsFormsTest
{
public class ImageTransformer
{
/*Применяет произвольное 3D-преобразование к изображению*/
public static void ApplyTransform(string input, string output, Matrix3D matr)
{
Viewport3D myViewport3D;
BitmapImage image = new BitmapImage(new Uri(input));
// Declare scene objects.
myViewport3D = new Viewport3D();
Model3DGroup myModel3DGroup = new Model3DGroup();
GeometryModel3D myGeometryModel = new GeometryModel3D();
ModelVisual3D myModelVisual3D = new ModelVisual3D();
// Defines the camera used to view the 3D object. In order to view the 3D object,
// the camera must be positioned and pointed such that the object is within view
// of the camera.
PerspectiveCamera myPCamera = new PerspectiveCamera();
// Specify where in the 3D scene the camera is.
myPCamera.Position = new Point3D(0, 0, Math.Max(image.PixelWidth,image.PixelHeight)*2);
// Specify the direction that the camera is pointing.
myPCamera.LookDirection = new Vector3D(0, 0, -1);
// Define camera's horizontal field of view in degrees.
myPCamera.FieldOfView = 60;
// Asign the camera to the viewport
myViewport3D.Camera = myPCamera;
// Define the lights cast in the scene. Without light, the 3D object cannot
// be seen. Note: to illuminate an object from additional directions, create
// additional lights.
AmbientLight al = new AmbientLight(Colors.White);
myModel3DGroup.Children.Add(al);
// The geometry specifes the shape of the 3D plane. In this sample, a flat sheet
// is created.
MeshGeometry3D myMeshGeometry3D = new MeshGeometry3D();
// Create a collection of normal vectors for the MeshGeometry3D.
Vector3DCollection myNormalCollection = new Vector3DCollection();
myNormalCollection.Add(new Vector3D(0, 0, 1));
myNormalCollection.Add(new Vector3D(0, 0, 1));
myNormalCollection.Add(new Vector3D(0, 0, 1));
myNormalCollection.Add(new Vector3D(0, 0, 1));
myNormalCollection.Add(new Vector3D(0, 0, 1));
myNormalCollection.Add(new Vector3D(0, 0, 1));
myMeshGeometry3D.Normals = myNormalCollection;
// Create a collection of vertex positions for the MeshGeometry3D.
Point3DCollection myPositionCollection = new Point3DCollection();
myPositionCollection.Add(new Point3D(-image.PixelWidth / 2.0, -image.PixelHeight / 2.0, 0.5));
myPositionCollection.Add(new Point3D(image.PixelWidth / 2.0, -image.PixelHeight / 2.0, 0.5));
myPositionCollection.Add(new Point3D(image.PixelWidth / 2.0, image.PixelHeight / 2.0, 0.5));
myPositionCollection.Add(new Point3D(image.PixelWidth / 2.0, image.PixelHeight / 2.0, 0.5));
myPositionCollection.Add(new Point3D(-image.PixelWidth / 2.0, image.PixelHeight / 2.0, 0.5));
myPositionCollection.Add(new Point3D(-image.PixelWidth / 2.0, -image.PixelHeight / 2.0, 0.5));
myMeshGeometry3D.Positions = myPositionCollection;
// Create a collection of texture coordinates for the MeshGeometry3D.
PointCollection myTextureCoordinatesCollection = new PointCollection();
Point p5 = new Point(0, 0);
Point p34 = new Point(1, 0);
Point p2 = new Point(1, 1);
Point p16 = new Point(0, 1);
myTextureCoordinatesCollection.Add(p16);
myTextureCoordinatesCollection.Add(p2);
myTextureCoordinatesCollection.Add(p34);
myTextureCoordinatesCollection.Add(p34);
myTextureCoordinatesCollection.Add(p5);
myTextureCoordinatesCollection.Add(p16);
myMeshGeometry3D.TextureCoordinates = myTextureCoordinatesCollection;
// Create a collection of triangle indices for the MeshGeometry3D.
Int32Collection myTriangleIndicesCollection = new Int32Collection();
myTriangleIndicesCollection.Add(0);
myTriangleIndicesCollection.Add(1);
myTriangleIndicesCollection.Add(2);
myTriangleIndicesCollection.Add(3);
myTriangleIndicesCollection.Add(4);
myTriangleIndicesCollection.Add(5);
myMeshGeometry3D.TriangleIndices = myTriangleIndicesCollection;
// Apply the mesh to the geometry model.
myGeometryModel.Geometry = myMeshGeometry3D;
// The material specifies the material applied to the 3D object.
ImageBrush br = new ImageBrush(image);
// Define material and apply to the mesh geometries.
DiffuseMaterial myMaterial = new DiffuseMaterial(br);
myGeometryModel.Material = myMaterial;
myGeometryModel.BackMaterial = myMaterial;
MatrixTransform3D transform = new MatrixTransform3D(matr);
myGeometryModel.Transform = transform;
// Add the geometry model to the model group.
myModel3DGroup.Children.Add(myGeometryModel);
// Add the group of models to the ModelVisual3d.
myModelVisual3D.Content = myModel3DGroup;
myViewport3D.Children.Add(myModelVisual3D);
//render Viewport3D into bitmap
int width = image.PixelWidth ;
int height = image.PixelHeight;
myViewport3D.Width = width;
myViewport3D.Height = height;
myViewport3D.Measure(new Size(width, height));
myViewport3D.Arrange(new Rect(0, 0, width, height));
RenderTargetBitmap rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(myViewport3D);
//Save bitmap to file
using (var fileStream = new FileStream(output, FileMode.Create))
{
BitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(fileStream);
}
}
/*Поворачивает изображение вокруг указанной оси на указанный угол*/
public static void RotateImage(string input, string output, Vector3D axis, int angle)
{
var myRotateTransform3D = new RotateTransform3D();
var myAxisAngleRotation3d = new AxisAngleRotation3D();
myAxisAngleRotation3d.Axis = axis;
myAxisAngleRotation3d.Angle = angle;
myRotateTransform3D.Rotation = myAxisAngleRotation3d;
ImageTransformer.ApplyTransform(input, output, myRotateTransform3D.Value);
}
}
}
Пример использования (поворот на 30 градусов вокруг оси Y):
ImageTransformer.RotateImage(
"c:\\images\\image.jpg",
"c:\\images\\image2.jpg",
new System.Windows.Media.Media3D.Vector3D(0, 1, 0),
30);
Применение произвольного преобразования, заданного матрицей:
ImageTransformer.ApplyTransform(
"c:\\images\\image.jpg",
"c:\\images\\image2.jpg",
new System.Windows.Media.Media3D.Matrix3D(.615, 0, 0, .00015, 0, .615, 0, -.000005, 0, 0, 1, 0, -151, -120, 0, 1));
При использовании в проекте WinForms нужно добавить ссылки на необходимые сборки, но делать отдельный поток для обработки сообщений WPF (Application.Run) не обязательно.
Способ 2 - С использованием MSHTML
При наличии IE 10+ можно программно растеризовать приведенный HTML+CSS с помощью такого кода:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
//Reference: COM -> Microsoft HTML Object Library
namespace WindowsFormsTest
{
public class HtmlVisualizer
{
public static void DrawHtml(string content, string output)
{
RECTL rcClient = new RECTL();
bool b = SystemParametersInfo(SPI_GETWORKAREA, 0, ref rcClient, 0);
if (b == false) { rcClient.bottom = 480; rcClient.right = 640; }
int width = (int)(rcClient.right - rcClient.left);
int height = (int)(rcClient.bottom - rcClient.top);
IntPtr screendc = GetDC(IntPtr.Zero);
mshtml.HTMLDocument doc = null;
mshtml.IHTMLDocument2 d2 = null;
IOleObject pObj = null;
IViewObject pView = null;
try
{
doc = new mshtml.HTMLDocument(); //создание документа
d2 = (mshtml.IHTMLDocument2)doc;
int hr;
//установка размера документа
pObj = (IOleObject)d2;
SIZEL sz = new SIZEL();
sz.x = (uint)MulDiv(width, HIMETRIC_INCH, GetDeviceCaps(screendc, LOGPIXELSX));
sz.y = (uint)MulDiv(height, HIMETRIC_INCH, GetDeviceCaps(screendc, LOGPIXELSY));
;
hr = pObj.SetExtent((int)System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT, ref sz);
if (hr != 0) throw Marshal.GetExceptionForHR(hr);
d2.write(content);
d2.close();
while (d2.readyState != "complete") System.Windows.Forms.Application.DoEvents();
//преобразование в Bitmap
pView = (IViewObject)d2;
Bitmap bmp = new Bitmap(width, height);
Graphics g = Graphics.FromImage(bmp);
using (g)
{
IntPtr hdc = g.GetHdc();
hr = pView.Draw((int)System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT,
-1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, hdc, ref rcClient, IntPtr.Zero,
IntPtr.Zero, 0);
if (hr != 0) throw Marshal.GetExceptionForHR(hr);
g.ReleaseHdc(hdc);
}
//сохранение в PNG
bmp.Save(output, System.Drawing.Imaging.ImageFormat.Png);
}
finally
{
//освобождение ресурсов
if (d2 != null) Marshal.ReleaseComObject(d2);
if (pObj != null) Marshal.ReleaseComObject(pObj);
if (pView != null) Marshal.ReleaseComObject(pView);
if (doc != null) Marshal.ReleaseComObject(doc);
}
}
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool SystemParametersInfo(int nAction, int nParam, ref RECTL rc, int nUpdate);
public static int MulDiv(int number, int numerator, int denominator)
{
return (int)(((long)number * numerator) / denominator);
}
const int LOGPIXELSX = 88;
const int LOGPIXELSY = 90;
const int HIMETRIC_INCH = 2540;
const int SPI_GETWORKAREA = 48;
}
[ComImport()]
[GuidAttribute("0000010d-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IViewObject
{
int Draw([MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw,
ref RECTL lprcBounds, IntPtr lprcWBounds, IntPtr pfnContinue, int dwContinue);
int a();
int b();
int c();
int d();
int e();
}
[ComImport()]
[Guid("00000112-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleObject
{
void f();
void g();
void SetHostNames(object szContainerApp, object szContainerObj);
void Close(uint dwSaveOption);
void SetMoniker(uint dwWhichMoniker, object pmk);
void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk);
void x();
void y();
void DoVerb(uint iVerb, uint lpmsg, object pActiveSite, uint lindex, uint hwndParent, uint lprcPosRect);
void EnumVerbs(ref object ppEnumOleVerb);
void Update();
void IsUpToDate();
void GetUserClassID(uint pClsid);
void GetUserType(uint dwFormOfType, uint pszUserType);
int SetExtent(uint dwDrawAspect, ref SIZEL psizel);
void GetExtent(uint dwDrawAspect, uint psizel);
void Advise(object pAdvSink, uint pdwConnection);
void Unadvise(uint dwConnection);
void EnumAdvise(ref object ppenumAdvise);
void GetMiscStatus(uint dwAspect, uint pdwStatus);
void SetColorScheme(object pLogpal);
};
public struct RECTL
{
public uint left;
public uint top;
public uint right;
public uint bottom;
}
public struct SIZEL
{
public uint x;
public uint y;
}
}
На вход подавать HTML такого вида:
<html><head>
<meta http-equiv="X-UA-Compatible" content="IE=10" />
<style>
html{width:100%;overflow:hidden;}body{margin:0;position:relative;transform-origin:top left;}@supports(transform:scale(1)){body{-ms-zoom:1!important;zoom:1!important;}}
.camera{pointer-events:none;}.screen:hover{z-index:2;outline:10px solid rgba(255,0,0,.5);}
body {
width: 1920px;
height: 1080px;
}
img {
position: absolute;
}
.camera {
width: 1920px;
height: 1080px;
}
.screen {
width: 1600px;
height: 900px;
transform: matrix3d(.615,0,0,.00015,0,.615,0,-.000005,0,0,1,0,-151,-120,0,1);
}
</style>
</head>
<body>
<img class="screen" src="file://localhost/c:/images/screen.png">
<img class="camera" src="file://localhost/c:/images/kamera.png">
</body></html>
Источники:
How to: Create a 3-D Scene
Projecting an object into a scene based on world coordinates only
wpf, c#, renderTargetBitmap of viewport3D without assigning it to a window
Using IE to Save SVGs as Bitmaps to Wherever
Виртуальный выделенный сервер (VDS) становится отличным выбором
Хочу запустить apache kafka на Win7(На компе с Win10 все прошло гладко)
Подскажите, как можно передать дополнительную model во Modal view
ПриветсвтуюЕсть задача отобразить таблицу в компоненте textBox
У меня есть форма с установленной фоновой картинкойПоверх у меня лежит TextBox1