add GetPixels() and GetPixel() APIs (wip).

This commit is contained in:
hecomi
2016-11-30 02:40:16 +09:00
parent 32adf6c2d0
commit 7e3df8ec2e
21 changed files with 376 additions and 3 deletions
+36
View File
@@ -0,0 +1,36 @@
using UnityEngine;
public class GetPixelsExample : MonoBehaviour
{
[SerializeField] uDesktopDuplication.Texture uddTexture;
[SerializeField] int x = 100;
[SerializeField] int y = 100;
const int width = 64;
const int height = 32;
public Texture2D texture;
Color32[] colors = new Color32[width * height];
void Start()
{
// must be called (performance will be slightly down).
uDesktopDuplication.Manager.useGetPixels = true;
texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
GetComponent<Renderer>().material.mainTexture = texture;
}
void Update()
{
var monitor = uddTexture.monitor;
if (!monitor.hasBeenUpdated) return;
if (monitor.GetPixels(colors, x, y, width, height)) {
texture.SetPixels32(colors);
texture.Apply();
}
Debug.Log(monitor.GetPixel(monitor.cursorX, monitor.cursorY));
}
}
+12
View File
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fd3f838ff57152e44a1810274a9df49b
timeCreated: 1480420430
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 38a5db360d5147c46ae925104162b881
timeCreated: 1480420198
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
using UnityEngine;
using System.Collections;
public class GetPixels : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: bc0250b1a79f38b48bfddf1e05bda638
timeCreated: 1480420057
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+42 -1
View File
@@ -1,4 +1,5 @@
using System;
using UnityEngine;
using System;
using System.Text;
using System.Runtime.InteropServices;
@@ -78,6 +79,8 @@ public static class Lib
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DebugLogDelegate(string str);
[DllImport("uDesktopDuplication")]
public static extern bool IsInitialized();
[DllImport("uDesktopDuplication")]
public static extern void Initialize();
[DllImport("uDesktopDuplication")]
@@ -164,6 +167,12 @@ public static class Lib
public static extern int GetDirtyRectCount(int id);
[DllImport("uDesktopDuplication", EntryPoint = "GetDirtyRects")]
private static extern IntPtr GetDirtyRects_Internal(int id);
[DllImport("uDesktopDuplication", EntryPoint = "GetPixels")]
private static extern bool GetPixels_Internal(int id, System.IntPtr ptr, int x, int y, int width, int height);
[DllImport("uDesktopDuplication")]
public static extern bool HasBeenUpdated(int id);
[DllImport("uDesktopDuplication")]
public static extern bool UseGetPixels(bool use);
public static string GetName(int id)
{
@@ -197,6 +206,38 @@ public static class Lib
}
return rects;
}
public static Color32[] GetPixels(int id, int x, int y, int width, int height)
{
var color = new Color32[width * height];
GetPixels(id, color, x, y, width, height);
return color;
}
public static bool GetPixels(int id, Color32[] colors, int x, int y, int width, int height)
{
if (!Mathf.IsPowerOfTwo(width) || !Mathf.IsPowerOfTwo(height)) {
Debug.LogErrorFormat("width and height must be power of two.");
return false;
}
if (colors.Length < width * height) {
Debug.LogErrorFormat("colors is small.", id, x, y, width, height);
return false;
}
var handle = GCHandle.Alloc(colors, GCHandleType.Pinned);
var ptr = handle.AddrOfPinnedObject();
if (!GetPixels_Internal(id, ptr, x, y, width, height)) {
Debug.LogErrorFormat("GetPixels({0}, {1}, {2}, {3}, {4}) failed.", id, x, y, width, height);
return false;
}
handle.Free();
return true;
}
public static Color32 GetPixel(int id, int x, int y)
{
return GetPixels(id, x, y, 1, 1)[0];
}
}
}
@@ -52,6 +52,14 @@ public class Manager : MonoBehaviour
}
}
static public bool useGetPixels
{
set
{
Lib.UseGetPixels(value);
}
}
[Tooltip("Debug mode is not applied while running.")]
[SerializeField] DebugMode debugMode = DebugMode.File;
@@ -232,6 +232,11 @@ public class Monitor
get { return Lib.GetDirtyRects(id); }
}
public bool hasBeenUpdated
{
get { return Lib.HasBeenUpdated(id); }
}
public bool shouldBeUpdated
{
get;
@@ -317,6 +322,21 @@ public class Monitor
{
CreateTextureIfNeeded();
}
public Color32[] GetPixels(int x, int y, int width, int height)
{
return Lib.GetPixels(id, x, y, width, height);
}
public bool GetPixels(Color32[] colors, int x, int y, int width, int height)
{
return Lib.GetPixels(id, colors, x, y, width, height);
}
public Color32 GetPixel(int x, int y)
{
return Lib.GetPixel(id, x, y);
}
}
}
@@ -176,8 +176,8 @@ void Cursor::Draw(Monitor* monitor)
// Check if box is inner desktop area
if (capturedImageLeft < 0 ||
capturedImageTop < 0 ||
capturedImageRight > desktopImageWidth ||
capturedImageBottom > desktopImageHeight)
capturedImageRight >= desktopImageWidth ||
capturedImageBottom >= desktopImageHeight)
{
Debug::Error("Cursor::UpdateTexture() => box is out of area.");
Debug::Error(
@@ -193,6 +193,22 @@ void Monitor::Render(UINT timeout)
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture);
}
{
std::lock_guard<std::mutex> lock(mutex_);
ComPtr<ID3D11DeviceContext> context;
if (!ddaTexture_)
{
if (FAILED(GetDevice()->CreateTexture2D(&dstDesc, nullptr, &ddaTexture_)))
{
Debug::Error("Monitor::Render() => GetDevice()->CreateTexture2D() failed.");
return;
}
}
GetDevice()->GetImmediateContext(&context);
context->CopyResource(ddaTexture_.Get(), texture);
}
}
UpdateMetadata(frameInfo);
@@ -232,6 +248,8 @@ void Monitor::Render(UINT timeout)
}
return;
}
hasBeenUpdated_ = true;
}
@@ -456,4 +474,155 @@ int Monitor::GetDirtyRectCount() const
RECT* Monitor::GetDirtyRects() const
{
return metaData_.As<RECT>(moveRectSize_);
}
bool Monitor::GetPixels(UINT* ptr, int x, int y, int width, int height)
{
if (!GetMonitorManager()->UseGetPixels())
{
Debug::Error("Monitor::GetPixels() => UseGetPixels(true) must have been called when you want to use GetPixels().");
return false;
}
if (!ddaTexture_)
{
Debug::Error("Monitor::GetPixels() => texture is not set.");
return false;
}
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(GetRotation());
const auto monitorWidth = GetWidth();
const auto monitorHeight = GetHeight();
const auto isVertical =
monitorRot == DXGI_MODE_ROTATION_ROTATE90 ||
monitorRot == DXGI_MODE_ROTATION_ROTATE270;
const auto desktopImageWidth = !isVertical ? monitorWidth : monitorHeight;
const auto desktopImageHeight = !isVertical ? monitorHeight : monitorWidth;
int left, top, right, bottom;
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
{
left = y;
top = monitorWidth - x - width;
right = y + width;
bottom = monitorWidth - x;
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
{
left = monitorWidth - x - width;
top = monitorHeight - y - height;
right = monitorWidth - x;
bottom = monitorHeight - y;
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
{
left = monitorHeight - y - height;
top = x;
right = monitorHeight - y;
bottom = x + width;
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
default:
{
left = x;
top = y;
right = x + width;
bottom = y + height;
break;
}
}
if (left < 0 ||
top < 0 ||
right >= desktopImageWidth ||
bottom >= desktopImageHeight)
{
Debug::Error("Monitor::GetPixels() => is out of area.");
Debug::Error(
" ",
"(", left, ", ", top, ")",
" ~ (", right, ", ", bottom, ") > ",
"(", desktopImageWidth, ", ", desktopImageHeight, ")");
return false;
}
D3D11_BOX box;
box.left = left;
box.top = top;
box.front = 0;
box.right = right;
box.bottom = bottom;
box.back = 1;
// Create texture for capturing desktop image
ComPtr<ID3D11Texture2D> texture;
D3D11_TEXTURE2D_DESC desc;
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
if (FAILED(GetDevice()->CreateTexture2D(&desc, nullptr, &texture)))
{
Debug::Error("Monitor::GetPixels() => GetDevice()->CreateTexture2D() failed.");
return false;
}
std::lock_guard<std::mutex> lock(mutex_);
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopySubresourceRegion(texture.Get(), 0, 0, 0, 0, ddaTexture_.Get(), 0, &box);
ComPtr<IDXGISurface> surface;
if (FAILED(texture.As(&surface)))
{
Debug::Error("Monitor::GetPixels() => texture.As() failed.");
return false;
}
DXGI_MAPPED_RECT mappedSurface;
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
{
Debug::Error("Monitor::GetPixels() => surface->Map() failed.");
return false;
}
const auto colors = reinterpret_cast<UINT*>(mappedSurface.pBits);
std::memcpy(ptr, colors, width * height * sizeof(UINT));
std::reverse(&ptr[0], &ptr[width * height - 1]);
for (int row = 0; row < height; ++row)
{
const auto start = width * row;
const auto end = width * (row + 1) - 1;
std::reverse(&ptr[start], &ptr[end]);
}
if (FAILED(surface->Unmap()))
{
Debug::Error("Monitor::GetPixels() => surface->Unmap() failed.");
return false;
}
return true;
}
bool Monitor::HasBeenUpdated() const
{
return hasBeenUpdated_;
}
@@ -4,6 +4,7 @@
#include <dxgi1_2.h>
#include <wrl/client.h>
#include <memory>
#include <mutex>
#include "Common.h"
enum class MonitorState
@@ -51,6 +52,8 @@ public:
DXGI_OUTDUPL_MOVE_RECT* GetMoveRects() const;
int GetDirtyRectCount() const;
RECT* GetDirtyRects() const;
bool GetPixels(UINT* ptr, int x, int y, int width, int height);
bool HasBeenUpdated() const;
private:
void UpdateCursor(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
@@ -61,12 +64,15 @@ private:
int id_ = -1;
UINT dpiX_ = -1, dpiY_ = -1;
int width_ = -1, height_ = -1;
bool hasBeenUpdated_ = false;
State state_ = State::NotSet;
IDXGIOutputDuplication* deskDupl_ = nullptr;
ID3D11Texture2D* unityTexture_ = nullptr;
Microsoft::WRL::ComPtr<ID3D11Texture2D> ddaTexture_;
DXGI_OUTPUT_DESC outputDesc_;
MONITORINFOEX monitorInfo_;
Buffer<BYTE> metaData_;
UINT moveRectSize_ = 0;
UINT dirtyRectSize_ = 0;;
mutable std::mutex mutex_;
};
@@ -140,6 +140,18 @@ int MonitorManager::GetTimeout() const
}
void MonitorManager::UseGetPixels(bool use)
{
useGetPixels_ = use;
}
bool MonitorManager::UseGetPixels() const
{
return useGetPixels_;
}
int MonitorManager::GetMonitorCount() const
{
return static_cast<int>(monitors_.size());
@@ -32,6 +32,8 @@ public:
void Update();
void SetTimeout(int timeout);
int GetTimeout() const;
void UseGetPixels(bool use);
bool UseGetPixels() const;
// Getters from Unity
public:
@@ -41,6 +43,7 @@ public:
private:
int timeout_ = 10;
bool useGetPixels_ = false;
std::vector<std::shared_ptr<Monitor>> monitors_;
std::shared_ptr<Cursor> cursor_ = std::make_shared<Cursor>();
int cursorMonitorId_ = -1;
@@ -25,6 +25,11 @@ std::queue<Message> g_messages;
extern "C"
{
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsInitialized()
{
return g_unity && g_manager;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Initialize()
{
if (g_unity && !g_manager)
@@ -387,4 +392,30 @@ extern "C"
}
return nullptr;
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API GetPixels(int id, UINT* ptr, int x, int y, int width, int height)
{
if (!g_manager) return nullptr;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetPixels(ptr, x, y, width, height);
}
return false;
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API HasBeenUpdated(int id)
{
if (!g_manager) return nullptr;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->HasBeenUpdated();
}
return false;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UseGetPixels(bool use)
{
if (!g_manager) return;
g_manager->UseGetPixels(use);
}
}