MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Как рисовать векторную графику в Windows API?"
Answer 886630
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; }
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.