Чем можно рисовать векторное изображение в Windows API? Особенно интересует SVG в GDI+. Есть ли смысл разбирать формат? Хотелось бы получить что-то похожее на то что сделано в C# XAML (не используя сторонние программы)
<Path Stroke="Black" Fill="Gray"
Data="M 10,100 C 10,300 300,-200 300,100"/>
GDI+ не поддерживает работу с SVG. Также в Windows нет другого стандартного средства для работы с SVG, доступного во всех поддерживаемых версиях ОС. Можно предложить несколько обходных путей.
Вариант 1 - Парсинг XMLЕсли вам нужен только небольшой набор элементов из SVG, можно написать свой парсер, вытянуть из SVG данные и отрисовать объекты вручную. Поскольку SVG основан на XML, можно использовать любую библиотеку для работы с XML, например MSXML. Пример чтения данных есть здесь.
Вариант 2 - Использование метафайловGDI+ поддерживает метафайлы (EMF/WMF), стандартный для Windows формат векторной графики. Он поддерживает далеко не все, что есть в SVG, но с базовыми фигурами справляется. Можно преобразовать SVG в метафайл с помощью сторонней программы или онлайн-сервиса, и использовать в программе уже его.
Например, создадим такой простейший SVG:
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0">
<circle cx="200" cy="200" r="150" stroke="red" fill="green" stroke-width="10"/>
</svg>
Перегоним его в WMF с помощью онлайн-сервиса, получим такой файл (он по какой-то причине некорректно отображается в Paint, но он не поврежден; при отрисовке с помощью кода ниже он нормально отображается).
Напишем код для отрисовки метафайла:
#include <Windows.h>
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
const int W_IMAGE = 6000; //для метафайлов почему-то приходится задавать очень большую ширину и высоту, иначе они не попадают в область отображения.
const int H_IMAGE = 6000;
Gdiplus::Metafile * wmf = NULL;
//...
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//загрузка изображения
wmf = new Gdiplus::Metafile(L"D:\\images\\circle.wmf");
}
break;
case WM_PAINT:
{
//отрисовка изображения
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics g(hdc);
if(wmf != NULL){
g.DrawImage(wmf,0, 0, 0, 0, W_IMAGE, H_IMAGE, Gdiplus::Unit::UnitPixel);
}
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Результат:
Вариант 3 - Преобразование в растровое изображениеМожно преобразовать SVG в Bitmap (в отличие от предыдущего способа, это легко сделать на лету, программно) и нарисовать его. Например так, с помощью MSHTML и OLE (требует наличия IE 11):
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <Windows.h>
#include <oleidl.h>
#include <Mshtml.h>
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
const int W_IMAGE = 600;
const int H_IMAGE = 600;
//Глобальные переменные
HINSTANCE hInst; // текущий экземпляр
WCHAR szTitle[100] = L"Window"; // текст строки заголовка
WCHAR szWindowClass[100] = L"MyClass"; // имя класса главного окна
Gdiplus::Bitmap* svgbitmap = NULL; // объект изображения
//объявления функций, включенных в этот модуль кода:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DocumentWrite(IHTMLDocument2* pdoc, const WCHAR* content) {
HRESULT hr = 0;
VARIANT *param;
BSTR bstr = SysAllocString(content);
// Creates a new one-dimensional array
SAFEARRAY *psaStrings = SafeArrayCreateVector(VT_VARIANT, 0, 1);
if (psaStrings == NULL) {
goto cleanup;
}
hr = SafeArrayAccessData(psaStrings, (LPVOID*)¶m);
param->vt = VT_BSTR;
param->bstrVal = bstr;
hr = SafeArrayUnaccessData(psaStrings);
hr = pdoc->write(psaStrings);
cleanup:
// SafeArrayDestroy calls SysFreeString for each BSTR
if (psaStrings != NULL) {
SafeArrayDestroy(psaStrings);
}
}
// *** Преобразование SVG в Bitmap ***
Gdiplus::Bitmap* SvgToBitmap(const WCHAR* svgcontent, int w_image, int h_image) {
const int HIMETRIC_INCH = 2540;
SIZEL sz = { 0 };
RECTL rcClient = { 0 };
HDC screendc;
HDC hdc;
WCHAR svghtml1[] = L"<html><head><meta http-equiv=\"X-UA-Compatible\" content=\"IE=11\" /></head><body>";
WCHAR svghtml2[] = L"</body></html>";
Gdiplus::Bitmap* bmp = NULL;
Gdiplus::Graphics* g = NULL;
IHTMLDocument2* d2 = NULL;
IOleObject* pObj = NULL;
IViewObject* pView = NULL;
BOOL b = SystemParametersInfo(SPI_GETWORKAREA, 0, &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);
//создание документа
HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (LPVOID*)&d2);
if (FAILED(hr)) {
MessageBox(NULL, L"CoCreateInstance failed", NULL, MB_OK|MB_ICONERROR); goto End;
}
hr = d2->QueryInterface(IID_IOleObject, (LPVOID*)&pObj);
if (FAILED(hr)) { MessageBox(NULL, L"QueryInterface failed", 0, 0); goto End; }
//установка размера документа
screendc = GetDC(NULL);
sz.cx = (UINT)MulDiv(width, HIMETRIC_INCH, GetDeviceCaps(screendc, LOGPIXELSX));
sz.cy = (UINT)MulDiv(height, HIMETRIC_INCH, GetDeviceCaps(screendc, LOGPIXELSY));
ReleaseDC(NULL,screendc);
hr = pObj->SetExtent(DVASPECT_CONTENT,&sz);
if (FAILED(hr)) { MessageBox(NULL, L"SetExtent failed", NULL, MB_OK | MB_ICONERROR); goto End; }
//запись SVG в документ
DocumentWrite(d2, svghtml1);
DocumentWrite(d2, svgcontent);
DocumentWrite(d2, svghtml2);
d2->close();
//преобразование в Bitmap
hr = d2->QueryInterface(IID_IViewObject, (LPVOID*)&pView);
if (FAILED(hr)) { MessageBox(NULL, L"Cannot get IViewObject!", NULL, MB_OK | MB_ICONERROR); goto End; }
bmp = new Gdiplus::Bitmap(w_image, h_image);
g = Gdiplus::Graphics::FromImage(bmp);
hdc = g->GetHDC();
hr = pView->Draw(DVASPECT_CONTENT,-1, NULL, NULL, NULL, hdc, & rcClient, NULL,NULL, 0);
g->ReleaseHDC(hdc);
if (FAILED(hr)) MessageBox(NULL, L"Draw failed", NULL, MB_OK | MB_ICONERROR);
End:
if( g != NULL) delete g;
if (d2 != NULL)d2->Release();
if (pObj != NULL)pObj->Release();
if (pView != NULL)pView->Release();
return bmp;
}
// *** GUI ***
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
CoInitialize(NULL);
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
MyRegisterClass(hInstance);
// Выполнить инициализацию приложения
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszClassName = szWindowClass;
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Сохранить маркер экземпляра в глобальной переменной
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//загрузка изображения
std::wifstream fStream(L"c:\\web\\image.svg");
std::wstringstream wstrStream;
wstrStream << fStream.rdbuf();
std::wstring s = wstrStream.str();
svgbitmap = SvgToBitmap(s.c_str(), W_IMAGE, H_IMAGE);
}
break;
case WM_PAINT:
{
//отрисовка изображения
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics g(hdc);
Gdiplus::Status res = g.DrawImage(svgbitmap, 0, 0, 0, 0, W_IMAGE, H_IMAGE, Gdiplus::Unit::UnitPixel);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
При работе с проектом в intellij idea, после внесения каких либо изменений (изменение текста кода или создание файла) эти изменения сразу передаются...
Имеется ошибка, сервер работает нормально, но последнее время проскакивает эта ошибка очень часто, источников не нашел
Из ардуино идут данные датчиковПример - приложение PhysicaloidTest работает и показывает что данные поступают и они верные