mirror of
https://github.com/Bloodysharp/Cheat-ImGui-Menu-Design.git
synced 2024-12-22 16:07:23 +08:00
Merge branch 'master' of https://github.com/AZRAELSANTI/ImGui-Menu
This commit is contained in:
commit
525b0a44a7
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Include yourself SDK and FreeType sources
|
||||||
|
|
||||||
|
https://github.com/user-attachments/assets/7cb85929-0472-4b07-9cb4-46048e73981e
|
@ -1,9 +0,0 @@
|
|||||||
See BACKENDS and EXAMPLES files in the docs/ folder, or on the web at: https://github.com/ocornut/imgui/tree/master/docs
|
|
||||||
|
|
||||||
Backends = Helper code to facilitate integration with platforms/graphics api (used by Examples + should be used by your app).
|
|
||||||
Examples = Standalone applications showcasing integration with platforms/graphics api.
|
|
||||||
|
|
||||||
Some Examples have extra README files in their respective directory, please check them too!
|
|
||||||
|
|
||||||
Once Dear ImGui is running (in either examples or your own application/game/engine),
|
|
||||||
run and refer to ImGui::ShowDemoWindow() in imgui_demo.cpp for the end-user API.
|
|
605
backends/imgui_impl_dx11.cpp
Normal file
605
backends/imgui_impl_dx11.cpp
Normal file
@ -0,0 +1,605 @@
|
|||||||
|
// dear imgui: Renderer Backend for DirectX11
|
||||||
|
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
|
// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||||
|
// 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer.
|
||||||
|
// 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled).
|
||||||
|
// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
|
||||||
|
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||||
|
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||||
|
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||||
|
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||||
|
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
|
||||||
|
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
|
||||||
|
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
|
||||||
|
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||||
|
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
|
||||||
|
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||||
|
// 2016-05-07: DirectX11: Disabling depth-write.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_dx11.h"
|
||||||
|
|
||||||
|
// DirectX
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <d3dcompiler.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DirectX11 data
|
||||||
|
struct ImGui_ImplDX11_Data
|
||||||
|
{
|
||||||
|
ID3D11Device* pd3dDevice;
|
||||||
|
ID3D11DeviceContext* pd3dDeviceContext;
|
||||||
|
IDXGIFactory* pFactory;
|
||||||
|
ID3D11Buffer* pVB;
|
||||||
|
ID3D11Buffer* pIB;
|
||||||
|
ID3D11VertexShader* pVertexShader;
|
||||||
|
ID3D11InputLayout* pInputLayout;
|
||||||
|
ID3D11Buffer* pVertexConstantBuffer;
|
||||||
|
ID3D11PixelShader* pPixelShader;
|
||||||
|
ID3D11SamplerState* pFontSampler;
|
||||||
|
ID3D11ShaderResourceView* pFontTextureView;
|
||||||
|
ID3D11RasterizerState* pRasterizerState;
|
||||||
|
ID3D11BlendState* pBlendState;
|
||||||
|
ID3D11DepthStencilState* pDepthStencilState;
|
||||||
|
int VertexBufferSize;
|
||||||
|
int IndexBufferSize;
|
||||||
|
|
||||||
|
ImGui_ImplDX11_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VERTEX_CONSTANT_BUFFER_DX11
|
||||||
|
{
|
||||||
|
float mvp[4][4];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplDX11_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||||
|
|
||||||
|
// Setup viewport
|
||||||
|
D3D11_VIEWPORT vp;
|
||||||
|
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
|
||||||
|
vp.Width = draw_data->DisplaySize.x;
|
||||||
|
vp.Height = draw_data->DisplaySize.y;
|
||||||
|
vp.MinDepth = 0.0f;
|
||||||
|
vp.MaxDepth = 1.0f;
|
||||||
|
vp.TopLeftX = vp.TopLeftY = 0;
|
||||||
|
ctx->RSSetViewports(1, &vp);
|
||||||
|
|
||||||
|
// Setup shader and vertex buffers
|
||||||
|
unsigned int stride = sizeof(ImDrawVert);
|
||||||
|
unsigned int offset = 0;
|
||||||
|
ctx->IASetInputLayout(bd->pInputLayout);
|
||||||
|
ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
|
||||||
|
ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||||
|
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||||
|
ctx->VSSetShader(bd->pVertexShader, nullptr, 0);
|
||||||
|
ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
|
||||||
|
ctx->PSSetShader(bd->pPixelShader, nullptr, 0);
|
||||||
|
ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
|
||||||
|
ctx->GSSetShader(nullptr, nullptr, 0);
|
||||||
|
ctx->HSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||||
|
ctx->DSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||||
|
ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||||
|
|
||||||
|
// Setup blend state
|
||||||
|
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||||
|
ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
|
||||||
|
ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
|
||||||
|
ctx->RSSetState(bd->pRasterizerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render function
|
||||||
|
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
|
||||||
|
{
|
||||||
|
// Avoid rendering when minimized
|
||||||
|
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||||
|
ID3D11DeviceContext* ctx = bd->pd3dDeviceContext;
|
||||||
|
|
||||||
|
// Create and grow vertex/index buffers if needed
|
||||||
|
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||||
|
{
|
||||||
|
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||||
|
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||||
|
D3D11_BUFFER_DESC desc;
|
||||||
|
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||||
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||||
|
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
|
||||||
|
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||||
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||||
|
desc.MiscFlags = 0;
|
||||||
|
if (bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pVB) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||||
|
{
|
||||||
|
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||||
|
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||||
|
D3D11_BUFFER_DESC desc;
|
||||||
|
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||||
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||||
|
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
|
||||||
|
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||||
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||||
|
if (bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pIB) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload vertex/index data into a single contiguous GPU buffer
|
||||||
|
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
|
||||||
|
if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
|
||||||
|
return;
|
||||||
|
if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
|
||||||
|
return;
|
||||||
|
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
|
||||||
|
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||||
|
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||||
|
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||||
|
idx_dst += cmd_list->IdxBuffer.Size;
|
||||||
|
}
|
||||||
|
ctx->Unmap(bd->pVB, 0);
|
||||||
|
ctx->Unmap(bd->pIB, 0);
|
||||||
|
|
||||||
|
// Setup orthographic projection matrix into our constant buffer
|
||||||
|
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||||
|
{
|
||||||
|
D3D11_MAPPED_SUBRESOURCE mapped_resource;
|
||||||
|
if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
|
||||||
|
return;
|
||||||
|
VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData;
|
||||||
|
float L = draw_data->DisplayPos.x;
|
||||||
|
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||||
|
float T = draw_data->DisplayPos.y;
|
||||||
|
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||||
|
float mvp[4][4] =
|
||||||
|
{
|
||||||
|
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||||
|
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||||
|
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||||
|
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||||
|
};
|
||||||
|
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
|
||||||
|
ctx->Unmap(bd->pVertexConstantBuffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
|
||||||
|
struct BACKUP_DX11_STATE
|
||||||
|
{
|
||||||
|
UINT ScissorRectsCount, ViewportsCount;
|
||||||
|
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||||
|
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||||
|
ID3D11RasterizerState* RS;
|
||||||
|
ID3D11BlendState* BlendState;
|
||||||
|
FLOAT BlendFactor[4];
|
||||||
|
UINT SampleMask;
|
||||||
|
UINT StencilRef;
|
||||||
|
ID3D11DepthStencilState* DepthStencilState;
|
||||||
|
ID3D11ShaderResourceView* PSShaderResource;
|
||||||
|
ID3D11SamplerState* PSSampler;
|
||||||
|
ID3D11PixelShader* PS;
|
||||||
|
ID3D11VertexShader* VS;
|
||||||
|
ID3D11GeometryShader* GS;
|
||||||
|
UINT PSInstancesCount, VSInstancesCount, GSInstancesCount;
|
||||||
|
ID3D11ClassInstance *PSInstances[256], *VSInstances[256], *GSInstances[256]; // 256 is max according to PSSetShader documentation
|
||||||
|
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
|
||||||
|
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
|
||||||
|
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
|
||||||
|
DXGI_FORMAT IndexBufferFormat;
|
||||||
|
ID3D11InputLayout* InputLayout;
|
||||||
|
};
|
||||||
|
BACKUP_DX11_STATE old = {};
|
||||||
|
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
|
||||||
|
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||||
|
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||||
|
ctx->RSGetState(&old.RS);
|
||||||
|
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||||
|
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||||
|
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||||
|
ctx->PSGetSamplers(0, 1, &old.PSSampler);
|
||||||
|
old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256;
|
||||||
|
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
|
||||||
|
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
|
||||||
|
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||||
|
ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
|
||||||
|
|
||||||
|
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||||
|
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||||
|
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||||
|
ctx->IAGetInputLayout(&old.InputLayout);
|
||||||
|
|
||||||
|
// Setup desired DX state
|
||||||
|
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||||
|
|
||||||
|
// Render command lists
|
||||||
|
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||||
|
int global_idx_offset = 0;
|
||||||
|
int global_vtx_offset = 0;
|
||||||
|
ImVec2 clip_off = draw_data->DisplayPos;
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||||
|
{
|
||||||
|
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||||
|
if (pcmd->UserCallback != nullptr)
|
||||||
|
{
|
||||||
|
// User callback, registered via ImDrawList::AddCallback()
|
||||||
|
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||||
|
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||||
|
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||||
|
else
|
||||||
|
pcmd->UserCallback(cmd_list, pcmd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||||
|
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||||
|
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Apply scissor/clipping rectangle
|
||||||
|
const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||||
|
ctx->RSSetScissorRects(1, &r);
|
||||||
|
|
||||||
|
// Bind texture, Draw
|
||||||
|
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID();
|
||||||
|
ctx->PSSetShaderResources(0, 1, &texture_srv);
|
||||||
|
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||||
|
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore modified DX state
|
||||||
|
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||||
|
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||||
|
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||||
|
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||||
|
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||||
|
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||||
|
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||||
|
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
|
||||||
|
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
|
||||||
|
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
|
||||||
|
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||||
|
ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
|
||||||
|
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
|
||||||
|
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||||
|
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||||
|
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||||
|
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplDX11_CreateFontsTexture()
|
||||||
|
{
|
||||||
|
// Build texture atlas
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||||
|
unsigned char* pixels;
|
||||||
|
int width, height;
|
||||||
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||||
|
|
||||||
|
// Upload texture to graphics system
|
||||||
|
{
|
||||||
|
D3D11_TEXTURE2D_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.Width = width;
|
||||||
|
desc.Height = height;
|
||||||
|
desc.MipLevels = 1;
|
||||||
|
desc.ArraySize = 1;
|
||||||
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
desc.SampleDesc.Count = 1;
|
||||||
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||||
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||||
|
desc.CPUAccessFlags = 0;
|
||||||
|
|
||||||
|
ID3D11Texture2D* pTexture = nullptr;
|
||||||
|
D3D11_SUBRESOURCE_DATA subResource;
|
||||||
|
subResource.pSysMem = pixels;
|
||||||
|
subResource.SysMemPitch = desc.Width * 4;
|
||||||
|
subResource.SysMemSlicePitch = 0;
|
||||||
|
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
||||||
|
IM_ASSERT(pTexture != nullptr);
|
||||||
|
|
||||||
|
// Create texture view
|
||||||
|
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||||
|
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
||||||
|
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||||
|
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||||
|
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||||
|
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView);
|
||||||
|
pTexture->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
|
||||||
|
|
||||||
|
// Create texture sampler
|
||||||
|
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||||
|
{
|
||||||
|
D3D11_SAMPLER_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||||
|
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||||
|
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||||
|
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||||
|
desc.MipLODBias = 0.f;
|
||||||
|
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
||||||
|
desc.MinLOD = 0.f;
|
||||||
|
desc.MaxLOD = 0.f;
|
||||||
|
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||||
|
if (!bd->pd3dDevice)
|
||||||
|
return false;
|
||||||
|
if (bd->pFontSampler)
|
||||||
|
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||||
|
|
||||||
|
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||||
|
// If you would like to use this DX11 sample code but remove this dependency you can:
|
||||||
|
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
|
||||||
|
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||||
|
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||||
|
|
||||||
|
// Create the vertex shader
|
||||||
|
{
|
||||||
|
static const char* vertexShader =
|
||||||
|
"cbuffer vertexBuffer : register(b0) \
|
||||||
|
{\
|
||||||
|
float4x4 ProjectionMatrix; \
|
||||||
|
};\
|
||||||
|
struct VS_INPUT\
|
||||||
|
{\
|
||||||
|
float2 pos : POSITION;\
|
||||||
|
float4 col : COLOR0;\
|
||||||
|
float2 uv : TEXCOORD0;\
|
||||||
|
};\
|
||||||
|
\
|
||||||
|
struct PS_INPUT\
|
||||||
|
{\
|
||||||
|
float4 pos : SV_POSITION;\
|
||||||
|
float4 col : COLOR0;\
|
||||||
|
float2 uv : TEXCOORD0;\
|
||||||
|
};\
|
||||||
|
\
|
||||||
|
PS_INPUT main(VS_INPUT input)\
|
||||||
|
{\
|
||||||
|
PS_INPUT output;\
|
||||||
|
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
||||||
|
output.col = input.col;\
|
||||||
|
output.uv = input.uv;\
|
||||||
|
return output;\
|
||||||
|
}";
|
||||||
|
|
||||||
|
ID3DBlob* vertexShaderBlob;
|
||||||
|
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), nullptr, nullptr, nullptr, "main", "vs_4_0", 0, 0, &vertexShaderBlob, nullptr)))
|
||||||
|
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||||
|
if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), nullptr, &bd->pVertexShader) != S_OK)
|
||||||
|
{
|
||||||
|
vertexShaderBlob->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the input layout
|
||||||
|
D3D11_INPUT_ELEMENT_DESC local_layout[] =
|
||||||
|
{
|
||||||
|
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||||
|
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||||
|
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)offsetof(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||||
|
};
|
||||||
|
if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK)
|
||||||
|
{
|
||||||
|
vertexShaderBlob->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
vertexShaderBlob->Release();
|
||||||
|
|
||||||
|
// Create the constant buffer
|
||||||
|
{
|
||||||
|
D3D11_BUFFER_DESC desc;
|
||||||
|
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX11);
|
||||||
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||||
|
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||||
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||||
|
desc.MiscFlags = 0;
|
||||||
|
bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pVertexConstantBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the pixel shader
|
||||||
|
{
|
||||||
|
static const char* pixelShader =
|
||||||
|
"struct PS_INPUT\
|
||||||
|
{\
|
||||||
|
float4 pos : SV_POSITION;\
|
||||||
|
float4 col : COLOR0;\
|
||||||
|
float2 uv : TEXCOORD0;\
|
||||||
|
};\
|
||||||
|
sampler sampler0;\
|
||||||
|
Texture2D texture0;\
|
||||||
|
\
|
||||||
|
float4 main(PS_INPUT input) : SV_Target\
|
||||||
|
{\
|
||||||
|
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
||||||
|
return out_col; \
|
||||||
|
}";
|
||||||
|
|
||||||
|
ID3DBlob* pixelShaderBlob;
|
||||||
|
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), nullptr, nullptr, nullptr, "main", "ps_4_0", 0, 0, &pixelShaderBlob, nullptr)))
|
||||||
|
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||||
|
if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), nullptr, &bd->pPixelShader) != S_OK)
|
||||||
|
{
|
||||||
|
pixelShaderBlob->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pixelShaderBlob->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the blending setup
|
||||||
|
{
|
||||||
|
D3D11_BLEND_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.AlphaToCoverageEnable = false;
|
||||||
|
desc.RenderTarget[0].BlendEnable = true;
|
||||||
|
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||||
|
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||||
|
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
|
||||||
|
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
||||||
|
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
||||||
|
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||||
|
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||||
|
bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the rasterizer state
|
||||||
|
{
|
||||||
|
D3D11_RASTERIZER_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.FillMode = D3D11_FILL_SOLID;
|
||||||
|
desc.CullMode = D3D11_CULL_NONE;
|
||||||
|
desc.ScissorEnable = true;
|
||||||
|
desc.DepthClipEnable = true;
|
||||||
|
bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create depth-stencil State
|
||||||
|
{
|
||||||
|
D3D11_DEPTH_STENCIL_DESC desc;
|
||||||
|
ZeroMemory(&desc, sizeof(desc));
|
||||||
|
desc.DepthEnable = false;
|
||||||
|
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
|
||||||
|
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
|
||||||
|
desc.StencilEnable = false;
|
||||||
|
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
|
||||||
|
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
|
||||||
|
desc.BackFace = desc.FrontFace;
|
||||||
|
bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplDX11_CreateFontsTexture();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX11_InvalidateDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||||
|
if (!bd->pd3dDevice)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; }
|
||||||
|
if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||||
|
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||||
|
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||||
|
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; }
|
||||||
|
if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = nullptr; }
|
||||||
|
if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = nullptr; }
|
||||||
|
if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = nullptr; }
|
||||||
|
if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = nullptr; }
|
||||||
|
if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = nullptr; }
|
||||||
|
if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = nullptr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplDX11_Data* bd = IM_NEW(ImGui_ImplDX11_Data)();
|
||||||
|
io.BackendRendererUserData = (void*)bd;
|
||||||
|
io.BackendRendererName = "imgui_impl_dx11";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||||
|
|
||||||
|
// Get factory from device
|
||||||
|
IDXGIDevice* pDXGIDevice = nullptr;
|
||||||
|
IDXGIAdapter* pDXGIAdapter = nullptr;
|
||||||
|
IDXGIFactory* pFactory = nullptr;
|
||||||
|
|
||||||
|
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
|
||||||
|
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
|
||||||
|
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
|
||||||
|
{
|
||||||
|
bd->pd3dDevice = device;
|
||||||
|
bd->pd3dDeviceContext = device_context;
|
||||||
|
bd->pFactory = pFactory;
|
||||||
|
}
|
||||||
|
if (pDXGIDevice) pDXGIDevice->Release();
|
||||||
|
if (pDXGIAdapter) pDXGIAdapter->Release();
|
||||||
|
bd->pd3dDevice->AddRef();
|
||||||
|
bd->pd3dDeviceContext->AddRef();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX11_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||||
|
if (bd->pFactory) { bd->pFactory->Release(); }
|
||||||
|
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
|
||||||
|
if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); }
|
||||||
|
io.BackendRendererName = nullptr;
|
||||||
|
io.BackendRendererUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplDX11_NewFrame()
|
||||||
|
{
|
||||||
|
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplDX11_Init()?");
|
||||||
|
|
||||||
|
if (!bd->pFontSampler)
|
||||||
|
ImGui_ImplDX11_CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
32
backends/imgui_impl_dx11.h
Normal file
32
backends/imgui_impl_dx11.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// dear imgui: Renderer Backend for DirectX11
|
||||||
|
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
struct ID3D11Device;
|
||||||
|
struct ID3D11DeviceContext;
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
|
||||||
|
|
||||||
|
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
903
backends/imgui_impl_win32.cpp
Normal file
903
backends/imgui_impl_win32.cpp
Normal file
@ -0,0 +1,903 @@
|
|||||||
|
// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications)
|
||||||
|
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_impl_win32.h"
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#include <windowsx.h> // GET_X_LPARAM(), GET_Y_LPARAM()
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <dwmapi.h>
|
||||||
|
|
||||||
|
// Configuration flags to add in your imconfig.h file:
|
||||||
|
//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant.
|
||||||
|
|
||||||
|
// Using XInput for gamepad (will load DLL dynamically)
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
#include <xinput.h>
|
||||||
|
typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
|
||||||
|
typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
|
||||||
|
// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL).
|
||||||
|
// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window.
|
||||||
|
// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218)
|
||||||
|
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702)
|
||||||
|
// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162)
|
||||||
|
// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
|
||||||
|
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||||
|
// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode).
|
||||||
|
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
|
||||||
|
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
|
||||||
|
// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
|
||||||
|
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
|
||||||
|
// 2022-01-17: Inputs: always update key mods next and before a key event (not in NewFrame) to fix input queue with very low framerates.
|
||||||
|
// 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API.
|
||||||
|
// 2022-01-12: Inputs: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted.
|
||||||
|
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
|
||||||
|
// 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness.
|
||||||
|
// 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages.
|
||||||
|
// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus.
|
||||||
|
// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using TrackMouseEvent() to receive WM_MOUSELEAVE events).
|
||||||
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
|
// 2021-06-08: Fixed ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1).
|
||||||
|
// 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS).
|
||||||
|
// 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi).
|
||||||
|
// 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1.
|
||||||
|
// 2021-01-25: Inputs: Dynamically loading XInput DLL.
|
||||||
|
// 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed.
|
||||||
|
// 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs)
|
||||||
|
// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
|
||||||
|
// 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT.
|
||||||
|
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
|
||||||
|
// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
|
||||||
|
// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
|
||||||
|
// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
|
||||||
|
// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
|
||||||
|
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||||
|
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
|
||||||
|
// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
|
||||||
|
// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
|
||||||
|
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
|
||||||
|
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
|
||||||
|
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
|
||||||
|
// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
|
||||||
|
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||||
|
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
|
||||||
|
// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
|
||||||
|
// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
|
||||||
|
// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
|
||||||
|
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
|
||||||
|
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set.
|
||||||
|
|
||||||
|
struct ImGui_ImplWin32_Data
|
||||||
|
{
|
||||||
|
HWND hWnd;
|
||||||
|
HWND MouseHwnd;
|
||||||
|
int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area
|
||||||
|
int MouseButtonsDown;
|
||||||
|
INT64 Time;
|
||||||
|
INT64 TicksPerSecond;
|
||||||
|
ImGuiMouseCursor LastMouseCursor;
|
||||||
|
UINT32 KeyboardCodePage;
|
||||||
|
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
bool HasGamepad;
|
||||||
|
bool WantUpdateHasGamepad;
|
||||||
|
HMODULE XInputDLL;
|
||||||
|
PFN_XInputGetCapabilities XInputGetCapabilities;
|
||||||
|
PFN_XInputGetState XInputGetState;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ImGui_ImplWin32_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
|
||||||
|
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
|
||||||
|
static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
static void ImGui_ImplWin32_UpdateKeyboardCodePage()
|
||||||
|
{
|
||||||
|
// Retrieve keyboard code page, required for handling of non-Unicode Windows.
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
HKL keyboard_layout = ::GetKeyboardLayout(0);
|
||||||
|
LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT);
|
||||||
|
if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0)
|
||||||
|
bd->KeyboardCodePage = CP_ACP; // Fallback to default ANSI code page when fails.
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||||
|
|
||||||
|
INT64 perf_frequency, perf_counter;
|
||||||
|
if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&perf_frequency))
|
||||||
|
return false;
|
||||||
|
if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)();
|
||||||
|
io.BackendPlatformUserData = (void*)bd;
|
||||||
|
io.BackendPlatformName = "imgui_impl_win32";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||||
|
|
||||||
|
bd->hWnd = (HWND)hwnd;
|
||||||
|
bd->TicksPerSecond = perf_frequency;
|
||||||
|
bd->Time = perf_counter;
|
||||||
|
bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||||
|
ImGui_ImplWin32_UpdateKeyboardCodePage();
|
||||||
|
|
||||||
|
// Set platform dependent data in viewport
|
||||||
|
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd;
|
||||||
|
IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch
|
||||||
|
|
||||||
|
// Dynamically load XInput library
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
bd->WantUpdateHasGamepad = true;
|
||||||
|
const char* xinput_dll_names[] =
|
||||||
|
{
|
||||||
|
"xinput1_4.dll", // Windows 8+
|
||||||
|
"xinput1_3.dll", // DirectX SDK
|
||||||
|
"xinput9_1_0.dll", // Windows Vista, Windows 7
|
||||||
|
"xinput1_2.dll", // DirectX SDK
|
||||||
|
"xinput1_1.dll" // DirectX SDK
|
||||||
|
};
|
||||||
|
for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++)
|
||||||
|
if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n]))
|
||||||
|
{
|
||||||
|
bd->XInputDLL = dll;
|
||||||
|
bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities");
|
||||||
|
bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd)
|
||||||
|
{
|
||||||
|
return ImGui_ImplWin32_InitEx(hwnd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd)
|
||||||
|
{
|
||||||
|
// OpenGL needs CS_OWNDC
|
||||||
|
return ImGui_ImplWin32_InitEx(hwnd, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWin32_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Unload XInput library
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
if (bd->XInputDLL)
|
||||||
|
::FreeLibrary(bd->XInputDLL);
|
||||||
|
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
|
||||||
|
io.BackendPlatformName = nullptr;
|
||||||
|
io.BackendPlatformUserData = nullptr;
|
||||||
|
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplWin32_UpdateMouseCursor()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||||
|
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
||||||
|
{
|
||||||
|
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||||
|
::SetCursor(nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Show OS mouse cursor
|
||||||
|
LPTSTR win32_cursor = IDC_ARROW;
|
||||||
|
switch (imgui_cursor)
|
||||||
|
{
|
||||||
|
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
|
||||||
|
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
|
||||||
|
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
|
||||||
|
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
|
||||||
|
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
|
||||||
|
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
|
||||||
|
case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break;
|
||||||
|
}
|
||||||
|
::SetCursor(::LoadCursor(nullptr, win32_cursor));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsVkDown(int vk)
|
||||||
|
{
|
||||||
|
return (::GetKeyState(vk) & 0x8000) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode = -1)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(key, down);
|
||||||
|
io.SetKeyEventNativeData(key, native_keycode, native_scancode); // To support legacy indexing (<1.87 user code)
|
||||||
|
IM_UNUSED(native_scancode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
|
||||||
|
{
|
||||||
|
// Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one.
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && !IsVkDown(VK_LSHIFT))
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, false, VK_LSHIFT);
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_RightShift) && !IsVkDown(VK_RSHIFT))
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, false, VK_RSHIFT);
|
||||||
|
|
||||||
|
// Sometimes WM_KEYUP for Win key is not passed down to the app (e.g. for Win+V on some setups, according to GLFW).
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_LeftSuper) && !IsVkDown(VK_LWIN))
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftSuper, false, VK_LWIN);
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_RightSuper) && !IsVkDown(VK_RWIN))
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightSuper, false, VK_RWIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_UpdateKeyModifiers()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL));
|
||||||
|
io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT));
|
||||||
|
io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU));
|
||||||
|
io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplWin32_UpdateMouseData()
|
||||||
|
{
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(bd->hWnd != 0);
|
||||||
|
|
||||||
|
HWND focused_window = ::GetForegroundWindow();
|
||||||
|
const bool is_app_focused = (focused_window == bd->hWnd);
|
||||||
|
if (is_app_focused)
|
||||||
|
{
|
||||||
|
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||||
|
if (io.WantSetMousePos)
|
||||||
|
{
|
||||||
|
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
||||||
|
if (::ClientToScreen(bd->hWnd, &pos))
|
||||||
|
::SetCursorPos(pos.x, pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
|
||||||
|
// This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE
|
||||||
|
if (!io.WantSetMousePos && bd->MouseTrackedArea == 0)
|
||||||
|
{
|
||||||
|
POINT pos;
|
||||||
|
if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos))
|
||||||
|
io.AddMousePosEvent((float)pos.x, (float)pos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gamepad navigation mapping
|
||||||
|
static void ImGui_ImplWin32_UpdateGamepads()
|
||||||
|
{
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
//if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
|
||||||
|
// return;
|
||||||
|
|
||||||
|
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
|
||||||
|
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
|
||||||
|
if (bd->WantUpdateHasGamepad)
|
||||||
|
{
|
||||||
|
XINPUT_CAPABILITIES caps = {};
|
||||||
|
bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false;
|
||||||
|
bd->WantUpdateHasGamepad = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||||
|
XINPUT_STATE xinput_state;
|
||||||
|
XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
|
||||||
|
if (!bd->HasGamepad || bd->XInputGetState == nullptr || bd->XInputGetState(0, &xinput_state) != ERROR_SUCCESS)
|
||||||
|
return;
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||||
|
|
||||||
|
#define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
|
||||||
|
#define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0); }
|
||||||
|
#define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn)); }
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadStart, XINPUT_GAMEPAD_START);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadBack, XINPUT_GAMEPAD_BACK);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, XINPUT_GAMEPAD_X);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceRight, XINPUT_GAMEPAD_B);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceUp, XINPUT_GAMEPAD_Y);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceDown, XINPUT_GAMEPAD_A);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, XINPUT_GAMEPAD_DPAD_LEFT);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadRight, XINPUT_GAMEPAD_DPAD_RIGHT);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadUp, XINPUT_GAMEPAD_DPAD_UP);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadDown, XINPUT_GAMEPAD_DPAD_DOWN);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL1, XINPUT_GAMEPAD_LEFT_SHOULDER);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR1, XINPUT_GAMEPAD_RIGHT_SHOULDER);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadL2, gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadR2, gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL3, XINPUT_GAMEPAD_LEFT_THUMB);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR3, XINPUT_GAMEPAD_RIGHT_THUMB);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, gamepad.sThumbRX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickRight, gamepad.sThumbRX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickUp, gamepad.sThumbRY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickDown, gamepad.sThumbRY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||||
|
#undef MAP_BUTTON
|
||||||
|
#undef MAP_ANALOG
|
||||||
|
#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplWin32_NewFrame()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplWin32_Init()?");
|
||||||
|
|
||||||
|
// Setup display size (every frame to accommodate for window resizing)
|
||||||
|
RECT rect = { 0, 0, 0, 0 };
|
||||||
|
::GetClientRect(bd->hWnd, &rect);
|
||||||
|
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
|
||||||
|
|
||||||
|
// Setup time step
|
||||||
|
INT64 current_time = 0;
|
||||||
|
::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time);
|
||||||
|
io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond;
|
||||||
|
bd->Time = current_time;
|
||||||
|
|
||||||
|
// Update OS mouse position
|
||||||
|
ImGui_ImplWin32_UpdateMouseData();
|
||||||
|
|
||||||
|
// Process workarounds for known Windows key handling issues
|
||||||
|
ImGui_ImplWin32_ProcessKeyEventsWorkarounds();
|
||||||
|
|
||||||
|
// Update OS mouse cursor with the cursor requested by imgui
|
||||||
|
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
||||||
|
if (bd->LastMouseCursor != mouse_cursor)
|
||||||
|
{
|
||||||
|
bd->LastMouseCursor = mouse_cursor;
|
||||||
|
ImGui_ImplWin32_UpdateMouseCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update game controllers (if enabled and available)
|
||||||
|
ImGui_ImplWin32_UpdateGamepads();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255)
|
||||||
|
#define IM_VK_KEYPAD_ENTER (VK_RETURN + 256)
|
||||||
|
|
||||||
|
// Map VK_xxx to ImGuiKey_xxx.
|
||||||
|
static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
|
||||||
|
{
|
||||||
|
switch (wParam)
|
||||||
|
{
|
||||||
|
case VK_TAB: return ImGuiKey_Tab;
|
||||||
|
case VK_LEFT: return ImGuiKey_LeftArrow;
|
||||||
|
case VK_RIGHT: return ImGuiKey_RightArrow;
|
||||||
|
case VK_UP: return ImGuiKey_UpArrow;
|
||||||
|
case VK_DOWN: return ImGuiKey_DownArrow;
|
||||||
|
case VK_PRIOR: return ImGuiKey_PageUp;
|
||||||
|
case VK_NEXT: return ImGuiKey_PageDown;
|
||||||
|
case VK_HOME: return ImGuiKey_Home;
|
||||||
|
case VK_END: return ImGuiKey_End;
|
||||||
|
case VK_INSERT: return ImGuiKey_Insert;
|
||||||
|
case VK_DELETE: return ImGuiKey_Delete;
|
||||||
|
case VK_BACK: return ImGuiKey_Backspace;
|
||||||
|
case VK_SPACE: return ImGuiKey_Space;
|
||||||
|
case VK_RETURN: return ImGuiKey_Enter;
|
||||||
|
case VK_ESCAPE: return ImGuiKey_Escape;
|
||||||
|
case VK_OEM_7: return ImGuiKey_Apostrophe;
|
||||||
|
case VK_OEM_COMMA: return ImGuiKey_Comma;
|
||||||
|
case VK_OEM_MINUS: return ImGuiKey_Minus;
|
||||||
|
case VK_OEM_PERIOD: return ImGuiKey_Period;
|
||||||
|
case VK_OEM_2: return ImGuiKey_Slash;
|
||||||
|
case VK_OEM_1: return ImGuiKey_Semicolon;
|
||||||
|
case VK_OEM_PLUS: return ImGuiKey_Equal;
|
||||||
|
case VK_OEM_4: return ImGuiKey_LeftBracket;
|
||||||
|
case VK_OEM_5: return ImGuiKey_Backslash;
|
||||||
|
case VK_OEM_6: return ImGuiKey_RightBracket;
|
||||||
|
case VK_OEM_3: return ImGuiKey_GraveAccent;
|
||||||
|
case VK_CAPITAL: return ImGuiKey_CapsLock;
|
||||||
|
case VK_SCROLL: return ImGuiKey_ScrollLock;
|
||||||
|
case VK_NUMLOCK: return ImGuiKey_NumLock;
|
||||||
|
case VK_SNAPSHOT: return ImGuiKey_PrintScreen;
|
||||||
|
case VK_PAUSE: return ImGuiKey_Pause;
|
||||||
|
case VK_NUMPAD0: return ImGuiKey_Keypad0;
|
||||||
|
case VK_NUMPAD1: return ImGuiKey_Keypad1;
|
||||||
|
case VK_NUMPAD2: return ImGuiKey_Keypad2;
|
||||||
|
case VK_NUMPAD3: return ImGuiKey_Keypad3;
|
||||||
|
case VK_NUMPAD4: return ImGuiKey_Keypad4;
|
||||||
|
case VK_NUMPAD5: return ImGuiKey_Keypad5;
|
||||||
|
case VK_NUMPAD6: return ImGuiKey_Keypad6;
|
||||||
|
case VK_NUMPAD7: return ImGuiKey_Keypad7;
|
||||||
|
case VK_NUMPAD8: return ImGuiKey_Keypad8;
|
||||||
|
case VK_NUMPAD9: return ImGuiKey_Keypad9;
|
||||||
|
case VK_DECIMAL: return ImGuiKey_KeypadDecimal;
|
||||||
|
case VK_DIVIDE: return ImGuiKey_KeypadDivide;
|
||||||
|
case VK_MULTIPLY: return ImGuiKey_KeypadMultiply;
|
||||||
|
case VK_SUBTRACT: return ImGuiKey_KeypadSubtract;
|
||||||
|
case VK_ADD: return ImGuiKey_KeypadAdd;
|
||||||
|
case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter;
|
||||||
|
case VK_LSHIFT: return ImGuiKey_LeftShift;
|
||||||
|
case VK_LCONTROL: return ImGuiKey_LeftCtrl;
|
||||||
|
case VK_LMENU: return ImGuiKey_LeftAlt;
|
||||||
|
case VK_LWIN: return ImGuiKey_LeftSuper;
|
||||||
|
case VK_RSHIFT: return ImGuiKey_RightShift;
|
||||||
|
case VK_RCONTROL: return ImGuiKey_RightCtrl;
|
||||||
|
case VK_RMENU: return ImGuiKey_RightAlt;
|
||||||
|
case VK_RWIN: return ImGuiKey_RightSuper;
|
||||||
|
case VK_APPS: return ImGuiKey_Menu;
|
||||||
|
case '0': return ImGuiKey_0;
|
||||||
|
case '1': return ImGuiKey_1;
|
||||||
|
case '2': return ImGuiKey_2;
|
||||||
|
case '3': return ImGuiKey_3;
|
||||||
|
case '4': return ImGuiKey_4;
|
||||||
|
case '5': return ImGuiKey_5;
|
||||||
|
case '6': return ImGuiKey_6;
|
||||||
|
case '7': return ImGuiKey_7;
|
||||||
|
case '8': return ImGuiKey_8;
|
||||||
|
case '9': return ImGuiKey_9;
|
||||||
|
case 'A': return ImGuiKey_A;
|
||||||
|
case 'B': return ImGuiKey_B;
|
||||||
|
case 'C': return ImGuiKey_C;
|
||||||
|
case 'D': return ImGuiKey_D;
|
||||||
|
case 'E': return ImGuiKey_E;
|
||||||
|
case 'F': return ImGuiKey_F;
|
||||||
|
case 'G': return ImGuiKey_G;
|
||||||
|
case 'H': return ImGuiKey_H;
|
||||||
|
case 'I': return ImGuiKey_I;
|
||||||
|
case 'J': return ImGuiKey_J;
|
||||||
|
case 'K': return ImGuiKey_K;
|
||||||
|
case 'L': return ImGuiKey_L;
|
||||||
|
case 'M': return ImGuiKey_M;
|
||||||
|
case 'N': return ImGuiKey_N;
|
||||||
|
case 'O': return ImGuiKey_O;
|
||||||
|
case 'P': return ImGuiKey_P;
|
||||||
|
case 'Q': return ImGuiKey_Q;
|
||||||
|
case 'R': return ImGuiKey_R;
|
||||||
|
case 'S': return ImGuiKey_S;
|
||||||
|
case 'T': return ImGuiKey_T;
|
||||||
|
case 'U': return ImGuiKey_U;
|
||||||
|
case 'V': return ImGuiKey_V;
|
||||||
|
case 'W': return ImGuiKey_W;
|
||||||
|
case 'X': return ImGuiKey_X;
|
||||||
|
case 'Y': return ImGuiKey_Y;
|
||||||
|
case 'Z': return ImGuiKey_Z;
|
||||||
|
case VK_F1: return ImGuiKey_F1;
|
||||||
|
case VK_F2: return ImGuiKey_F2;
|
||||||
|
case VK_F3: return ImGuiKey_F3;
|
||||||
|
case VK_F4: return ImGuiKey_F4;
|
||||||
|
case VK_F5: return ImGuiKey_F5;
|
||||||
|
case VK_F6: return ImGuiKey_F6;
|
||||||
|
case VK_F7: return ImGuiKey_F7;
|
||||||
|
case VK_F8: return ImGuiKey_F8;
|
||||||
|
case VK_F9: return ImGuiKey_F9;
|
||||||
|
case VK_F10: return ImGuiKey_F10;
|
||||||
|
case VK_F11: return ImGuiKey_F11;
|
||||||
|
case VK_F12: return ImGuiKey_F12;
|
||||||
|
case VK_F13: return ImGuiKey_F13;
|
||||||
|
case VK_F14: return ImGuiKey_F14;
|
||||||
|
case VK_F15: return ImGuiKey_F15;
|
||||||
|
case VK_F16: return ImGuiKey_F16;
|
||||||
|
case VK_F17: return ImGuiKey_F17;
|
||||||
|
case VK_F18: return ImGuiKey_F18;
|
||||||
|
case VK_F19: return ImGuiKey_F19;
|
||||||
|
case VK_F20: return ImGuiKey_F20;
|
||||||
|
case VK_F21: return ImGuiKey_F21;
|
||||||
|
case VK_F22: return ImGuiKey_F22;
|
||||||
|
case VK_F23: return ImGuiKey_F23;
|
||||||
|
case VK_F24: return ImGuiKey_F24;
|
||||||
|
case VK_BROWSER_BACK: return ImGuiKey_AppBack;
|
||||||
|
case VK_BROWSER_FORWARD: return ImGuiKey_AppForward;
|
||||||
|
default: return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
|
||||||
|
#ifndef WM_MOUSEHWHEEL
|
||||||
|
#define WM_MOUSEHWHEEL 0x020E
|
||||||
|
#endif
|
||||||
|
#ifndef DBT_DEVNODES_CHANGED
|
||||||
|
#define DBT_DEVNODES_CHANGED 0x0007
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
|
||||||
|
// Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
|
||||||
|
// When implementing your own backend, you can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if Dear ImGui wants to use your inputs.
|
||||||
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
||||||
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
||||||
|
// Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
|
||||||
|
// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
|
||||||
|
// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
|
||||||
|
#if 0
|
||||||
|
// Copy this line into your .cpp file to forward declare the function.
|
||||||
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages
|
||||||
|
// Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this.
|
||||||
|
static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()
|
||||||
|
{
|
||||||
|
LPARAM extra_info = ::GetMessageExtraInfo();
|
||||||
|
if ((extra_info & 0xFFFFFF80) == 0xFF515700)
|
||||||
|
return ImGuiMouseSource_Pen;
|
||||||
|
if ((extra_info & 0xFFFFFF80) == 0xFF515780)
|
||||||
|
return ImGuiMouseSource_TouchScreen;
|
||||||
|
return ImGuiMouseSource_Mouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (ImGui::GetCurrentContext() == nullptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
|
||||||
|
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
|
case WM_NCMOUSEMOVE:
|
||||||
|
{
|
||||||
|
// We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
|
const int area = (msg == WM_MOUSEMOVE) ? 1 : 2;
|
||||||
|
bd->MouseHwnd = hwnd;
|
||||||
|
if (bd->MouseTrackedArea != area)
|
||||||
|
{
|
||||||
|
TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
|
||||||
|
TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 };
|
||||||
|
if (bd->MouseTrackedArea != 0)
|
||||||
|
::TrackMouseEvent(&tme_cancel);
|
||||||
|
::TrackMouseEvent(&tme_track);
|
||||||
|
bd->MouseTrackedArea = area;
|
||||||
|
}
|
||||||
|
POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) };
|
||||||
|
if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates.
|
||||||
|
break;
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
|
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_MOUSELEAVE:
|
||||||
|
case WM_NCMOUSELEAVE:
|
||||||
|
{
|
||||||
|
const int area = (msg == WM_MOUSELEAVE) ? 1 : 2;
|
||||||
|
if (bd->MouseTrackedArea == area)
|
||||||
|
{
|
||||||
|
if (bd->MouseHwnd == hwnd)
|
||||||
|
bd->MouseHwnd = nullptr;
|
||||||
|
bd->MouseTrackedArea = 0;
|
||||||
|
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
||||||
|
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
||||||
|
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
|
||||||
|
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
|
||||||
|
{
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
|
int button = 0;
|
||||||
|
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
|
||||||
|
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
|
||||||
|
if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
|
||||||
|
if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
|
||||||
|
if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr)
|
||||||
|
::SetCapture(hwnd);
|
||||||
|
bd->MouseButtonsDown |= 1 << button;
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
|
io.AddMouseButtonEvent(button, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
case WM_XBUTTONUP:
|
||||||
|
{
|
||||||
|
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
|
||||||
|
int button = 0;
|
||||||
|
if (msg == WM_LBUTTONUP) { button = 0; }
|
||||||
|
if (msg == WM_RBUTTONUP) { button = 1; }
|
||||||
|
if (msg == WM_MBUTTONUP) { button = 2; }
|
||||||
|
if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
|
||||||
|
bd->MouseButtonsDown &= ~(1 << button);
|
||||||
|
if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd)
|
||||||
|
::ReleaseCapture();
|
||||||
|
io.AddMouseSourceEvent(mouse_source);
|
||||||
|
io.AddMouseButtonEvent(button, false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_MOUSEWHEEL:
|
||||||
|
io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
|
||||||
|
return 0;
|
||||||
|
case WM_MOUSEHWHEEL:
|
||||||
|
io.AddMouseWheelEvent(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f);
|
||||||
|
return 0;
|
||||||
|
case WM_KEYDOWN:
|
||||||
|
case WM_KEYUP:
|
||||||
|
case WM_SYSKEYDOWN:
|
||||||
|
case WM_SYSKEYUP:
|
||||||
|
{
|
||||||
|
const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN);
|
||||||
|
if (wParam < 256)
|
||||||
|
{
|
||||||
|
// Submit modifiers
|
||||||
|
ImGui_ImplWin32_UpdateKeyModifiers();
|
||||||
|
|
||||||
|
// Obtain virtual key code
|
||||||
|
// (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.)
|
||||||
|
int vk = (int)wParam;
|
||||||
|
if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
|
||||||
|
vk = IM_VK_KEYPAD_ENTER;
|
||||||
|
const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
|
||||||
|
const int scancode = (int)LOBYTE(HIWORD(lParam));
|
||||||
|
|
||||||
|
// Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event.
|
||||||
|
if (key == ImGuiKey_PrintScreen && !is_key_down)
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(key, true, vk, scancode);
|
||||||
|
|
||||||
|
// Submit key event
|
||||||
|
if (key != ImGuiKey_None)
|
||||||
|
ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode);
|
||||||
|
|
||||||
|
// Submit individual left/right modifier events
|
||||||
|
if (vk == VK_SHIFT)
|
||||||
|
{
|
||||||
|
// Important: Shift keys tend to get stuck when pressed together, missing key-up events are corrected in ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
|
||||||
|
if (IsVkDown(VK_LSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); }
|
||||||
|
if (IsVkDown(VK_RSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); }
|
||||||
|
}
|
||||||
|
else if (vk == VK_CONTROL)
|
||||||
|
{
|
||||||
|
if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftCtrl, is_key_down, VK_LCONTROL, scancode); }
|
||||||
|
if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightCtrl, is_key_down, VK_RCONTROL, scancode); }
|
||||||
|
}
|
||||||
|
else if (vk == VK_MENU)
|
||||||
|
{
|
||||||
|
if (IsVkDown(VK_LMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); }
|
||||||
|
if (IsVkDown(VK_RMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_SETFOCUS:
|
||||||
|
case WM_KILLFOCUS:
|
||||||
|
io.AddFocusEvent(msg == WM_SETFOCUS);
|
||||||
|
return 0;
|
||||||
|
case WM_INPUTLANGCHANGE:
|
||||||
|
ImGui_ImplWin32_UpdateKeyboardCodePage();
|
||||||
|
return 0;
|
||||||
|
case WM_CHAR:
|
||||||
|
if (::IsWindowUnicode(hwnd))
|
||||||
|
{
|
||||||
|
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
|
||||||
|
if (wParam > 0 && wParam < 0x10000)
|
||||||
|
io.AddInputCharacterUTF16((unsigned short)wParam);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wchar_t wch = 0;
|
||||||
|
::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1);
|
||||||
|
io.AddInputCharacter(wch);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
case WM_SETCURSOR:
|
||||||
|
// This is required to restore cursor when transitioning from e.g resize borders to client area.
|
||||||
|
if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
case WM_DEVICECHANGE:
|
||||||
|
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||||
|
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
|
||||||
|
bd->WantUpdateHasGamepad = true;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
// DPI-related helpers (optional)
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
// - Use to enable DPI awareness without having to create an application manifest.
|
||||||
|
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
|
||||||
|
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
|
||||||
|
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
|
||||||
|
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
// This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable.
|
||||||
|
// ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically.
|
||||||
|
// If you are trying to implement your own backend for your own engine, you may ignore that noise.
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Perform our own check with RtlVerifyVersionInfo() instead of using functions from <VersionHelpers.h> as they
|
||||||
|
// require a manifest to be functional for checks above 8.1. See https://github.com/ocornut/imgui/issues/4200
|
||||||
|
static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD)
|
||||||
|
{
|
||||||
|
typedef LONG(WINAPI* PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG);
|
||||||
|
static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = nullptr;
|
||||||
|
if (RtlVerifyVersionInfoFn == nullptr)
|
||||||
|
if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll"))
|
||||||
|
RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo");
|
||||||
|
if (RtlVerifyVersionInfoFn == nullptr)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
RTL_OSVERSIONINFOEXW versionInfo = { };
|
||||||
|
ULONGLONG conditionMask = 0;
|
||||||
|
versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
|
||||||
|
versionInfo.dwMajorVersion = major;
|
||||||
|
versionInfo.dwMinorVersion = minor;
|
||||||
|
VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||||
|
VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||||
|
return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _IsWindowsVistaOrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA
|
||||||
|
#define _IsWindows8OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8
|
||||||
|
#define _IsWindows8Point1OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE
|
||||||
|
#define _IsWindows10OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10
|
||||||
|
|
||||||
|
#ifndef DPI_ENUMS_DECLARED
|
||||||
|
typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
|
||||||
|
typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
|
||||||
|
#endif
|
||||||
|
#ifndef _DPI_AWARENESS_CONTEXTS_
|
||||||
|
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
|
||||||
|
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3
|
||||||
|
#endif
|
||||||
|
#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
|
||||||
|
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4
|
||||||
|
#endif
|
||||||
|
typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+
|
||||||
|
typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+
|
||||||
|
typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update)
|
||||||
|
|
||||||
|
// Helper function to enable DPI awareness without setting up a manifest
|
||||||
|
void ImGui_ImplWin32_EnableDpiAwareness()
|
||||||
|
{
|
||||||
|
if (_IsWindows10OrGreater())
|
||||||
|
{
|
||||||
|
static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
|
||||||
|
if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
|
||||||
|
{
|
||||||
|
SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_IsWindows8Point1OrGreater())
|
||||||
|
{
|
||||||
|
static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
|
||||||
|
if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"))
|
||||||
|
{
|
||||||
|
SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if _WIN32_WINNT >= 0x0600
|
||||||
|
::SetProcessDPIAware();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(NOGDI)
|
||||||
|
#pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps(). MinGW will require linking with '-lgdi32'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor)
|
||||||
|
{
|
||||||
|
UINT xdpi = 96, ydpi = 96;
|
||||||
|
if (_IsWindows8Point1OrGreater())
|
||||||
|
{
|
||||||
|
static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
|
||||||
|
static PFN_GetDpiForMonitor GetDpiForMonitorFn = nullptr;
|
||||||
|
if (GetDpiForMonitorFn == nullptr && shcore_dll != nullptr)
|
||||||
|
GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor");
|
||||||
|
if (GetDpiForMonitorFn != nullptr)
|
||||||
|
{
|
||||||
|
GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
|
||||||
|
IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
|
||||||
|
return xdpi / 96.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef NOGDI
|
||||||
|
const HDC dc = ::GetDC(nullptr);
|
||||||
|
xdpi = ::GetDeviceCaps(dc, LOGPIXELSX);
|
||||||
|
ydpi = ::GetDeviceCaps(dc, LOGPIXELSY);
|
||||||
|
IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
|
||||||
|
::ReleaseDC(nullptr, dc);
|
||||||
|
#endif
|
||||||
|
return xdpi / 96.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd)
|
||||||
|
{
|
||||||
|
HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
|
return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
// Transparency related helpers (optional)
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma comment(lib, "dwmapi") // Link with dwmapi.lib. MinGW will require linking with '-ldwmapi'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// [experimental]
|
||||||
|
// Borrowed from GLFW's function updateFramebufferTransparency() in src/win32_window.c
|
||||||
|
// (the Dwm* functions are Vista era functions but we are borrowing logic from GLFW)
|
||||||
|
void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd)
|
||||||
|
{
|
||||||
|
if (!_IsWindowsVistaOrGreater())
|
||||||
|
return;
|
||||||
|
|
||||||
|
BOOL composition;
|
||||||
|
if (FAILED(::DwmIsCompositionEnabled(&composition)) || !composition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BOOL opaque;
|
||||||
|
DWORD color;
|
||||||
|
if (_IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque))
|
||||||
|
{
|
||||||
|
HRGN region = ::CreateRectRgn(0, 0, -1, -1);
|
||||||
|
DWM_BLURBEHIND bb = {};
|
||||||
|
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
|
||||||
|
bb.hRgnBlur = region;
|
||||||
|
bb.fEnable = TRUE;
|
||||||
|
::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
|
||||||
|
::DeleteObject(region);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DWM_BLURBEHIND bb = {};
|
||||||
|
bb.dwFlags = DWM_BB_ENABLE;
|
||||||
|
::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
52
backends/imgui_impl_win32.h
Normal file
52
backends/imgui_impl_win32.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications)
|
||||||
|
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
||||||
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// Learn about Dear ImGui:
|
||||||
|
// - FAQ https://dearimgui.com/faq
|
||||||
|
// - Getting Started https://dearimgui.com/getting-started
|
||||||
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||||
|
// - Introduction, links and more at the top of imgui.cpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
|
||||||
|
|
||||||
|
// Win32 message handler your application need to call.
|
||||||
|
// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on <windows.h> from this helper.
|
||||||
|
// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
|
||||||
|
// - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DPI-related helpers (optional)
|
||||||
|
// - Use to enable DPI awareness without having to create an application manifest.
|
||||||
|
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
|
||||||
|
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
|
||||||
|
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
|
||||||
|
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
|
||||||
|
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
|
||||||
|
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
|
||||||
|
|
||||||
|
// Transparency related helpers (optional) [experimental]
|
||||||
|
// - Use to enable alpha compositing transparency with the desktop.
|
||||||
|
// - Use together with e.g. clearing your framebuffer with zero-alpha.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
129
imconfig.h
Normal file
129
imconfig.h
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||||
|
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||||
|
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||||
|
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||||
|
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||||
|
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||||
|
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//---- Define assertion handler. Defaults to calling assert().
|
||||||
|
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||||
|
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||||
|
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||||
|
|
||||||
|
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||||
|
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||||
|
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||||
|
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||||
|
//#define IMGUI_API __declspec( dllexport )
|
||||||
|
//#define IMGUI_API __declspec( dllimport )
|
||||||
|
|
||||||
|
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||||
|
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||||
|
|
||||||
|
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||||
|
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||||
|
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||||
|
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||||
|
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||||
|
|
||||||
|
//---- Don't implement some functions to reduce linkage requirements.
|
||||||
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||||
|
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||||
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||||
|
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||||
|
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||||
|
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||||
|
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||||
|
|
||||||
|
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||||
|
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||||
|
|
||||||
|
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||||
|
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
|
||||||
|
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||||
|
//#define IMGUI_USE_WCHAR32
|
||||||
|
|
||||||
|
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||||
|
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||||
|
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||||
|
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||||
|
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||||
|
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||||
|
|
||||||
|
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||||
|
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||||
|
//#define IMGUI_USE_STB_SPRINTF
|
||||||
|
|
||||||
|
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||||
|
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||||
|
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||||
|
//#define IMGUI_ENABLE_FREETYPE
|
||||||
|
|
||||||
|
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
|
||||||
|
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
|
||||||
|
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||||
|
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||||
|
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||||
|
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||||
|
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||||
|
|
||||||
|
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||||
|
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||||
|
/*
|
||||||
|
#define IM_VEC2_CLASS_EXTRA \
|
||||||
|
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
|
||||||
|
operator MyVec2() const { return MyVec2(x,y); }
|
||||||
|
|
||||||
|
#define IM_VEC4_CLASS_EXTRA \
|
||||||
|
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
||||||
|
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||||
|
*/
|
||||||
|
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||||
|
//#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
|
||||||
|
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||||
|
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||||
|
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||||
|
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||||
|
//#define ImDrawIdx unsigned int
|
||||||
|
|
||||||
|
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||||
|
//struct ImDrawList;
|
||||||
|
//struct ImDrawCmd;
|
||||||
|
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||||
|
//#define ImDrawCallback MyImDrawCallback
|
||||||
|
|
||||||
|
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||||
|
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||||
|
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||||
|
//#define IM_DEBUG_BREAK __debugbreak()
|
||||||
|
|
||||||
|
//---- Debug Tools: Enable slower asserts
|
||||||
|
//#define IMGUI_DEBUG_PARANOID
|
||||||
|
|
||||||
|
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||||
|
/*
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||||
|
}
|
||||||
|
*/
|
8635
imgui_demo.cpp
Normal file
8635
imgui_demo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
5332
imgui_draw.cpp
Normal file
5332
imgui_draw.cpp
Normal file
File diff suppressed because it is too large
Load Diff
792
imgui_freetype.cpp
Normal file
792
imgui_freetype.cpp
Normal file
@ -0,0 +1,792 @@
|
|||||||
|
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||||
|
// (code)
|
||||||
|
|
||||||
|
// Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
|
||||||
|
// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut.
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
|
||||||
|
// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
|
||||||
|
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
|
||||||
|
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format.
|
||||||
|
// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
|
||||||
|
// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'.
|
||||||
|
// renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
|
||||||
|
// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
|
||||||
|
// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
|
||||||
|
// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
|
||||||
|
// 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
|
||||||
|
// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
|
||||||
|
// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
|
||||||
|
// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
|
||||||
|
// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
|
||||||
|
// 2017/09/26: fixes for imgui internal changes.
|
||||||
|
// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
|
||||||
|
// 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
|
||||||
|
|
||||||
|
// About Gamma Correct Blending:
|
||||||
|
// - FreeType assumes blending in linear space rather than gamma space.
|
||||||
|
// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
|
||||||
|
// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
|
||||||
|
// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
|
||||||
|
|
||||||
|
// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
|
||||||
|
|
||||||
|
#include "imgui_freetype.h"
|
||||||
|
#include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*,
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <freetype/include/ft2build.h>
|
||||||
|
#include <freetype/include/freetype/freetype.h>// <freetype/freetype.h
|
||||||
|
#include <freetype/include/freetype/ftmodapi.h > // <freetype/ftmodapi.h>
|
||||||
|
#include <freetype/include/freetype/ftglyph.h> // <freetype/ftglyph.h>
|
||||||
|
#include <freetype/include/freetype/ftsynth.h> // <freetype/ftsynth.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
||||||
|
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
||||||
|
#pragma GCC diagnostic ignored "-Wsubobject-linkage" // warning: 'xxxx' has a field ‘xxxx’ whose type uses the anonymous namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Data
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Default memory allocators
|
||||||
|
static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
|
||||||
|
static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
|
||||||
|
|
||||||
|
// Current memory allocators
|
||||||
|
static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
|
||||||
|
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
|
||||||
|
static void* GImGuiFreeTypeAllocatorUserData = NULL;
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Code
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Glyph metrics:
|
||||||
|
// --------------
|
||||||
|
//
|
||||||
|
// xmin xmax
|
||||||
|
// | |
|
||||||
|
// |<-------- width -------->|
|
||||||
|
// | |
|
||||||
|
// | +-------------------------+----------------- ymax
|
||||||
|
// | | ggggggggg ggggg | ^ ^
|
||||||
|
// | | g:::::::::ggg::::g | | |
|
||||||
|
// | | g:::::::::::::::::g | | |
|
||||||
|
// | | g::::::ggggg::::::gg | | |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// offsetX -|-------->| g:::::g g:::::g | offsetY |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// | | g::::::g g:::::g | | |
|
||||||
|
// | | g:::::::ggggg:::::g | | |
|
||||||
|
// | | g::::::::::::::::g | | height
|
||||||
|
// | | gg::::::::::::::g | | |
|
||||||
|
// baseline ---*---------|---- gggggggg::::::g-----*-------- |
|
||||||
|
// / | | g:::::g | |
|
||||||
|
// origin | | gggggg g:::::g | |
|
||||||
|
// | | g:::::gg gg:::::g | |
|
||||||
|
// | | g::::::ggg:::::::g | |
|
||||||
|
// | | gg:::::::::::::g | |
|
||||||
|
// | | ggg::::::ggg | |
|
||||||
|
// | | gggggg | v
|
||||||
|
// | +-------------------------+----------------- ymin
|
||||||
|
// | |
|
||||||
|
// |------------- advanceX ----------->|
|
||||||
|
|
||||||
|
// A structure that describe a glyph.
|
||||||
|
struct GlyphInfo
|
||||||
|
{
|
||||||
|
int Width; // Glyph's width in pixels.
|
||||||
|
int Height; // Glyph's height in pixels.
|
||||||
|
FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph.
|
||||||
|
FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0.
|
||||||
|
float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
|
||||||
|
bool IsColored; // The glyph is colored
|
||||||
|
};
|
||||||
|
|
||||||
|
// Font parameters and metrics.
|
||||||
|
struct FontInfo
|
||||||
|
{
|
||||||
|
uint32_t PixelHeight; // Size this font was generated with.
|
||||||
|
float Ascender; // The pixel extents above the baseline in pixels (typically positive).
|
||||||
|
float Descender; // The extents below the baseline in pixels (typically negative).
|
||||||
|
float LineSpacing; // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate.
|
||||||
|
float LineGap; // The spacing in pixels between one row's descent and the next row's ascent.
|
||||||
|
float MaxAdvanceWidth; // This field gives the maximum horizontal cursor advance for all glyphs in the font.
|
||||||
|
};
|
||||||
|
|
||||||
|
// FreeType glyph rasterizer.
|
||||||
|
// NB: No ctor/dtor, explicitly call Init()/Shutdown()
|
||||||
|
struct FreeTypeFont
|
||||||
|
{
|
||||||
|
bool InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags); // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime.
|
||||||
|
void CloseFont();
|
||||||
|
void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
|
||||||
|
const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
|
||||||
|
const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
|
||||||
|
void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL);
|
||||||
|
~FreeTypeFont() { CloseFont(); }
|
||||||
|
|
||||||
|
// [Internals]
|
||||||
|
FontInfo Info; // Font descriptor of the current font.
|
||||||
|
FT_Face Face;
|
||||||
|
unsigned int UserFlags; // = ImFontConfig::RasterizerFlags
|
||||||
|
FT_Int32 LoadFlags;
|
||||||
|
FT_Render_Mode RenderMode;
|
||||||
|
};
|
||||||
|
|
||||||
|
// From SDL_ttf: Handy routines for converting from fixed point
|
||||||
|
#define FT_CEIL(X) (((X + 63) & -64) / 64)
|
||||||
|
|
||||||
|
bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags)
|
||||||
|
{
|
||||||
|
FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Convert to FreeType flags (NB: Bold and Oblique are processed separately)
|
||||||
|
UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags;
|
||||||
|
|
||||||
|
LoadFlags = 0;
|
||||||
|
if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0)
|
||||||
|
LoadFlags |= FT_LOAD_NO_BITMAP;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting)
|
||||||
|
LoadFlags |= FT_LOAD_NO_HINTING;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint)
|
||||||
|
LoadFlags |= FT_LOAD_NO_AUTOHINT;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint)
|
||||||
|
LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting)
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_LIGHT;
|
||||||
|
else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting)
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_MONO;
|
||||||
|
else
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_NORMAL;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome)
|
||||||
|
RenderMode = FT_RENDER_MODE_MONO;
|
||||||
|
else
|
||||||
|
RenderMode = FT_RENDER_MODE_NORMAL;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
|
||||||
|
LoadFlags |= FT_LOAD_COLOR;
|
||||||
|
|
||||||
|
memset(&Info, 0, sizeof(Info));
|
||||||
|
SetPixelHeight((uint32_t)cfg.SizePixels);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::CloseFont()
|
||||||
|
{
|
||||||
|
if (Face)
|
||||||
|
{
|
||||||
|
FT_Done_Face(Face);
|
||||||
|
Face = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::SetPixelHeight(int pixel_height)
|
||||||
|
{
|
||||||
|
// Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
|
||||||
|
// is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
|
||||||
|
// NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
|
||||||
|
FT_Size_RequestRec req;
|
||||||
|
req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
|
||||||
|
req.width = 0;
|
||||||
|
req.height = (uint32_t)pixel_height * 64;
|
||||||
|
req.horiResolution = 0;
|
||||||
|
req.vertResolution = 0;
|
||||||
|
FT_Request_Size(Face, &req);
|
||||||
|
|
||||||
|
// Update font info
|
||||||
|
FT_Size_Metrics metrics = Face->size->metrics;
|
||||||
|
Info.PixelHeight = (uint32_t)pixel_height;
|
||||||
|
Info.Ascender = (float)FT_CEIL(metrics.ascender);
|
||||||
|
Info.Descender = (float)FT_CEIL(metrics.descender);
|
||||||
|
Info.LineSpacing = (float)FT_CEIL(metrics.height);
|
||||||
|
Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender);
|
||||||
|
Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance);
|
||||||
|
}
|
||||||
|
|
||||||
|
const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint)
|
||||||
|
{
|
||||||
|
uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint);
|
||||||
|
if (glyph_index == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts.
|
||||||
|
// - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076
|
||||||
|
// - https://github.com/ocornut/imgui/issues/4567
|
||||||
|
// - https://github.com/ocornut/imgui/issues/4566
|
||||||
|
// You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version.
|
||||||
|
FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags);
|
||||||
|
if (error)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Need an outline for this to work
|
||||||
|
FT_GlyphSlot slot = Face->glyph;
|
||||||
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
|
||||||
|
|
||||||
|
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
|
||||||
|
FT_GlyphSlot_Embolden(slot);
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique)
|
||||||
|
{
|
||||||
|
FT_GlyphSlot_Oblique(slot);
|
||||||
|
//FT_BBox bbox;
|
||||||
|
//FT_Outline_Get_BBox(&slot->outline, &bbox);
|
||||||
|
//slot->metrics.width = bbox.xMax - bbox.xMin;
|
||||||
|
//slot->metrics.height = bbox.yMax - bbox.yMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &slot->metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
|
||||||
|
{
|
||||||
|
FT_GlyphSlot slot = Face->glyph;
|
||||||
|
FT_Error error = FT_Render_Glyph(slot, RenderMode);
|
||||||
|
if (error != 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
FT_Bitmap* ft_bitmap = &Face->glyph->bitmap;
|
||||||
|
out_glyph_info->Width = (int)ft_bitmap->width;
|
||||||
|
out_glyph_info->Height = (int)ft_bitmap->rows;
|
||||||
|
out_glyph_info->OffsetX = Face->glyph->bitmap_left;
|
||||||
|
out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
|
||||||
|
out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
|
||||||
|
out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
|
||||||
|
|
||||||
|
return ft_bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
|
||||||
|
{
|
||||||
|
IM_ASSERT(ft_bitmap != NULL);
|
||||||
|
const uint32_t w = ft_bitmap->width;
|
||||||
|
const uint32_t h = ft_bitmap->rows;
|
||||||
|
const uint8_t* src = ft_bitmap->buffer;
|
||||||
|
const uint32_t src_pitch = ft_bitmap->pitch;
|
||||||
|
|
||||||
|
switch (ft_bitmap->pixel_mode)
|
||||||
|
{
|
||||||
|
case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
|
||||||
|
{
|
||||||
|
if (multiply_table == NULL)
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, src[x]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
|
||||||
|
{
|
||||||
|
uint8_t color0 = multiply_table ? multiply_table[0] : 0;
|
||||||
|
uint8_t color1 = multiply_table ? multiply_table[255] : 255;
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
{
|
||||||
|
uint8_t bits = 0;
|
||||||
|
const uint8_t* bits_ptr = src;
|
||||||
|
for (uint32_t x = 0; x < w; x++, bits <<= 1)
|
||||||
|
{
|
||||||
|
if ((x & 7) == 0)
|
||||||
|
bits = *bits_ptr++;
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_PIXEL_MODE_BGRA:
|
||||||
|
{
|
||||||
|
// FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
|
||||||
|
#define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
|
||||||
|
if (multiply_table == NULL)
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||||
|
dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
{
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||||
|
dst[x] = IM_COL32(multiply_table[DE_MULTIPLY(r, a)], multiply_table[DE_MULTIPLY(g, a)], multiply_table[DE_MULTIPLY(b, a)], multiply_table[a]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef DE_MULTIPLY
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
|
||||||
|
#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0)
|
||||||
|
#define STBRP_STATIC
|
||||||
|
#define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#endif
|
||||||
|
#ifdef IMGUI_STB_RECT_PACK_FILENAME
|
||||||
|
#include IMGUI_STB_RECT_PACK_FILENAME
|
||||||
|
#else
|
||||||
|
#include "imstb_rectpack.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ImFontBuildSrcGlyphFT
|
||||||
|
{
|
||||||
|
GlyphInfo Info;
|
||||||
|
uint32_t Codepoint;
|
||||||
|
unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
|
||||||
|
|
||||||
|
ImFontBuildSrcGlyphFT() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImFontBuildSrcDataFT
|
||||||
|
{
|
||||||
|
FreeTypeFont Font;
|
||||||
|
stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position.
|
||||||
|
const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
|
||||||
|
int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
|
||||||
|
int GlyphsHighest; // Highest requested codepoint
|
||||||
|
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
|
||||||
|
ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
|
||||||
|
ImVector<ImFontBuildSrcGlyphFT> GlyphsList;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
|
||||||
|
struct ImFontBuildDstDataFT
|
||||||
|
{
|
||||||
|
int SrcCount; // Number of source fonts targeting this destination font.
|
||||||
|
int GlyphsHighest;
|
||||||
|
int GlyphsCount;
|
||||||
|
ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
|
||||||
|
{
|
||||||
|
IM_ASSERT(atlas->ConfigData.Size > 0);
|
||||||
|
|
||||||
|
ImFontAtlasBuildInit(atlas);
|
||||||
|
|
||||||
|
// Clear atlas
|
||||||
|
atlas->TexID = (ImTextureID)NULL;
|
||||||
|
atlas->TexWidth = atlas->TexHeight = 0;
|
||||||
|
atlas->TexUvScale = ImVec2(0.0f, 0.0f);
|
||||||
|
atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
|
||||||
|
atlas->ClearTexData();
|
||||||
|
|
||||||
|
// Temporary storage for building
|
||||||
|
bool src_load_color = false;
|
||||||
|
ImVector<ImFontBuildSrcDataFT> src_tmp_array;
|
||||||
|
ImVector<ImFontBuildDstDataFT> dst_tmp_array;
|
||||||
|
src_tmp_array.resize(atlas->ConfigData.Size);
|
||||||
|
dst_tmp_array.resize(atlas->Fonts.Size);
|
||||||
|
memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
|
||||||
|
memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
|
||||||
|
|
||||||
|
// 1. Initialize font loading structure, check font data validity
|
||||||
|
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
FreeTypeFont& font_face = src_tmp.Font;
|
||||||
|
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
|
||||||
|
|
||||||
|
// Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
|
||||||
|
src_tmp.DstIndex = -1;
|
||||||
|
for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
|
||||||
|
if (cfg.DstFont == atlas->Fonts[output_i])
|
||||||
|
src_tmp.DstIndex = output_i;
|
||||||
|
IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
|
||||||
|
if (src_tmp.DstIndex == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Load font
|
||||||
|
if (!font_face.InitFont(ft_library, cfg, extra_flags))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Measure highest codepoints
|
||||||
|
src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
|
||||||
|
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||||
|
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
|
||||||
|
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||||
|
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
|
||||||
|
dst_tmp.SrcCount++;
|
||||||
|
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
|
||||||
|
int total_glyphs_count = 0;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||||
|
src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
|
||||||
|
if (dst_tmp.GlyphsSet.Storage.empty())
|
||||||
|
dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);
|
||||||
|
|
||||||
|
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||||
|
for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++)
|
||||||
|
{
|
||||||
|
if (dst_tmp.GlyphsSet.TestBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
|
||||||
|
continue;
|
||||||
|
uint32_t glyph_index = FT_Get_Char_Index(src_tmp.Font.Face, codepoint); // It is actually in the font? (FIXME-OPT: We are not storing the glyph_index..)
|
||||||
|
if (glyph_index == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add to avail set/counters
|
||||||
|
src_tmp.GlyphsCount++;
|
||||||
|
dst_tmp.GlyphsCount++;
|
||||||
|
src_tmp.GlyphsSet.SetBit(codepoint);
|
||||||
|
dst_tmp.GlyphsSet.SetBit(codepoint);
|
||||||
|
total_glyphs_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
|
||||||
|
|
||||||
|
IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32));
|
||||||
|
const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin();
|
||||||
|
const ImU32* it_end = src_tmp.GlyphsSet.Storage.end();
|
||||||
|
for (const ImU32* it = it_begin; it < it_end; it++)
|
||||||
|
if (ImU32 entries_32 = *it)
|
||||||
|
for (ImU32 bit_n = 0; bit_n < 32; bit_n++)
|
||||||
|
if (entries_32 & ((ImU32)1 << bit_n))
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT src_glyph;
|
||||||
|
src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
|
||||||
|
//src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
|
||||||
|
src_tmp.GlyphsList.push_back(src_glyph);
|
||||||
|
}
|
||||||
|
src_tmp.GlyphsSet.Clear();
|
||||||
|
IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
|
||||||
|
}
|
||||||
|
for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
|
||||||
|
dst_tmp_array[dst_i].GlyphsSet.Clear();
|
||||||
|
dst_tmp_array.clear();
|
||||||
|
|
||||||
|
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
|
||||||
|
// (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
|
||||||
|
ImVector<stbrp_rect> buf_rects;
|
||||||
|
buf_rects.resize(total_glyphs_count);
|
||||||
|
memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
|
||||||
|
|
||||||
|
// Allocate temporary rasterization data buffers.
|
||||||
|
// We could not find a way to retrieve accurate glyph size without rendering them.
|
||||||
|
// (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform)
|
||||||
|
// We allocate in chunks of 256 KB to not waste too much extra memory ahead. Hopefully users of FreeType won't mind the temporary allocations.
|
||||||
|
const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024;
|
||||||
|
int buf_bitmap_current_used_bytes = 0;
|
||||||
|
ImVector<unsigned char*> buf_bitmap_buffers;
|
||||||
|
buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
|
||||||
|
|
||||||
|
// 4. Gather glyphs sizes so we can pack them in our virtual canvas.
|
||||||
|
// 8. Render/rasterize font characters into the texture
|
||||||
|
int total_surface = 0;
|
||||||
|
int buf_rects_out_n = 0;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
src_tmp.Rects = &buf_rects[buf_rects_out_n];
|
||||||
|
buf_rects_out_n += src_tmp.GlyphsCount;
|
||||||
|
|
||||||
|
// Compute multiply table if requested
|
||||||
|
const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f);
|
||||||
|
unsigned char multiply_table[256];
|
||||||
|
if (multiply_enabled)
|
||||||
|
ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
|
||||||
|
|
||||||
|
// Gather the sizes of all rectangles we will need to pack
|
||||||
|
const int padding = atlas->TexGlyphPadding;
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
|
||||||
|
|
||||||
|
const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
|
||||||
|
if (metrics == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Render glyph into a bitmap (currently held by FreeType)
|
||||||
|
const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info);
|
||||||
|
if (ft_bitmap == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Allocate new temporary chunk if needed
|
||||||
|
const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
|
||||||
|
if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
|
||||||
|
{
|
||||||
|
buf_bitmap_current_used_bytes = 0;
|
||||||
|
buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
|
||||||
|
}
|
||||||
|
IM_ASSERT(buf_bitmap_current_used_bytes + bitmap_size_in_bytes <= BITMAP_BUFFERS_CHUNK_SIZE); // We could probably allocate custom-sized buffer instead.
|
||||||
|
|
||||||
|
// Blit rasterized pixels to our temporary buffer and keep a pointer to it.
|
||||||
|
src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
|
||||||
|
buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
|
||||||
|
src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : NULL);
|
||||||
|
|
||||||
|
src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
|
||||||
|
src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
|
||||||
|
total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a width for the skyline algorithm, any width!
|
||||||
|
// The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
|
||||||
|
// User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
|
||||||
|
const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
|
||||||
|
atlas->TexHeight = 0;
|
||||||
|
if (atlas->TexDesiredWidth > 0)
|
||||||
|
atlas->TexWidth = atlas->TexDesiredWidth;
|
||||||
|
else
|
||||||
|
atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
|
||||||
|
|
||||||
|
// 5. Start packing
|
||||||
|
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
|
||||||
|
const int TEX_HEIGHT_MAX = 1024 * 32;
|
||||||
|
const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding;
|
||||||
|
ImVector<stbrp_node> pack_nodes;
|
||||||
|
pack_nodes.resize(num_nodes_for_packing_algorithm);
|
||||||
|
stbrp_context pack_context;
|
||||||
|
stbrp_init_target(&pack_context, atlas->TexWidth - atlas->TexGlyphPadding, TEX_HEIGHT_MAX - atlas->TexGlyphPadding, pack_nodes.Data, pack_nodes.Size);
|
||||||
|
ImFontAtlasBuildPackCustomRects(atlas, &pack_context);
|
||||||
|
|
||||||
|
// 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount);
|
||||||
|
|
||||||
|
// Extend texture height and mark missing glyphs as non-packed so we won't render them.
|
||||||
|
// FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||||
|
if (src_tmp.Rects[glyph_i].was_packed)
|
||||||
|
atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Allocate texture
|
||||||
|
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
|
||||||
|
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
|
||||||
|
if (src_load_color)
|
||||||
|
{
|
||||||
|
size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 4;
|
||||||
|
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(tex_size);
|
||||||
|
memset(atlas->TexPixelsRGBA32, 0, tex_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 1;
|
||||||
|
atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(tex_size);
|
||||||
|
memset(atlas->TexPixelsAlpha8, 0, tex_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Copy rasterized font characters back into the main texture
|
||||||
|
// 9. Setup ImFont and glyphs for runtime
|
||||||
|
bool tex_use_colors = false;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// When merging fonts with MergeMode=true:
|
||||||
|
// - We can have multiple input fonts writing into a same destination font.
|
||||||
|
// - dst_font->ConfigData is != from cfg which is our source configuration.
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
ImFont* dst_font = cfg.DstFont;
|
||||||
|
|
||||||
|
const float ascent = src_tmp.Font.Info.Ascender;
|
||||||
|
const float descent = src_tmp.Font.Info.Descender;
|
||||||
|
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
|
||||||
|
const float font_off_x = cfg.GlyphOffset.x;
|
||||||
|
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
|
||||||
|
|
||||||
|
const int padding = atlas->TexGlyphPadding;
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
|
||||||
|
stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
|
||||||
|
IM_ASSERT(pack_rect.was_packed);
|
||||||
|
if (pack_rect.w == 0 && pack_rect.h == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GlyphInfo& info = src_glyph.Info;
|
||||||
|
IM_ASSERT(info.Width + padding <= pack_rect.w);
|
||||||
|
IM_ASSERT(info.Height + padding <= pack_rect.h);
|
||||||
|
const int tx = pack_rect.x + padding;
|
||||||
|
const int ty = pack_rect.y + padding;
|
||||||
|
|
||||||
|
// Register glyph
|
||||||
|
float x0 = info.OffsetX + font_off_x;
|
||||||
|
float y0 = info.OffsetY + font_off_y;
|
||||||
|
float x1 = x0 + info.Width;
|
||||||
|
float y1 = y0 + info.Height;
|
||||||
|
float u0 = (tx) / (float)atlas->TexWidth;
|
||||||
|
float v0 = (ty) / (float)atlas->TexHeight;
|
||||||
|
float u1 = (tx + info.Width) / (float)atlas->TexWidth;
|
||||||
|
float v1 = (ty + info.Height) / (float)atlas->TexHeight;
|
||||||
|
dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX);
|
||||||
|
|
||||||
|
ImFontGlyph* dst_glyph = &dst_font->Glyphs.back();
|
||||||
|
IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint);
|
||||||
|
if (src_glyph.Info.IsColored)
|
||||||
|
dst_glyph->Colored = tex_use_colors = true;
|
||||||
|
|
||||||
|
// Blit from temporary buffer to final texture
|
||||||
|
size_t blit_src_stride = (size_t)src_glyph.Info.Width;
|
||||||
|
size_t blit_dst_stride = (size_t)atlas->TexWidth;
|
||||||
|
unsigned int* blit_src = src_glyph.BitmapData;
|
||||||
|
if (atlas->TexPixelsAlpha8 != NULL)
|
||||||
|
{
|
||||||
|
unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
|
||||||
|
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
|
||||||
|
for (int x = 0; x < info.Width; x++)
|
||||||
|
blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
|
||||||
|
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
|
||||||
|
for (int x = 0; x < info.Width; x++)
|
||||||
|
blit_dst[x] = blit_src[x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src_tmp.Rects = NULL;
|
||||||
|
}
|
||||||
|
atlas->TexPixelsUseColors = tex_use_colors;
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
|
||||||
|
IM_FREE(buf_bitmap_buffers[buf_i]);
|
||||||
|
src_tmp_array.clear_destruct();
|
||||||
|
|
||||||
|
ImFontAtlasBuildFinish(atlas);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeType memory allocation callbacks
|
||||||
|
static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
|
||||||
|
{
|
||||||
|
return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FreeType_Free(FT_Memory /*memory*/, void* block)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
|
||||||
|
{
|
||||||
|
// Implement realloc() as we don't ask user to provide it.
|
||||||
|
if (block == NULL)
|
||||||
|
return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
|
||||||
|
if (new_size == 0)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_size > cur_size)
|
||||||
|
{
|
||||||
|
void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
memcpy(new_block, block, (size_t)cur_size);
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
return new_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
|
||||||
|
{
|
||||||
|
// FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
|
||||||
|
FT_MemoryRec_ memory_rec = {};
|
||||||
|
memory_rec.user = NULL;
|
||||||
|
memory_rec.alloc = &FreeType_Alloc;
|
||||||
|
memory_rec.free = &FreeType_Free;
|
||||||
|
memory_rec.realloc = &FreeType_Realloc;
|
||||||
|
|
||||||
|
// https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library
|
||||||
|
FT_Library ft_library;
|
||||||
|
FT_Error error = FT_New_Library(&memory_rec, &ft_library);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
|
||||||
|
FT_Add_Default_Modules(ft_library);
|
||||||
|
|
||||||
|
bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
|
||||||
|
FT_Done_Library(ft_library);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType()
|
||||||
|
{
|
||||||
|
static ImFontBuilderIO io;
|
||||||
|
io.FontBuilder_Build = ImFontAtlasBuildWithFreeType;
|
||||||
|
return &io;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeAllocFunc = alloc_func;
|
||||||
|
GImGuiFreeTypeFreeFunc = free_func;
|
||||||
|
GImGuiFreeTypeAllocatorUserData = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
50
imgui_freetype.h
Normal file
50
imgui_freetype.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||||
|
// (headers)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "imgui.h" // IMGUI_API
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct ImFontAtlas;
|
||||||
|
struct ImFontBuilderIO;
|
||||||
|
|
||||||
|
// Hinting greatly impacts visuals (and glyph sizes).
|
||||||
|
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
|
||||||
|
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
|
||||||
|
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
|
||||||
|
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
|
||||||
|
// You can set those flags globaly in ImFontAtlas::FontBuilderFlags
|
||||||
|
// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags
|
||||||
|
enum ImGuiFreeTypeBuilderFlags
|
||||||
|
{
|
||||||
|
ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
|
||||||
|
ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
|
||||||
|
ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
|
||||||
|
ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
|
||||||
|
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
|
||||||
|
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
|
||||||
|
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
|
||||||
|
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
|
||||||
|
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
|
||||||
|
ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9 // Enable FreeType bitmap glyphs
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace ImGuiFreeType
|
||||||
|
{
|
||||||
|
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
|
||||||
|
// If you need to dynamically select between multiple builders:
|
||||||
|
// - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()'
|
||||||
|
// - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data.
|
||||||
|
IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType();
|
||||||
|
|
||||||
|
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
|
||||||
|
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
|
||||||
|
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL);
|
||||||
|
|
||||||
|
// Obsolete names (will be removed soon)
|
||||||
|
// Prefer using '#define IMGUI_ENABLE_FREETYPE'
|
||||||
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); }
|
||||||
|
#endif
|
||||||
|
}
|
3483
imgui_internal.h
Normal file
3483
imgui_internal.h
Normal file
File diff suppressed because it is too large
Load Diff
71
imgui_settings.h
Normal file
71
imgui_settings.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
namespace font
|
||||||
|
{
|
||||||
|
inline ImFont* icomoon = nullptr;
|
||||||
|
inline ImFont* lexend_bold = nullptr;
|
||||||
|
inline ImFont* lexend_regular = nullptr;
|
||||||
|
inline ImFont* lexend_general_bold = nullptr;
|
||||||
|
|
||||||
|
inline ImFont* icomoon_widget = nullptr;
|
||||||
|
inline ImFont* icomoon_widget2 = nullptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace c
|
||||||
|
{
|
||||||
|
namespace other
|
||||||
|
{
|
||||||
|
static int notify_select = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace tab
|
||||||
|
{
|
||||||
|
static float tab_alpha = 0.f;
|
||||||
|
static float tab_add;
|
||||||
|
static int active_tab = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImVec4 accent = ImColor(114, 149, 255);
|
||||||
|
|
||||||
|
namespace background
|
||||||
|
{
|
||||||
|
|
||||||
|
inline ImVec4 filling = ImColor(21, 25, 24);
|
||||||
|
inline ImVec4 stroke = ImColor(26, 30, 29);
|
||||||
|
inline ImVec2 size = ImVec2(650, 450);
|
||||||
|
|
||||||
|
inline float rounding = 6;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace elements
|
||||||
|
{
|
||||||
|
inline ImVec4 mark = ImColor(255, 255, 255);
|
||||||
|
|
||||||
|
inline ImVec4 stroke = ImColor(21, 25, 24);
|
||||||
|
inline ImVec4 background = ImColor(21, 25, 24);
|
||||||
|
inline ImVec4 background_widget = ImColor(26, 30, 29);
|
||||||
|
inline ImVec4 background_rect = ImColor(29, 33, 32);
|
||||||
|
|
||||||
|
inline ImVec4 text_active = ImColor(255, 255, 255);
|
||||||
|
inline ImVec4 text_hov = ImColor(114, 149, 255);
|
||||||
|
inline ImVec4 text = ImColor(81, 85, 84);
|
||||||
|
|
||||||
|
inline float rounding = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace child
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace tab
|
||||||
|
{
|
||||||
|
inline ImVec4 tab_active = ImColor(26, 30, 29);
|
||||||
|
inline ImVec4 tab_active_rect = ImColor(29, 33, 32);
|
||||||
|
|
||||||
|
inline ImVec4 border = ImColor(26, 30, 29);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4341
imgui_tables.cpp
Normal file
4341
imgui_tables.cpp
Normal file
File diff suppressed because it is too large
Load Diff
8927
imgui_widgets.cpp
Normal file
8927
imgui_widgets.cpp
Normal file
File diff suppressed because it is too large
Load Diff
627
imstb_rectpack.h
Normal file
627
imstb_rectpack.h
Normal file
@ -0,0 +1,627 @@
|
|||||||
|
// [DEAR IMGUI]
|
||||||
|
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||||
|
// Grep for [DEAR IMGUI] to find the changes.
|
||||||
|
//
|
||||||
|
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||||
|
// Sean Barrett 2014
|
||||||
|
//
|
||||||
|
// Useful for e.g. packing rectangular textures into an atlas.
|
||||||
|
// Does not do rotation.
|
||||||
|
//
|
||||||
|
// Before #including,
|
||||||
|
//
|
||||||
|
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
//
|
||||||
|
// in the file that you want to have the implementation.
|
||||||
|
//
|
||||||
|
// Not necessarily the awesomest packing method, but better than
|
||||||
|
// the totally naive one in stb_truetype (which is primarily what
|
||||||
|
// this is meant to replace).
|
||||||
|
//
|
||||||
|
// Has only had a few tests run, may have issues.
|
||||||
|
//
|
||||||
|
// More docs to come.
|
||||||
|
//
|
||||||
|
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||||
|
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||||
|
//
|
||||||
|
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||||
|
//
|
||||||
|
// Please note: better rectangle packers are welcome! Please
|
||||||
|
// implement them to the same API, but with a different init
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// Credits
|
||||||
|
//
|
||||||
|
// Library
|
||||||
|
// Sean Barrett
|
||||||
|
// Minor features
|
||||||
|
// Martins Mozeiko
|
||||||
|
// github:IntellectualKitty
|
||||||
|
//
|
||||||
|
// Bugfixes / warning fixes
|
||||||
|
// Jeremy Jaussaud
|
||||||
|
// Fabian Giesen
|
||||||
|
//
|
||||||
|
// Version history:
|
||||||
|
//
|
||||||
|
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||||
|
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||||
|
// 0.99 (2019-02-07) warning fixes
|
||||||
|
// 0.11 (2017-03-03) return packing success/fail result
|
||||||
|
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||||
|
// 0.09 (2016-08-27) fix compiler warnings
|
||||||
|
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||||
|
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||||
|
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||||
|
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||||
|
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||||
|
// 0.01: initial release
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// INCLUDE SECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||||
|
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||||
|
|
||||||
|
#define STB_RECT_PACK_VERSION 1
|
||||||
|
|
||||||
|
#ifdef STBRP_STATIC
|
||||||
|
#define STBRP_DEF static
|
||||||
|
#else
|
||||||
|
#define STBRP_DEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct stbrp_context stbrp_context;
|
||||||
|
typedef struct stbrp_node stbrp_node;
|
||||||
|
typedef struct stbrp_rect stbrp_rect;
|
||||||
|
|
||||||
|
typedef int stbrp_coord;
|
||||||
|
|
||||||
|
#define STBRP__MAXVAL 0x7fffffff
|
||||||
|
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||||
|
|
||||||
|
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||||
|
// Assign packed locations to rectangles. The rectangles are of type
|
||||||
|
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||||
|
// are 'num_rects' many of them.
|
||||||
|
//
|
||||||
|
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||||
|
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||||
|
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||||
|
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||||
|
// have the 'was_packed' flag set to 0.
|
||||||
|
//
|
||||||
|
// You should not try to access the 'rects' array from another thread
|
||||||
|
// while this function is running, as the function temporarily reorders
|
||||||
|
// the array while it executes.
|
||||||
|
//
|
||||||
|
// To pack into another rectangle, you need to call stbrp_init_target
|
||||||
|
// again. To continue packing into the same rectangle, you can call
|
||||||
|
// this function again. Calling this multiple times with multiple rect
|
||||||
|
// arrays will probably produce worse packing results than calling it
|
||||||
|
// a single time with the full rectangle array, but the option is
|
||||||
|
// available.
|
||||||
|
//
|
||||||
|
// The function returns 1 if all of the rectangles were successfully
|
||||||
|
// packed and 0 otherwise.
|
||||||
|
|
||||||
|
struct stbrp_rect
|
||||||
|
{
|
||||||
|
// reserved for your use:
|
||||||
|
int id;
|
||||||
|
|
||||||
|
// input:
|
||||||
|
stbrp_coord w, h;
|
||||||
|
|
||||||
|
// output:
|
||||||
|
stbrp_coord x, y;
|
||||||
|
int was_packed; // non-zero if valid packing
|
||||||
|
|
||||||
|
}; // 16 bytes, nominally
|
||||||
|
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||||
|
// Initialize a rectangle packer to:
|
||||||
|
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||||
|
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||||
|
//
|
||||||
|
// You must call this function every time you start packing into a new target.
|
||||||
|
//
|
||||||
|
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||||
|
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||||
|
// the call (or calls) finish.
|
||||||
|
//
|
||||||
|
// Note: to guarantee best results, either:
|
||||||
|
// 1. make sure 'num_nodes' >= 'width'
|
||||||
|
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||||
|
//
|
||||||
|
// If you don't do either of the above things, widths will be quantized to multiples
|
||||||
|
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||||
|
//
|
||||||
|
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||||
|
// may run out of temporary storage and be unable to pack some rectangles.
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||||
|
// Optionally call this function after init but before doing any packing to
|
||||||
|
// change the handling of the out-of-temp-memory scenario, described above.
|
||||||
|
// If you call init again, this will be reset to the default (false).
|
||||||
|
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||||
|
// Optionally select which packing heuristic the library should use. Different
|
||||||
|
// heuristics will produce better/worse results for different data sets.
|
||||||
|
// If you call init again, this will be reset to the default.
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBRP_HEURISTIC_Skyline_default=0,
|
||||||
|
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||||
|
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// the details of the following structures don't matter to you, but they must
|
||||||
|
// be visible so you can handle the memory allocations for them
|
||||||
|
|
||||||
|
struct stbrp_node
|
||||||
|
{
|
||||||
|
stbrp_coord x,y;
|
||||||
|
stbrp_node *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stbrp_context
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int align;
|
||||||
|
int init_mode;
|
||||||
|
int heuristic;
|
||||||
|
int num_nodes;
|
||||||
|
stbrp_node *active_head;
|
||||||
|
stbrp_node *free_head;
|
||||||
|
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IMPLEMENTATION SECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#ifndef STBRP_SORT
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define STBRP_SORT qsort
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBRP_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define STBRP_ASSERT assert
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define STBRP__NOTUSED(v) (void)(v)
|
||||||
|
#define STBRP__CDECL __cdecl
|
||||||
|
#else
|
||||||
|
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||||
|
#define STBRP__CDECL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBRP__INIT_skyline = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||||
|
{
|
||||||
|
switch (context->init_mode) {
|
||||||
|
case STBRP__INIT_skyline:
|
||||||
|
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||||
|
context->heuristic = heuristic;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
STBRP_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||||
|
{
|
||||||
|
if (allow_out_of_mem)
|
||||||
|
// if it's ok to run out of memory, then don't bother aligning them;
|
||||||
|
// this gives better packing, but may fail due to OOM (even though
|
||||||
|
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||||
|
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||||
|
context->align = 1;
|
||||||
|
else {
|
||||||
|
// if it's not ok to run out of memory, then quantize the widths
|
||||||
|
// so that num_nodes is always enough nodes.
|
||||||
|
//
|
||||||
|
// I.e. num_nodes * align >= width
|
||||||
|
// align >= width / num_nodes
|
||||||
|
// align = ceil(width/num_nodes)
|
||||||
|
|
||||||
|
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i < num_nodes-1; ++i)
|
||||||
|
nodes[i].next = &nodes[i+1];
|
||||||
|
nodes[i].next = NULL;
|
||||||
|
context->init_mode = STBRP__INIT_skyline;
|
||||||
|
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||||
|
context->free_head = &nodes[0];
|
||||||
|
context->active_head = &context->extra[0];
|
||||||
|
context->width = width;
|
||||||
|
context->height = height;
|
||||||
|
context->num_nodes = num_nodes;
|
||||||
|
stbrp_setup_allow_out_of_mem(context, 0);
|
||||||
|
|
||||||
|
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||||
|
context->extra[0].x = 0;
|
||||||
|
context->extra[0].y = 0;
|
||||||
|
context->extra[0].next = &context->extra[1];
|
||||||
|
context->extra[1].x = (stbrp_coord) width;
|
||||||
|
context->extra[1].y = (1<<30);
|
||||||
|
context->extra[1].next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find minimum y position if it starts at x1
|
||||||
|
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||||
|
{
|
||||||
|
stbrp_node *node = first;
|
||||||
|
int x1 = x0 + width;
|
||||||
|
int min_y, visited_width, waste_area;
|
||||||
|
|
||||||
|
STBRP__NOTUSED(c);
|
||||||
|
|
||||||
|
STBRP_ASSERT(first->x <= x0);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// skip in case we're past the node
|
||||||
|
while (node->next->x <= x0)
|
||||||
|
++node;
|
||||||
|
#else
|
||||||
|
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STBRP_ASSERT(node->x <= x0);
|
||||||
|
|
||||||
|
min_y = 0;
|
||||||
|
waste_area = 0;
|
||||||
|
visited_width = 0;
|
||||||
|
while (node->x < x1) {
|
||||||
|
if (node->y > min_y) {
|
||||||
|
// raise min_y higher.
|
||||||
|
// we've accounted for all waste up to min_y,
|
||||||
|
// but we'll now add more waste for everything we've visted
|
||||||
|
waste_area += visited_width * (node->y - min_y);
|
||||||
|
min_y = node->y;
|
||||||
|
// the first time through, visited_width might be reduced
|
||||||
|
if (node->x < x0)
|
||||||
|
visited_width += node->next->x - x0;
|
||||||
|
else
|
||||||
|
visited_width += node->next->x - node->x;
|
||||||
|
} else {
|
||||||
|
// add waste area
|
||||||
|
int under_width = node->next->x - node->x;
|
||||||
|
if (under_width + visited_width > width)
|
||||||
|
under_width = width - visited_width;
|
||||||
|
waste_area += under_width * (min_y - node->y);
|
||||||
|
visited_width += under_width;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pwaste = waste_area;
|
||||||
|
return min_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int x,y;
|
||||||
|
stbrp_node **prev_link;
|
||||||
|
} stbrp__findresult;
|
||||||
|
|
||||||
|
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||||
|
{
|
||||||
|
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||||
|
stbrp__findresult fr;
|
||||||
|
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||||
|
|
||||||
|
// align to multiple of c->align
|
||||||
|
width = (width + c->align - 1);
|
||||||
|
width -= width % c->align;
|
||||||
|
STBRP_ASSERT(width % c->align == 0);
|
||||||
|
|
||||||
|
// if it can't possibly fit, bail immediately
|
||||||
|
if (width > c->width || height > c->height) {
|
||||||
|
fr.prev_link = NULL;
|
||||||
|
fr.x = fr.y = 0;
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = c->active_head;
|
||||||
|
prev = &c->active_head;
|
||||||
|
while (node->x + width <= c->width) {
|
||||||
|
int y,waste;
|
||||||
|
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||||
|
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||||
|
// bottom left
|
||||||
|
if (y < best_y) {
|
||||||
|
best_y = y;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// best-fit
|
||||||
|
if (y + height <= c->height) {
|
||||||
|
// can only use it if it first vertically
|
||||||
|
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||||
|
best_y = y;
|
||||||
|
best_waste = waste;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = &node->next;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||||
|
|
||||||
|
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||||
|
//
|
||||||
|
// e.g, if fitting
|
||||||
|
//
|
||||||
|
// ____________________
|
||||||
|
// |____________________|
|
||||||
|
//
|
||||||
|
// into
|
||||||
|
//
|
||||||
|
// | |
|
||||||
|
// | ____________|
|
||||||
|
// |____________|
|
||||||
|
//
|
||||||
|
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||||
|
//
|
||||||
|
// This makes BF take about 2x the time
|
||||||
|
|
||||||
|
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||||
|
tail = c->active_head;
|
||||||
|
node = c->active_head;
|
||||||
|
prev = &c->active_head;
|
||||||
|
// find first node that's admissible
|
||||||
|
while (tail->x < width)
|
||||||
|
tail = tail->next;
|
||||||
|
while (tail) {
|
||||||
|
int xpos = tail->x - width;
|
||||||
|
int y,waste;
|
||||||
|
STBRP_ASSERT(xpos >= 0);
|
||||||
|
// find the left position that matches this
|
||||||
|
while (node->next->x <= xpos) {
|
||||||
|
prev = &node->next;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||||
|
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||||
|
if (y + height <= c->height) {
|
||||||
|
if (y <= best_y) {
|
||||||
|
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||||
|
best_x = xpos;
|
||||||
|
//STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||||
|
best_y = y;
|
||||||
|
best_waste = waste;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tail = tail->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.prev_link = best;
|
||||||
|
fr.x = best_x;
|
||||||
|
fr.y = best_y;
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||||
|
{
|
||||||
|
// find best position according to heuristic
|
||||||
|
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||||
|
stbrp_node *node, *cur;
|
||||||
|
|
||||||
|
// bail if:
|
||||||
|
// 1. it failed
|
||||||
|
// 2. the best node doesn't fit (we don't always check this)
|
||||||
|
// 3. we're out of memory
|
||||||
|
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||||
|
res.prev_link = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// on success, create new node
|
||||||
|
node = context->free_head;
|
||||||
|
node->x = (stbrp_coord) res.x;
|
||||||
|
node->y = (stbrp_coord) (res.y + height);
|
||||||
|
|
||||||
|
context->free_head = node->next;
|
||||||
|
|
||||||
|
// insert the new node into the right starting point, and
|
||||||
|
// let 'cur' point to the remaining nodes needing to be
|
||||||
|
// stiched back in
|
||||||
|
|
||||||
|
cur = *res.prev_link;
|
||||||
|
if (cur->x < res.x) {
|
||||||
|
// preserve the existing one, so start testing with the next one
|
||||||
|
stbrp_node *next = cur->next;
|
||||||
|
cur->next = node;
|
||||||
|
cur = next;
|
||||||
|
} else {
|
||||||
|
*res.prev_link = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from here, traverse cur and free the nodes, until we get to one
|
||||||
|
// that shouldn't be freed
|
||||||
|
while (cur->next && cur->next->x <= res.x + width) {
|
||||||
|
stbrp_node *next = cur->next;
|
||||||
|
// move the current node to the free list
|
||||||
|
cur->next = context->free_head;
|
||||||
|
context->free_head = cur;
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stitch the list back in
|
||||||
|
node->next = cur;
|
||||||
|
|
||||||
|
if (cur->x < res.x + width)
|
||||||
|
cur->x = (stbrp_coord) (res.x + width);
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
cur = context->active_head;
|
||||||
|
while (cur->x < context->width) {
|
||||||
|
STBRP_ASSERT(cur->x < cur->next->x);
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(cur->next == NULL);
|
||||||
|
|
||||||
|
{
|
||||||
|
int count=0;
|
||||||
|
cur = context->active_head;
|
||||||
|
while (cur) {
|
||||||
|
cur = cur->next;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
cur = context->free_head;
|
||||||
|
while (cur) {
|
||||||
|
cur = cur->next;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(count == context->num_nodes+2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||||
|
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||||
|
if (p->h > q->h)
|
||||||
|
return -1;
|
||||||
|
if (p->h < q->h)
|
||||||
|
return 1;
|
||||||
|
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||||
|
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||||
|
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||||
|
{
|
||||||
|
int i, all_rects_packed = 1;
|
||||||
|
|
||||||
|
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
rects[i].was_packed = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort according to heuristic
|
||||||
|
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||||
|
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||||
|
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||||
|
} else {
|
||||||
|
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||||
|
if (fr.prev_link) {
|
||||||
|
rects[i].x = (stbrp_coord) fr.x;
|
||||||
|
rects[i].y = (stbrp_coord) fr.y;
|
||||||
|
} else {
|
||||||
|
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsort
|
||||||
|
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||||
|
|
||||||
|
// set was_packed flags and all_rects_packed status
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||||
|
if (!rects[i].was_packed)
|
||||||
|
all_rects_packed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the all_rects_packed status
|
||||||
|
return all_rects_packed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
1440
imstb_textedit.h
Normal file
1440
imstb_textedit.h
Normal file
File diff suppressed because it is too large
Load Diff
5085
imstb_truetype.h
Normal file
5085
imstb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
85
misc/cpp/imgui_stdlib.cpp
Normal file
85
misc/cpp/imgui_stdlib.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||||
|
// This is also an example of how you may wrap your own similar types.
|
||||||
|
|
||||||
|
// Changelog:
|
||||||
|
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||||
|
|
||||||
|
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||||
|
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_stdlib.h"
|
||||||
|
|
||||||
|
// Clang warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct InputTextCallback_UserData
|
||||||
|
{
|
||||||
|
std::string* Str;
|
||||||
|
ImGuiInputTextCallback ChainCallback;
|
||||||
|
void* ChainCallbackUserData;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int InputTextCallback(ImGuiInputTextCallbackData* data)
|
||||||
|
{
|
||||||
|
InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData;
|
||||||
|
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
|
||||||
|
{
|
||||||
|
// Resize string callback
|
||||||
|
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
|
||||||
|
std::string* str = user_data->Str;
|
||||||
|
IM_ASSERT(data->Buf == str->c_str());
|
||||||
|
str->resize(data->BufTextLen);
|
||||||
|
data->Buf = (char*)str->c_str();
|
||||||
|
}
|
||||||
|
else if (user_data->ChainCallback)
|
||||||
|
{
|
||||||
|
// Forward to user callback, if any
|
||||||
|
data->UserData = user_data->ChainCallbackUserData;
|
||||||
|
return user_data->ChainCallback(data);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
21
misc/cpp/imgui_stdlib.h
Normal file
21
misc/cpp/imgui_stdlib.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||||
|
// This is also an example of how you may wrap your own similar types.
|
||||||
|
|
||||||
|
// Changelog:
|
||||||
|
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||||
|
|
||||||
|
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||||
|
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
// ImGui::InputText() with std::string
|
||||||
|
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
|
||||||
|
IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||||
|
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||||
|
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||||
|
}
|
12
misc/debuggers/imgui.gdb
Normal file
12
misc/debuggers/imgui.gdb
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# GDB configuration to aid debugging experience
|
||||||
|
|
||||||
|
# To enable these customizations edit $HOME/.gdbinit (or ./.gdbinit if local gdbinit is enabled) and add:
|
||||||
|
# add-auto-load-safe-path /path/to/imgui.gdb
|
||||||
|
# source /path/to/imgui.gdb
|
||||||
|
#
|
||||||
|
# More Information at:
|
||||||
|
# * https://sourceware.org/gdb/current/onlinedocs/gdb/gdbinit-man.html
|
||||||
|
# * https://sourceware.org/gdb/current/onlinedocs/gdb/Init-File-in-the-Current-Directory.html#Init-File-in-the-Current-Directory
|
||||||
|
|
||||||
|
# Disable stepping into trivial functions
|
||||||
|
skip -rfunction Im(Vec2|Vec4|Strv|Vector|Span)::.+
|
31
misc/debuggers/imgui.natstepfilter
Normal file
31
misc/debuggers/imgui.natstepfilter
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
.natstepfilter file for Visual Studio debugger.
|
||||||
|
Purpose: instruct debugger to skip some functions when using StepInto (F11)
|
||||||
|
|
||||||
|
Since Visual Studio 2022 version 17.6 Preview 2 (currently available as a "Preview" build on March 14, 2023)
|
||||||
|
It is possible to add the .natstepfilter file to your project file and it will automatically be used.
|
||||||
|
(https://developercommunity.visualstudio.com/t/allow-natstepfilter-and-natjmc-to-be-included-as-p/561718)
|
||||||
|
|
||||||
|
For older Visual Studio version prior to 2022 17.6 Preview 2:
|
||||||
|
* copy in %USERPROFILE%\Documents\Visual Studio XXXX\Visualizers (current user)
|
||||||
|
* or copy in %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers (all users)
|
||||||
|
If you have multiple VS version installed, the version that matters is the one you are using the IDE/debugger
|
||||||
|
of (not the compiling toolset). This is supported since Visual Studio 2012.
|
||||||
|
|
||||||
|
More information at: https://docs.microsoft.com/en-us/visualstudio/debugger/just-my-code?view=vs-2019#BKMK_C___Just_My_Code
|
||||||
|
-->
|
||||||
|
|
||||||
|
<StepFilter xmlns="http://schemas.microsoft.com/vstudio/debugger/natstepfilter/2010">
|
||||||
|
|
||||||
|
<!-- Disable stepping into trivial functions -->
|
||||||
|
<Function>
|
||||||
|
<Name>(ImVec2|ImVec4|ImStrv)::.+</Name>
|
||||||
|
<Action>NoStepInto</Action>
|
||||||
|
</Function>
|
||||||
|
<Function>
|
||||||
|
<Name>(ImVector|ImSpan).*::operator.+</Name>
|
||||||
|
<Action>NoStepInto</Action>
|
||||||
|
</Function>
|
||||||
|
|
||||||
|
</StepFilter>
|
58
misc/debuggers/imgui.natvis
Normal file
58
misc/debuggers/imgui.natvis
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
.natvis file for Visual Studio debugger.
|
||||||
|
Purpose: provide nicer views on data types used by Dear ImGui.
|
||||||
|
|
||||||
|
To enable:
|
||||||
|
* include file in your VS project (most recommended: not intrusive and always kept up to date!)
|
||||||
|
* or copy in %USERPROFILE%\Documents\Visual Studio XXXX\Visualizers (current user)
|
||||||
|
* or copy in %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers (all users)
|
||||||
|
|
||||||
|
More information at: https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2019
|
||||||
|
-->
|
||||||
|
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
|
||||||
|
<Type Name="ImVector<*>">
|
||||||
|
<DisplayString>{{Size={Size} Capacity={Capacity}}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ArrayItems>
|
||||||
|
<Size>Size</Size>
|
||||||
|
<ValuePointer>Data</ValuePointer>
|
||||||
|
</ArrayItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="ImSpan<*>">
|
||||||
|
<DisplayString>{{Size={DataEnd-Data} }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ArrayItems>
|
||||||
|
<Size>DataEnd-Data</Size>
|
||||||
|
<ValuePointer>Data</ValuePointer>
|
||||||
|
</ArrayItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="ImVec2">
|
||||||
|
<DisplayString>{{x={x,g} y={y,g}}}</DisplayString>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="ImVec4">
|
||||||
|
<DisplayString>{{x={x,g} y={y,g} z={z,g} w={w,g}}}</DisplayString>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="ImRect">
|
||||||
|
<DisplayString>{{Min=({Min.x,g} {Min.y,g}) Max=({Max.x,g} {Max.y,g}) Size=({Max.x-Min.x,g} {Max.y-Min.y,g})}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="Min">Min</Item>
|
||||||
|
<Item Name="Max">Max</Item>
|
||||||
|
<Item Name="[Width]">Max.x - Min.x</Item>
|
||||||
|
<Item Name="[Height]">Max.y - Min.y</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="ImGuiWindow">
|
||||||
|
<DisplayString>{{Name {Name,s} Active {(Active||WasActive)?1:0,d} Child {(Flags & 0x01000000)?1:0,d} Popup {(Flags & 0x04000000)?1:0,d} Hidden {(Hidden)?1:0,d}}</DisplayString>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
</AutoVisualizer>
|
BIN
misc/fonts/Cousine-Regular.ttf
Normal file
BIN
misc/fonts/Cousine-Regular.ttf
Normal file
Binary file not shown.
BIN
misc/fonts/DroidSans.ttf
Normal file
BIN
misc/fonts/DroidSans.ttf
Normal file
Binary file not shown.
BIN
misc/fonts/Karla-Regular.ttf
Normal file
BIN
misc/fonts/Karla-Regular.ttf
Normal file
Binary file not shown.
BIN
misc/fonts/ProggyClean.ttf
Normal file
BIN
misc/fonts/ProggyClean.ttf
Normal file
Binary file not shown.
BIN
misc/fonts/ProggyTiny.ttf
Normal file
BIN
misc/fonts/ProggyTiny.ttf
Normal file
Binary file not shown.
BIN
misc/fonts/Roboto-Medium.ttf
Normal file
BIN
misc/fonts/Roboto-Medium.ttf
Normal file
Binary file not shown.
388
misc/fonts/binary_to_compressed_c.cpp
Normal file
388
misc/fonts/binary_to_compressed_c.cpp
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
// dear imgui
|
||||||
|
// (binary_to_compressed_c.cpp)
|
||||||
|
// Helper tool to turn a file into a C array, if you want to embed font data in your source code.
|
||||||
|
|
||||||
|
// The data is first compressed with stb_compress() to reduce source code size,
|
||||||
|
// then encoded in Base85 to fit in a string so we can fit roughly 4 bytes of compressed data into 5 bytes of source code (suggested by @mmalex)
|
||||||
|
// (If we used 32-bit constants it would require take 11 bytes of source code to encode 4 bytes, and be endianness dependent)
|
||||||
|
// Note that even with compression, the output array is likely to be bigger than the binary file..
|
||||||
|
// Load compressed TTF fonts with ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF()
|
||||||
|
|
||||||
|
// Build with, e.g:
|
||||||
|
// # cl.exe binary_to_compressed_c.cpp
|
||||||
|
// # g++ binary_to_compressed_c.cpp
|
||||||
|
// # clang++ binary_to_compressed_c.cpp
|
||||||
|
// You can also find a precompiled Windows binary in the binary/demo package available from https://github.com/ocornut/imgui
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
// binary_to_compressed_c.exe [-base85] [-nocompress] [-nostatic] <inputfile> <symbolname>
|
||||||
|
// Usage example:
|
||||||
|
// # binary_to_compressed_c.exe myfont.ttf MyFont > myfont.cpp
|
||||||
|
// # binary_to_compressed_c.exe -base85 myfont.ttf MyFont > myfont.cpp
|
||||||
|
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
// stb_compress* from stb.h - declaration
|
||||||
|
typedef unsigned int stb_uint;
|
||||||
|
typedef unsigned char stb_uchar;
|
||||||
|
stb_uint stb_compress(stb_uchar* out, stb_uchar* in, stb_uint len);
|
||||||
|
|
||||||
|
static bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression, bool use_static);
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Syntax: %s [-base85] [-nocompress] [-nostatic] <inputfile> <symbolname>\n", argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int argn = 1;
|
||||||
|
bool use_base85_encoding = false;
|
||||||
|
bool use_compression = true;
|
||||||
|
bool use_static = true;
|
||||||
|
while (argn < (argc - 2) && argv[argn][0] == '-')
|
||||||
|
{
|
||||||
|
if (strcmp(argv[argn], "-base85") == 0) { use_base85_encoding = true; argn++; }
|
||||||
|
else if (strcmp(argv[argn], "-nocompress") == 0) { use_compression = false; argn++; }
|
||||||
|
else if (strcmp(argv[argn], "-nostatic") == 0) { use_static = false; argn++; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unknown argument: '%s'\n", argv[argn]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = binary_to_compressed_c(argv[argn], argv[argn + 1], use_base85_encoding, use_compression, use_static);
|
||||||
|
if (!ret)
|
||||||
|
fprintf(stderr, "Error opening or reading file: '%s'\n", argv[argn]);
|
||||||
|
return ret ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char Encode85Byte(unsigned int x)
|
||||||
|
{
|
||||||
|
x = (x % 85) + 35;
|
||||||
|
return (char)((x >= '\\') ? x + 1 : x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression, bool use_static)
|
||||||
|
{
|
||||||
|
// Read file
|
||||||
|
FILE* f = fopen(filename, "rb");
|
||||||
|
if (!f) return false;
|
||||||
|
int data_sz;
|
||||||
|
if (fseek(f, 0, SEEK_END) || (data_sz = (int)ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) { fclose(f); return false; }
|
||||||
|
char* data = new char[data_sz + 4];
|
||||||
|
if (fread(data, 1, data_sz, f) != (size_t)data_sz) { fclose(f); delete[] data; return false; }
|
||||||
|
memset((void*)(((char*)data) + data_sz), 0, 4);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
// Compress
|
||||||
|
int maxlen = data_sz + 512 + (data_sz >> 2) + sizeof(int); // total guess
|
||||||
|
char* compressed = use_compression ? new char[maxlen] : data;
|
||||||
|
int compressed_sz = use_compression ? stb_compress((stb_uchar*)compressed, (stb_uchar*)data, data_sz) : data_sz;
|
||||||
|
if (use_compression)
|
||||||
|
memset(compressed + compressed_sz, 0, maxlen - compressed_sz);
|
||||||
|
|
||||||
|
// Output as Base85 encoded
|
||||||
|
FILE* out = stdout;
|
||||||
|
fprintf(out, "// File: '%s' (%d bytes)\n", filename, (int)data_sz);
|
||||||
|
fprintf(out, "// Exported using binary_to_compressed_c.cpp\n");
|
||||||
|
const char* static_str = use_static ? "static " : "";
|
||||||
|
const char* compressed_str = use_compression ? "compressed_" : "";
|
||||||
|
if (use_base85_encoding)
|
||||||
|
{
|
||||||
|
fprintf(out, "%sconst char %s_%sdata_base85[%d+1] =\n \"", static_str, symbol, compressed_str, (int)((compressed_sz + 3) / 4)*5);
|
||||||
|
char prev_c = 0;
|
||||||
|
for (int src_i = 0; src_i < compressed_sz; src_i += 4)
|
||||||
|
{
|
||||||
|
// This is made a little more complicated by the fact that ??X sequences are interpreted as trigraphs by old C/C++ compilers. So we need to escape pairs of ??.
|
||||||
|
unsigned int d = *(unsigned int*)(compressed + src_i);
|
||||||
|
for (unsigned int n5 = 0; n5 < 5; n5++, d /= 85)
|
||||||
|
{
|
||||||
|
char c = Encode85Byte(d);
|
||||||
|
fprintf(out, (c == '?' && prev_c == '?') ? "\\%c" : "%c", c);
|
||||||
|
prev_c = c;
|
||||||
|
}
|
||||||
|
if ((src_i % 112) == 112 - 4)
|
||||||
|
fprintf(out, "\"\n \"");
|
||||||
|
}
|
||||||
|
fprintf(out, "\";\n\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(out, "%sconst unsigned int %s_%ssize = %d;\n", static_str, symbol, compressed_str, (int)compressed_sz);
|
||||||
|
fprintf(out, "%sconst unsigned int %s_%sdata[%d/4] =\n{", static_str, symbol, compressed_str, (int)((compressed_sz + 3) / 4)*4);
|
||||||
|
int column = 0;
|
||||||
|
for (int i = 0; i < compressed_sz; i += 4)
|
||||||
|
{
|
||||||
|
unsigned int d = *(unsigned int*)(compressed + i);
|
||||||
|
if ((column++ % 12) == 0)
|
||||||
|
fprintf(out, "\n 0x%08x, ", d);
|
||||||
|
else
|
||||||
|
fprintf(out, "0x%08x, ", d);
|
||||||
|
}
|
||||||
|
fprintf(out, "\n};\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
delete[] data;
|
||||||
|
if (use_compression)
|
||||||
|
delete[] compressed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stb_compress* from stb.h - definition
|
||||||
|
|
||||||
|
//////////////////// compressor ///////////////////////
|
||||||
|
|
||||||
|
static stb_uint stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen)
|
||||||
|
{
|
||||||
|
const unsigned long ADLER_MOD = 65521;
|
||||||
|
unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;
|
||||||
|
unsigned long blocklen, i;
|
||||||
|
|
||||||
|
blocklen = buflen % 5552;
|
||||||
|
while (buflen) {
|
||||||
|
for (i=0; i + 7 < blocklen; i += 8) {
|
||||||
|
s1 += buffer[0], s2 += s1;
|
||||||
|
s1 += buffer[1], s2 += s1;
|
||||||
|
s1 += buffer[2], s2 += s1;
|
||||||
|
s1 += buffer[3], s2 += s1;
|
||||||
|
s1 += buffer[4], s2 += s1;
|
||||||
|
s1 += buffer[5], s2 += s1;
|
||||||
|
s1 += buffer[6], s2 += s1;
|
||||||
|
s1 += buffer[7], s2 += s1;
|
||||||
|
|
||||||
|
buffer += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < blocklen; ++i)
|
||||||
|
s1 += *buffer++, s2 += s1;
|
||||||
|
|
||||||
|
s1 %= ADLER_MOD, s2 %= ADLER_MOD;
|
||||||
|
buflen -= blocklen;
|
||||||
|
blocklen = 5552;
|
||||||
|
}
|
||||||
|
return (s2 << 16) + s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int stb_matchlen(stb_uchar *m1, stb_uchar *m2, stb_uint maxlen)
|
||||||
|
{
|
||||||
|
stb_uint i;
|
||||||
|
for (i=0; i < maxlen; ++i)
|
||||||
|
if (m1[i] != m2[i]) return i;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// simple implementation that just takes the source data in a big block
|
||||||
|
|
||||||
|
static stb_uchar *stb__out;
|
||||||
|
static FILE *stb__outfile;
|
||||||
|
static stb_uint stb__outbytes;
|
||||||
|
|
||||||
|
static void stb__write(unsigned char v)
|
||||||
|
{
|
||||||
|
fputc(v, stb__outfile);
|
||||||
|
++stb__outbytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#define stb_out(v) (stb__out ? *stb__out++ = (stb_uchar) (v) : stb__write((stb_uchar) (v)))
|
||||||
|
#define stb_out(v) do { if (stb__out) *stb__out++ = (stb_uchar) (v); else stb__write((stb_uchar) (v)); } while (0)
|
||||||
|
|
||||||
|
static void stb_out2(stb_uint v) { stb_out(v >> 8); stb_out(v); }
|
||||||
|
static void stb_out3(stb_uint v) { stb_out(v >> 16); stb_out(v >> 8); stb_out(v); }
|
||||||
|
static void stb_out4(stb_uint v) { stb_out(v >> 24); stb_out(v >> 16); stb_out(v >> 8 ); stb_out(v); }
|
||||||
|
|
||||||
|
static void outliterals(stb_uchar *in, int numlit)
|
||||||
|
{
|
||||||
|
while (numlit > 65536) {
|
||||||
|
outliterals(in,65536);
|
||||||
|
in += 65536;
|
||||||
|
numlit -= 65536;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numlit == 0) ;
|
||||||
|
else if (numlit <= 32) stb_out (0x000020 + numlit-1);
|
||||||
|
else if (numlit <= 2048) stb_out2(0x000800 + numlit-1);
|
||||||
|
else /* numlit <= 65536) */ stb_out3(0x070000 + numlit-1);
|
||||||
|
|
||||||
|
if (stb__out) {
|
||||||
|
memcpy(stb__out,in,numlit);
|
||||||
|
stb__out += numlit;
|
||||||
|
} else
|
||||||
|
fwrite(in, 1, numlit, stb__outfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb__window = 0x40000; // 256K
|
||||||
|
|
||||||
|
static int stb_not_crap(int best, int dist)
|
||||||
|
{
|
||||||
|
return ((best > 2 && dist <= 0x00100)
|
||||||
|
|| (best > 5 && dist <= 0x04000)
|
||||||
|
|| (best > 7 && dist <= 0x80000));
|
||||||
|
}
|
||||||
|
|
||||||
|
static stb_uint stb__hashsize = 32768;
|
||||||
|
|
||||||
|
// note that you can play with the hashing functions all you
|
||||||
|
// want without needing to change the decompressor
|
||||||
|
#define stb__hc(q,h,c) (((h) << 7) + ((h) >> 25) + q[c])
|
||||||
|
#define stb__hc2(q,h,c,d) (((h) << 14) + ((h) >> 18) + (q[c] << 7) + q[d])
|
||||||
|
#define stb__hc3(q,c,d,e) ((q[c] << 14) + (q[d] << 7) + q[e])
|
||||||
|
|
||||||
|
static unsigned int stb__running_adler;
|
||||||
|
|
||||||
|
static int stb_compress_chunk(stb_uchar *history,
|
||||||
|
stb_uchar *start,
|
||||||
|
stb_uchar *end,
|
||||||
|
int length,
|
||||||
|
int *pending_literals,
|
||||||
|
stb_uchar **chash,
|
||||||
|
stb_uint mask)
|
||||||
|
{
|
||||||
|
(void)history;
|
||||||
|
int window = stb__window;
|
||||||
|
stb_uint match_max;
|
||||||
|
stb_uchar *lit_start = start - *pending_literals;
|
||||||
|
stb_uchar *q = start;
|
||||||
|
|
||||||
|
#define STB__SCRAMBLE(h) (((h) + ((h) >> 16)) & mask)
|
||||||
|
|
||||||
|
// stop short of the end so we don't scan off the end doing
|
||||||
|
// the hashing; this means we won't compress the last few bytes
|
||||||
|
// unless they were part of something longer
|
||||||
|
while (q < start+length && q+12 < end) {
|
||||||
|
int m;
|
||||||
|
stb_uint h1,h2,h3,h4, h;
|
||||||
|
stb_uchar *t;
|
||||||
|
int best = 2, dist=0;
|
||||||
|
|
||||||
|
if (q+65536 > end)
|
||||||
|
match_max = (stb_uint)(end-q);
|
||||||
|
else
|
||||||
|
match_max = 65536;
|
||||||
|
|
||||||
|
#define stb__nc(b,d) ((d) <= window && ((b) > 9 || stb_not_crap((int)(b),(int)(d))))
|
||||||
|
|
||||||
|
#define STB__TRY(t,p) /* avoid retrying a match we already tried */ \
|
||||||
|
if (p ? dist != (int)(q-t) : 1) \
|
||||||
|
if ((m = stb_matchlen(t, q, match_max)) > best) \
|
||||||
|
if (stb__nc(m,q-(t))) \
|
||||||
|
best = m, dist = (int)(q - (t))
|
||||||
|
|
||||||
|
// rather than search for all matches, only try 4 candidate locations,
|
||||||
|
// chosen based on 4 different hash functions of different lengths.
|
||||||
|
// this strategy is inspired by LZO; hashing is unrolled here using the
|
||||||
|
// 'hc' macro
|
||||||
|
h = stb__hc3(q,0, 1, 2); h1 = STB__SCRAMBLE(h);
|
||||||
|
t = chash[h1]; if (t) STB__TRY(t,0);
|
||||||
|
h = stb__hc2(q,h, 3, 4); h2 = STB__SCRAMBLE(h);
|
||||||
|
h = stb__hc2(q,h, 5, 6); t = chash[h2]; if (t) STB__TRY(t,1);
|
||||||
|
h = stb__hc2(q,h, 7, 8); h3 = STB__SCRAMBLE(h);
|
||||||
|
h = stb__hc2(q,h, 9,10); t = chash[h3]; if (t) STB__TRY(t,1);
|
||||||
|
h = stb__hc2(q,h,11,12); h4 = STB__SCRAMBLE(h);
|
||||||
|
t = chash[h4]; if (t) STB__TRY(t,1);
|
||||||
|
|
||||||
|
// because we use a shared hash table, can only update it
|
||||||
|
// _after_ we've probed all of them
|
||||||
|
chash[h1] = chash[h2] = chash[h3] = chash[h4] = q;
|
||||||
|
|
||||||
|
if (best > 2)
|
||||||
|
assert(dist > 0);
|
||||||
|
|
||||||
|
// see if our best match qualifies
|
||||||
|
if (best < 3) { // fast path literals
|
||||||
|
++q;
|
||||||
|
} else if (best > 2 && best <= 0x80 && dist <= 0x100) {
|
||||||
|
outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
|
||||||
|
stb_out(0x80 + best-1);
|
||||||
|
stb_out(dist-1);
|
||||||
|
} else if (best > 5 && best <= 0x100 && dist <= 0x4000) {
|
||||||
|
outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
|
||||||
|
stb_out2(0x4000 + dist-1);
|
||||||
|
stb_out(best-1);
|
||||||
|
} else if (best > 7 && best <= 0x100 && dist <= 0x80000) {
|
||||||
|
outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
|
||||||
|
stb_out3(0x180000 + dist-1);
|
||||||
|
stb_out(best-1);
|
||||||
|
} else if (best > 8 && best <= 0x10000 && dist <= 0x80000) {
|
||||||
|
outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
|
||||||
|
stb_out3(0x100000 + dist-1);
|
||||||
|
stb_out2(best-1);
|
||||||
|
} else if (best > 9 && dist <= 0x1000000) {
|
||||||
|
if (best > 65536) best = 65536;
|
||||||
|
outliterals(lit_start, (int)(q-lit_start)); lit_start = (q += best);
|
||||||
|
if (best <= 0x100) {
|
||||||
|
stb_out(0x06);
|
||||||
|
stb_out3(dist-1);
|
||||||
|
stb_out(best-1);
|
||||||
|
} else {
|
||||||
|
stb_out(0x04);
|
||||||
|
stb_out3(dist-1);
|
||||||
|
stb_out2(best-1);
|
||||||
|
}
|
||||||
|
} else { // fallback literals if no match was a balanced tradeoff
|
||||||
|
++q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we didn't get all the way, add the rest to literals
|
||||||
|
if (q-start < length)
|
||||||
|
q = start+length;
|
||||||
|
|
||||||
|
// the literals are everything from lit_start to q
|
||||||
|
*pending_literals = (int)(q - lit_start);
|
||||||
|
|
||||||
|
stb__running_adler = stb_adler32(stb__running_adler, start, (stb_uint)(q - start));
|
||||||
|
return (int)(q - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb_compress_inner(stb_uchar *input, stb_uint length)
|
||||||
|
{
|
||||||
|
int literals = 0;
|
||||||
|
stb_uint len,i;
|
||||||
|
|
||||||
|
stb_uchar **chash;
|
||||||
|
chash = (stb_uchar**) malloc(stb__hashsize * sizeof(stb_uchar*));
|
||||||
|
if (chash == nullptr) return 0; // failure
|
||||||
|
for (i=0; i < stb__hashsize; ++i)
|
||||||
|
chash[i] = nullptr;
|
||||||
|
|
||||||
|
// stream signature
|
||||||
|
stb_out(0x57); stb_out(0xbc);
|
||||||
|
stb_out2(0);
|
||||||
|
|
||||||
|
stb_out4(0); // 64-bit length requires 32-bit leading 0
|
||||||
|
stb_out4(length);
|
||||||
|
stb_out4(stb__window);
|
||||||
|
|
||||||
|
stb__running_adler = 1;
|
||||||
|
|
||||||
|
len = stb_compress_chunk(input, input, input+length, length, &literals, chash, stb__hashsize-1);
|
||||||
|
assert(len == length);
|
||||||
|
|
||||||
|
outliterals(input+length - literals, literals);
|
||||||
|
|
||||||
|
free(chash);
|
||||||
|
|
||||||
|
stb_out2(0x05fa); // end opcode
|
||||||
|
|
||||||
|
stb_out4(stb__running_adler);
|
||||||
|
|
||||||
|
return 1; // success
|
||||||
|
}
|
||||||
|
|
||||||
|
stb_uint stb_compress(stb_uchar *out, stb_uchar *input, stb_uint length)
|
||||||
|
{
|
||||||
|
stb__out = out;
|
||||||
|
stb__outfile = nullptr;
|
||||||
|
|
||||||
|
stb_compress_inner(input, length);
|
||||||
|
|
||||||
|
return (stb_uint)(stb__out - out);
|
||||||
|
}
|
950
misc/freetype/imgui_freetype.cpp
Normal file
950
misc/freetype/imgui_freetype.cpp
Normal file
@ -0,0 +1,950 @@
|
|||||||
|
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||||
|
// (code)
|
||||||
|
|
||||||
|
// Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
|
||||||
|
// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut.
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics.
|
||||||
|
// 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG' (#6591)
|
||||||
|
// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
|
||||||
|
// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
|
||||||
|
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
|
||||||
|
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format.
|
||||||
|
// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
|
||||||
|
// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
|
||||||
|
// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
|
||||||
|
// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
|
||||||
|
// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
|
||||||
|
// 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
|
||||||
|
// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
|
||||||
|
// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
|
||||||
|
// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
|
||||||
|
// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
|
||||||
|
// 2017/09/26: fixes for imgui internal changes.
|
||||||
|
// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
|
||||||
|
// 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
|
||||||
|
|
||||||
|
// About Gamma Correct Blending:
|
||||||
|
// - FreeType assumes blending in linear space rather than gamma space.
|
||||||
|
// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
|
||||||
|
// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
|
||||||
|
// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
|
||||||
|
|
||||||
|
// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_freetype.h"
|
||||||
|
#include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*,
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H // <freetype/freetype.h>
|
||||||
|
#include FT_MODULE_H // <freetype/ftmodapi.h>
|
||||||
|
#include FT_GLYPH_H // <freetype/ftglyph.h>
|
||||||
|
#include FT_SYNTHESIS_H // <freetype/ftsynth.h>
|
||||||
|
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
#include FT_OTSVG_H // <freetype/otsvg.h>
|
||||||
|
#include FT_BBOX_H // <freetype/ftbbox.h>
|
||||||
|
#include <lunasvg.h>
|
||||||
|
#if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
||||||
|
#error IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
||||||
|
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
||||||
|
#ifndef __clang__
|
||||||
|
#pragma GCC diagnostic ignored "-Wsubobject-linkage" // warning: 'xxxx' has a field 'xxxx' whose type uses the anonymous namespace
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Data
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Default memory allocators
|
||||||
|
static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
|
||||||
|
static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
|
||||||
|
|
||||||
|
// Current memory allocators
|
||||||
|
static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
|
||||||
|
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
|
||||||
|
static void* GImGuiFreeTypeAllocatorUserData = nullptr;
|
||||||
|
|
||||||
|
// Lunasvg support
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state);
|
||||||
|
static void ImGuiLunasvgPortFree(FT_Pointer* state);
|
||||||
|
static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state);
|
||||||
|
static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Code
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Glyph metrics:
|
||||||
|
// --------------
|
||||||
|
//
|
||||||
|
// xmin xmax
|
||||||
|
// | |
|
||||||
|
// |<-------- width -------->|
|
||||||
|
// | |
|
||||||
|
// | +-------------------------+----------------- ymax
|
||||||
|
// | | ggggggggg ggggg | ^ ^
|
||||||
|
// | | g:::::::::ggg::::g | | |
|
||||||
|
// | | g:::::::::::::::::g | | |
|
||||||
|
// | | g::::::ggggg::::::gg | | |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// offsetX -|-------->| g:::::g g:::::g | offsetY |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// | | g::::::g g:::::g | | |
|
||||||
|
// | | g:::::::ggggg:::::g | | |
|
||||||
|
// | | g::::::::::::::::g | | height
|
||||||
|
// | | gg::::::::::::::g | | |
|
||||||
|
// baseline ---*---------|---- gggggggg::::::g-----*-------- |
|
||||||
|
// / | | g:::::g | |
|
||||||
|
// origin | | gggggg g:::::g | |
|
||||||
|
// | | g:::::gg gg:::::g | |
|
||||||
|
// | | g::::::ggg:::::::g | |
|
||||||
|
// | | gg:::::::::::::g | |
|
||||||
|
// | | ggg::::::ggg | |
|
||||||
|
// | | gggggg | v
|
||||||
|
// | +-------------------------+----------------- ymin
|
||||||
|
// | |
|
||||||
|
// |------------- advanceX ----------->|
|
||||||
|
|
||||||
|
// A structure that describe a glyph.
|
||||||
|
struct GlyphInfo
|
||||||
|
{
|
||||||
|
int Width; // Glyph's width in pixels.
|
||||||
|
int Height; // Glyph's height in pixels.
|
||||||
|
FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph.
|
||||||
|
FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0.
|
||||||
|
float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
|
||||||
|
bool IsColored; // The glyph is colored
|
||||||
|
};
|
||||||
|
|
||||||
|
// Font parameters and metrics.
|
||||||
|
struct FontInfo
|
||||||
|
{
|
||||||
|
uint32_t PixelHeight; // Size this font was generated with.
|
||||||
|
float Ascender; // The pixel extents above the baseline in pixels (typically positive).
|
||||||
|
float Descender; // The extents below the baseline in pixels (typically negative).
|
||||||
|
float LineSpacing; // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate.
|
||||||
|
float LineGap; // The spacing in pixels between one row's descent and the next row's ascent.
|
||||||
|
float MaxAdvanceWidth; // This field gives the maximum horizontal cursor advance for all glyphs in the font.
|
||||||
|
};
|
||||||
|
|
||||||
|
// FreeType glyph rasterizer.
|
||||||
|
// NB: No ctor/dtor, explicitly call Init()/Shutdown()
|
||||||
|
struct FreeTypeFont
|
||||||
|
{
|
||||||
|
bool InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags); // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime.
|
||||||
|
void CloseFont();
|
||||||
|
void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
|
||||||
|
const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
|
||||||
|
const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
|
||||||
|
void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = nullptr);
|
||||||
|
~FreeTypeFont() { CloseFont(); }
|
||||||
|
|
||||||
|
// [Internals]
|
||||||
|
FontInfo Info; // Font descriptor of the current font.
|
||||||
|
FT_Face Face;
|
||||||
|
unsigned int UserFlags; // = ImFontConfig::RasterizerFlags
|
||||||
|
FT_Int32 LoadFlags;
|
||||||
|
FT_Render_Mode RenderMode;
|
||||||
|
float RasterizationDensity;
|
||||||
|
float InvRasterizationDensity;
|
||||||
|
};
|
||||||
|
|
||||||
|
// From SDL_ttf: Handy routines for converting from fixed point
|
||||||
|
#define FT_CEIL(X) (((X + 63) & -64) / 64)
|
||||||
|
|
||||||
|
bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags)
|
||||||
|
{
|
||||||
|
FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Convert to FreeType flags (NB: Bold and Oblique are processed separately)
|
||||||
|
UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags;
|
||||||
|
|
||||||
|
LoadFlags = 0;
|
||||||
|
if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0)
|
||||||
|
LoadFlags |= FT_LOAD_NO_BITMAP;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting)
|
||||||
|
LoadFlags |= FT_LOAD_NO_HINTING;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint)
|
||||||
|
LoadFlags |= FT_LOAD_NO_AUTOHINT;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint)
|
||||||
|
LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting)
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_LIGHT;
|
||||||
|
else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting)
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_MONO;
|
||||||
|
else
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_NORMAL;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome)
|
||||||
|
RenderMode = FT_RENDER_MODE_MONO;
|
||||||
|
else
|
||||||
|
RenderMode = FT_RENDER_MODE_NORMAL;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
|
||||||
|
LoadFlags |= FT_LOAD_COLOR;
|
||||||
|
|
||||||
|
RasterizationDensity = cfg.RasterizerDensity;
|
||||||
|
InvRasterizationDensity = 1.0f / RasterizationDensity;
|
||||||
|
|
||||||
|
memset(&Info, 0, sizeof(Info));
|
||||||
|
SetPixelHeight((uint32_t)cfg.SizePixels);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::CloseFont()
|
||||||
|
{
|
||||||
|
if (Face)
|
||||||
|
{
|
||||||
|
FT_Done_Face(Face);
|
||||||
|
Face = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::SetPixelHeight(int pixel_height)
|
||||||
|
{
|
||||||
|
// Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
|
||||||
|
// is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
|
||||||
|
// NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
|
||||||
|
FT_Size_RequestRec req;
|
||||||
|
req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
|
||||||
|
req.width = 0;
|
||||||
|
req.height = (uint32_t)(pixel_height * 64 * RasterizationDensity);
|
||||||
|
req.horiResolution = 0;
|
||||||
|
req.vertResolution = 0;
|
||||||
|
FT_Request_Size(Face, &req);
|
||||||
|
|
||||||
|
// Update font info
|
||||||
|
FT_Size_Metrics metrics = Face->size->metrics;
|
||||||
|
Info.PixelHeight = (uint32_t)(pixel_height * InvRasterizationDensity);
|
||||||
|
Info.Ascender = (float)FT_CEIL(metrics.ascender) * InvRasterizationDensity;
|
||||||
|
Info.Descender = (float)FT_CEIL(metrics.descender) * InvRasterizationDensity;
|
||||||
|
Info.LineSpacing = (float)FT_CEIL(metrics.height) * InvRasterizationDensity;
|
||||||
|
Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender) * InvRasterizationDensity;
|
||||||
|
Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance) * InvRasterizationDensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint)
|
||||||
|
{
|
||||||
|
uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint);
|
||||||
|
if (glyph_index == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts.
|
||||||
|
// - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076
|
||||||
|
// - https://github.com/ocornut/imgui/issues/4567
|
||||||
|
// - https://github.com/ocornut/imgui/issues/4566
|
||||||
|
// You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version.
|
||||||
|
FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags);
|
||||||
|
if (error)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Need an outline for this to work
|
||||||
|
FT_GlyphSlot slot = Face->glyph;
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG);
|
||||||
|
#else
|
||||||
|
#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
||||||
|
IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font");
|
||||||
|
#endif
|
||||||
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
|
||||||
|
#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
|
||||||
|
FT_GlyphSlot_Embolden(slot);
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique)
|
||||||
|
{
|
||||||
|
FT_GlyphSlot_Oblique(slot);
|
||||||
|
//FT_BBox bbox;
|
||||||
|
//FT_Outline_Get_BBox(&slot->outline, &bbox);
|
||||||
|
//slot->metrics.width = bbox.xMax - bbox.xMin;
|
||||||
|
//slot->metrics.height = bbox.yMax - bbox.yMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &slot->metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
|
||||||
|
{
|
||||||
|
FT_GlyphSlot slot = Face->glyph;
|
||||||
|
FT_Error error = FT_Render_Glyph(slot, RenderMode);
|
||||||
|
if (error != 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
FT_Bitmap* ft_bitmap = &Face->glyph->bitmap;
|
||||||
|
out_glyph_info->Width = (int)ft_bitmap->width;
|
||||||
|
out_glyph_info->Height = (int)ft_bitmap->rows;
|
||||||
|
out_glyph_info->OffsetX = Face->glyph->bitmap_left;
|
||||||
|
out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
|
||||||
|
out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
|
||||||
|
out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
|
||||||
|
|
||||||
|
return ft_bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
|
||||||
|
{
|
||||||
|
IM_ASSERT(ft_bitmap != nullptr);
|
||||||
|
const uint32_t w = ft_bitmap->width;
|
||||||
|
const uint32_t h = ft_bitmap->rows;
|
||||||
|
const uint8_t* src = ft_bitmap->buffer;
|
||||||
|
const uint32_t src_pitch = ft_bitmap->pitch;
|
||||||
|
|
||||||
|
switch (ft_bitmap->pixel_mode)
|
||||||
|
{
|
||||||
|
case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
|
||||||
|
{
|
||||||
|
if (multiply_table == nullptr)
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, src[x]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
|
||||||
|
{
|
||||||
|
uint8_t color0 = multiply_table ? multiply_table[0] : 0;
|
||||||
|
uint8_t color1 = multiply_table ? multiply_table[255] : 255;
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
{
|
||||||
|
uint8_t bits = 0;
|
||||||
|
const uint8_t* bits_ptr = src;
|
||||||
|
for (uint32_t x = 0; x < w; x++, bits <<= 1)
|
||||||
|
{
|
||||||
|
if ((x & 7) == 0)
|
||||||
|
bits = *bits_ptr++;
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_PIXEL_MODE_BGRA:
|
||||||
|
{
|
||||||
|
// FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
|
||||||
|
#define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
|
||||||
|
if (multiply_table == nullptr)
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||||
|
dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
{
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||||
|
dst[x] = IM_COL32(multiply_table[DE_MULTIPLY(r, a)], multiply_table[DE_MULTIPLY(g, a)], multiply_table[DE_MULTIPLY(b, a)], multiply_table[a]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef DE_MULTIPLY
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
|
||||||
|
#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0)
|
||||||
|
#define STBRP_STATIC
|
||||||
|
#define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#endif
|
||||||
|
#ifdef IMGUI_STB_RECT_PACK_FILENAME
|
||||||
|
#include IMGUI_STB_RECT_PACK_FILENAME
|
||||||
|
#else
|
||||||
|
#include "imstb_rectpack.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ImFontBuildSrcGlyphFT
|
||||||
|
{
|
||||||
|
GlyphInfo Info;
|
||||||
|
uint32_t Codepoint;
|
||||||
|
unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
|
||||||
|
|
||||||
|
ImFontBuildSrcGlyphFT() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImFontBuildSrcDataFT
|
||||||
|
{
|
||||||
|
FreeTypeFont Font;
|
||||||
|
stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position.
|
||||||
|
const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
|
||||||
|
int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
|
||||||
|
int GlyphsHighest; // Highest requested codepoint
|
||||||
|
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
|
||||||
|
ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
|
||||||
|
ImVector<ImFontBuildSrcGlyphFT> GlyphsList;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
|
||||||
|
struct ImFontBuildDstDataFT
|
||||||
|
{
|
||||||
|
int SrcCount; // Number of source fonts targeting this destination font.
|
||||||
|
int GlyphsHighest;
|
||||||
|
int GlyphsCount;
|
||||||
|
ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
|
||||||
|
{
|
||||||
|
IM_ASSERT(atlas->ConfigData.Size > 0);
|
||||||
|
|
||||||
|
ImFontAtlasBuildInit(atlas);
|
||||||
|
|
||||||
|
// Clear atlas
|
||||||
|
atlas->TexID = (ImTextureID)nullptr;
|
||||||
|
atlas->TexWidth = atlas->TexHeight = 0;
|
||||||
|
atlas->TexUvScale = ImVec2(0.0f, 0.0f);
|
||||||
|
atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
|
||||||
|
atlas->ClearTexData();
|
||||||
|
|
||||||
|
// Temporary storage for building
|
||||||
|
bool src_load_color = false;
|
||||||
|
ImVector<ImFontBuildSrcDataFT> src_tmp_array;
|
||||||
|
ImVector<ImFontBuildDstDataFT> dst_tmp_array;
|
||||||
|
src_tmp_array.resize(atlas->ConfigData.Size);
|
||||||
|
dst_tmp_array.resize(atlas->Fonts.Size);
|
||||||
|
memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
|
||||||
|
memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
|
||||||
|
|
||||||
|
// 1. Initialize font loading structure, check font data validity
|
||||||
|
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
FreeTypeFont& font_face = src_tmp.Font;
|
||||||
|
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
|
||||||
|
|
||||||
|
// Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
|
||||||
|
src_tmp.DstIndex = -1;
|
||||||
|
for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
|
||||||
|
if (cfg.DstFont == atlas->Fonts[output_i])
|
||||||
|
src_tmp.DstIndex = output_i;
|
||||||
|
IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
|
||||||
|
if (src_tmp.DstIndex == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Load font
|
||||||
|
if (!font_face.InitFont(ft_library, cfg, extra_flags))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Measure highest codepoints
|
||||||
|
src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
|
||||||
|
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||||
|
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
|
||||||
|
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||||
|
{
|
||||||
|
// Check for valid range. This may also help detect *some* dangling pointers, because a common
|
||||||
|
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
|
||||||
|
IM_ASSERT(src_range[0] <= src_range[1]);
|
||||||
|
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
|
||||||
|
}
|
||||||
|
dst_tmp.SrcCount++;
|
||||||
|
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
|
||||||
|
int total_glyphs_count = 0;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||||
|
src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
|
||||||
|
if (dst_tmp.GlyphsSet.Storage.empty())
|
||||||
|
dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);
|
||||||
|
|
||||||
|
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||||
|
for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++)
|
||||||
|
{
|
||||||
|
if (dst_tmp.GlyphsSet.TestBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
|
||||||
|
continue;
|
||||||
|
uint32_t glyph_index = FT_Get_Char_Index(src_tmp.Font.Face, codepoint); // It is actually in the font? (FIXME-OPT: We are not storing the glyph_index..)
|
||||||
|
if (glyph_index == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add to avail set/counters
|
||||||
|
src_tmp.GlyphsCount++;
|
||||||
|
dst_tmp.GlyphsCount++;
|
||||||
|
src_tmp.GlyphsSet.SetBit(codepoint);
|
||||||
|
dst_tmp.GlyphsSet.SetBit(codepoint);
|
||||||
|
total_glyphs_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
|
||||||
|
|
||||||
|
IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32));
|
||||||
|
const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin();
|
||||||
|
const ImU32* it_end = src_tmp.GlyphsSet.Storage.end();
|
||||||
|
for (const ImU32* it = it_begin; it < it_end; it++)
|
||||||
|
if (ImU32 entries_32 = *it)
|
||||||
|
for (ImU32 bit_n = 0; bit_n < 32; bit_n++)
|
||||||
|
if (entries_32 & ((ImU32)1 << bit_n))
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT src_glyph;
|
||||||
|
src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
|
||||||
|
//src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
|
||||||
|
src_tmp.GlyphsList.push_back(src_glyph);
|
||||||
|
}
|
||||||
|
src_tmp.GlyphsSet.Clear();
|
||||||
|
IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
|
||||||
|
}
|
||||||
|
for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
|
||||||
|
dst_tmp_array[dst_i].GlyphsSet.Clear();
|
||||||
|
dst_tmp_array.clear();
|
||||||
|
|
||||||
|
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
|
||||||
|
// (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
|
||||||
|
ImVector<stbrp_rect> buf_rects;
|
||||||
|
buf_rects.resize(total_glyphs_count);
|
||||||
|
memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
|
||||||
|
|
||||||
|
// Allocate temporary rasterization data buffers.
|
||||||
|
// We could not find a way to retrieve accurate glyph size without rendering them.
|
||||||
|
// (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform)
|
||||||
|
// We allocate in chunks of 256 KB to not waste too much extra memory ahead. Hopefully users of FreeType won't mind the temporary allocations.
|
||||||
|
const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024;
|
||||||
|
int buf_bitmap_current_used_bytes = 0;
|
||||||
|
ImVector<unsigned char*> buf_bitmap_buffers;
|
||||||
|
buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
|
||||||
|
|
||||||
|
// 4. Gather glyphs sizes so we can pack them in our virtual canvas.
|
||||||
|
// 8. Render/rasterize font characters into the texture
|
||||||
|
int total_surface = 0;
|
||||||
|
int buf_rects_out_n = 0;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
src_tmp.Rects = &buf_rects[buf_rects_out_n];
|
||||||
|
buf_rects_out_n += src_tmp.GlyphsCount;
|
||||||
|
|
||||||
|
// Compute multiply table if requested
|
||||||
|
const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f);
|
||||||
|
unsigned char multiply_table[256];
|
||||||
|
if (multiply_enabled)
|
||||||
|
ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
|
||||||
|
|
||||||
|
// Gather the sizes of all rectangles we will need to pack
|
||||||
|
const int padding = atlas->TexGlyphPadding;
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
|
||||||
|
|
||||||
|
const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
|
||||||
|
if (metrics == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Render glyph into a bitmap (currently held by FreeType)
|
||||||
|
const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info);
|
||||||
|
if (ft_bitmap == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Allocate new temporary chunk if needed
|
||||||
|
const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
|
||||||
|
if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
|
||||||
|
{
|
||||||
|
buf_bitmap_current_used_bytes = 0;
|
||||||
|
buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
|
||||||
|
}
|
||||||
|
IM_ASSERT(buf_bitmap_current_used_bytes + bitmap_size_in_bytes <= BITMAP_BUFFERS_CHUNK_SIZE); // We could probably allocate custom-sized buffer instead.
|
||||||
|
|
||||||
|
// Blit rasterized pixels to our temporary buffer and keep a pointer to it.
|
||||||
|
src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
|
||||||
|
buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
|
||||||
|
src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : nullptr);
|
||||||
|
|
||||||
|
src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
|
||||||
|
src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
|
||||||
|
total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a width for the skyline algorithm, any width!
|
||||||
|
// The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
|
||||||
|
// User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
|
||||||
|
const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
|
||||||
|
atlas->TexHeight = 0;
|
||||||
|
if (atlas->TexDesiredWidth > 0)
|
||||||
|
atlas->TexWidth = atlas->TexDesiredWidth;
|
||||||
|
else
|
||||||
|
atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
|
||||||
|
|
||||||
|
// 5. Start packing
|
||||||
|
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
|
||||||
|
const int TEX_HEIGHT_MAX = 1024 * 32;
|
||||||
|
const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding;
|
||||||
|
ImVector<stbrp_node> pack_nodes;
|
||||||
|
pack_nodes.resize(num_nodes_for_packing_algorithm);
|
||||||
|
stbrp_context pack_context;
|
||||||
|
stbrp_init_target(&pack_context, atlas->TexWidth - atlas->TexGlyphPadding, TEX_HEIGHT_MAX - atlas->TexGlyphPadding, pack_nodes.Data, pack_nodes.Size);
|
||||||
|
ImFontAtlasBuildPackCustomRects(atlas, &pack_context);
|
||||||
|
|
||||||
|
// 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount);
|
||||||
|
|
||||||
|
// Extend texture height and mark missing glyphs as non-packed so we won't render them.
|
||||||
|
// FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||||
|
if (src_tmp.Rects[glyph_i].was_packed)
|
||||||
|
atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Allocate texture
|
||||||
|
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
|
||||||
|
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
|
||||||
|
if (src_load_color)
|
||||||
|
{
|
||||||
|
size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 4;
|
||||||
|
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(tex_size);
|
||||||
|
memset(atlas->TexPixelsRGBA32, 0, tex_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 1;
|
||||||
|
atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(tex_size);
|
||||||
|
memset(atlas->TexPixelsAlpha8, 0, tex_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Copy rasterized font characters back into the main texture
|
||||||
|
// 9. Setup ImFont and glyphs for runtime
|
||||||
|
bool tex_use_colors = false;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// When merging fonts with MergeMode=true:
|
||||||
|
// - We can have multiple input fonts writing into a same destination font.
|
||||||
|
// - dst_font->ConfigData is != from cfg which is our source configuration.
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
ImFont* dst_font = cfg.DstFont;
|
||||||
|
|
||||||
|
const float ascent = src_tmp.Font.Info.Ascender;
|
||||||
|
const float descent = src_tmp.Font.Info.Descender;
|
||||||
|
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
|
||||||
|
const float font_off_x = cfg.GlyphOffset.x;
|
||||||
|
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
|
||||||
|
|
||||||
|
const int padding = atlas->TexGlyphPadding;
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
|
||||||
|
stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
|
||||||
|
IM_ASSERT(pack_rect.was_packed);
|
||||||
|
if (pack_rect.w == 0 && pack_rect.h == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GlyphInfo& info = src_glyph.Info;
|
||||||
|
IM_ASSERT(info.Width + padding <= pack_rect.w);
|
||||||
|
IM_ASSERT(info.Height + padding <= pack_rect.h);
|
||||||
|
const int tx = pack_rect.x + padding;
|
||||||
|
const int ty = pack_rect.y + padding;
|
||||||
|
|
||||||
|
// Register glyph
|
||||||
|
float x0 = info.OffsetX * src_tmp.Font.InvRasterizationDensity + font_off_x;
|
||||||
|
float y0 = info.OffsetY * src_tmp.Font.InvRasterizationDensity + font_off_y;
|
||||||
|
float x1 = x0 + info.Width * src_tmp.Font.InvRasterizationDensity;
|
||||||
|
float y1 = y0 + info.Height * src_tmp.Font.InvRasterizationDensity;
|
||||||
|
float u0 = (tx) / (float)atlas->TexWidth;
|
||||||
|
float v0 = (ty) / (float)atlas->TexHeight;
|
||||||
|
float u1 = (tx + info.Width) / (float)atlas->TexWidth;
|
||||||
|
float v1 = (ty + info.Height) / (float)atlas->TexHeight;
|
||||||
|
dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX * src_tmp.Font.InvRasterizationDensity);
|
||||||
|
|
||||||
|
ImFontGlyph* dst_glyph = &dst_font->Glyphs.back();
|
||||||
|
IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint);
|
||||||
|
if (src_glyph.Info.IsColored)
|
||||||
|
dst_glyph->Colored = tex_use_colors = true;
|
||||||
|
|
||||||
|
// Blit from temporary buffer to final texture
|
||||||
|
size_t blit_src_stride = (size_t)src_glyph.Info.Width;
|
||||||
|
size_t blit_dst_stride = (size_t)atlas->TexWidth;
|
||||||
|
unsigned int* blit_src = src_glyph.BitmapData;
|
||||||
|
if (atlas->TexPixelsAlpha8 != nullptr)
|
||||||
|
{
|
||||||
|
unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
|
||||||
|
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
|
||||||
|
for (int x = 0; x < info.Width; x++)
|
||||||
|
blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
|
||||||
|
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
|
||||||
|
for (int x = 0; x < info.Width; x++)
|
||||||
|
blit_dst[x] = blit_src[x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src_tmp.Rects = nullptr;
|
||||||
|
}
|
||||||
|
atlas->TexPixelsUseColors = tex_use_colors;
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
|
||||||
|
IM_FREE(buf_bitmap_buffers[buf_i]);
|
||||||
|
src_tmp_array.clear_destruct();
|
||||||
|
|
||||||
|
ImFontAtlasBuildFinish(atlas);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeType memory allocation callbacks
|
||||||
|
static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
|
||||||
|
{
|
||||||
|
return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FreeType_Free(FT_Memory /*memory*/, void* block)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
|
||||||
|
{
|
||||||
|
// Implement realloc() as we don't ask user to provide it.
|
||||||
|
if (block == nullptr)
|
||||||
|
return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
|
||||||
|
if (new_size == 0)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_size > cur_size)
|
||||||
|
{
|
||||||
|
void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
memcpy(new_block, block, (size_t)cur_size);
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
return new_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
|
||||||
|
{
|
||||||
|
// FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
|
||||||
|
FT_MemoryRec_ memory_rec = {};
|
||||||
|
memory_rec.user = nullptr;
|
||||||
|
memory_rec.alloc = &FreeType_Alloc;
|
||||||
|
memory_rec.free = &FreeType_Free;
|
||||||
|
memory_rec.realloc = &FreeType_Realloc;
|
||||||
|
|
||||||
|
// https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library
|
||||||
|
FT_Library ft_library;
|
||||||
|
FT_Error error = FT_New_Library(&memory_rec, &ft_library);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
|
||||||
|
FT_Add_Default_Modules(ft_library);
|
||||||
|
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
// Install svg hooks for FreeType
|
||||||
|
// https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks
|
||||||
|
// https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts
|
||||||
|
SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot };
|
||||||
|
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", &hooks);
|
||||||
|
#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
|
||||||
|
FT_Done_Library(ft_library);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType()
|
||||||
|
{
|
||||||
|
static ImFontBuilderIO io;
|
||||||
|
io.FontBuilder_Build = ImFontAtlasBuildWithFreeType;
|
||||||
|
return &io;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeAllocFunc = alloc_func;
|
||||||
|
GImGuiFreeTypeFreeFunc = free_func;
|
||||||
|
GImGuiFreeTypeAllocatorUserData = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
// For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c
|
||||||
|
// The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT)
|
||||||
|
struct LunasvgPortState
|
||||||
|
{
|
||||||
|
FT_Error err = FT_Err_Ok;
|
||||||
|
lunasvg::Matrix matrix;
|
||||||
|
std::unique_ptr<lunasvg::Document> svg = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
*_state = IM_NEW(LunasvgPortState)();
|
||||||
|
return FT_Err_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGuiLunasvgPortFree(FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
IM_DELETE(*(LunasvgPortState**)_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
LunasvgPortState* state = *(LunasvgPortState**)_state;
|
||||||
|
|
||||||
|
// If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error.
|
||||||
|
if (state->err != FT_Err_Ok)
|
||||||
|
return state->err;
|
||||||
|
|
||||||
|
// rows is height, pitch (or stride) equals to width * sizeof(int32)
|
||||||
|
lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
|
||||||
|
state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value
|
||||||
|
state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated
|
||||||
|
state->err = FT_Err_Ok;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
FT_SVG_Document document = (FT_SVG_Document)slot->other;
|
||||||
|
LunasvgPortState* state = *(LunasvgPortState**)_state;
|
||||||
|
FT_Size_Metrics& metrics = document->metrics;
|
||||||
|
|
||||||
|
// This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender().
|
||||||
|
// If it's the latter, don't do anything because it's // already done in the former.
|
||||||
|
if (cache)
|
||||||
|
return state->err;
|
||||||
|
|
||||||
|
state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length);
|
||||||
|
if (state->svg == nullptr)
|
||||||
|
{
|
||||||
|
state->err = FT_Err_Invalid_SVG_Document;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
lunasvg::Box box = state->svg->box();
|
||||||
|
double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h);
|
||||||
|
double xx = (double)document->transform.xx / (1 << 16);
|
||||||
|
double xy = -(double)document->transform.xy / (1 << 16);
|
||||||
|
double yx = -(double)document->transform.yx / (1 << 16);
|
||||||
|
double yy = (double)document->transform.yy / (1 << 16);
|
||||||
|
double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem;
|
||||||
|
double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem;
|
||||||
|
|
||||||
|
// Scale and transform, we don't translate the svg yet
|
||||||
|
state->matrix.identity();
|
||||||
|
state->matrix.scale(scale, scale);
|
||||||
|
state->matrix.transform(xx, xy, yx, yy, x0, y0);
|
||||||
|
state->svg->setMatrix(state->matrix);
|
||||||
|
|
||||||
|
// Pre-translate the matrix for the rendering step
|
||||||
|
state->matrix.translate(-box.x, -box.y);
|
||||||
|
|
||||||
|
// Get the box again after the transformation
|
||||||
|
box = state->svg->box();
|
||||||
|
|
||||||
|
// Calculate the bitmap size
|
||||||
|
slot->bitmap_left = FT_Int(box.x);
|
||||||
|
slot->bitmap_top = FT_Int(-box.y);
|
||||||
|
slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h));
|
||||||
|
slot->bitmap.width = (unsigned int)(ImCeil((float)box.w));
|
||||||
|
slot->bitmap.pitch = slot->bitmap.width * 4;
|
||||||
|
slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
||||||
|
|
||||||
|
// Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box.
|
||||||
|
double metrics_width = box.w;
|
||||||
|
double metrics_height = box.h;
|
||||||
|
double horiBearingX = box.x;
|
||||||
|
double horiBearingY = -box.y;
|
||||||
|
double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0;
|
||||||
|
double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0;
|
||||||
|
slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0)); // Using IM_ROUND() assume width and height are positive
|
||||||
|
slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0));
|
||||||
|
slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64);
|
||||||
|
slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64);
|
||||||
|
slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64);
|
||||||
|
slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64);
|
||||||
|
|
||||||
|
if (slot->metrics.vertAdvance == 0)
|
||||||
|
slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0);
|
||||||
|
|
||||||
|
state->err = FT_Err_Ok;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
52
misc/freetype/imgui_freetype.h
Normal file
52
misc/freetype/imgui_freetype.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||||
|
// (headers)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct ImFontAtlas;
|
||||||
|
struct ImFontBuilderIO;
|
||||||
|
|
||||||
|
// Hinting greatly impacts visuals (and glyph sizes).
|
||||||
|
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
|
||||||
|
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
|
||||||
|
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
|
||||||
|
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
|
||||||
|
// You can set those flags globaly in ImFontAtlas::FontBuilderFlags
|
||||||
|
// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags
|
||||||
|
enum ImGuiFreeTypeBuilderFlags
|
||||||
|
{
|
||||||
|
ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
|
||||||
|
ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
|
||||||
|
ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
|
||||||
|
ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
|
||||||
|
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
|
||||||
|
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
|
||||||
|
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
|
||||||
|
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
|
||||||
|
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
|
||||||
|
ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9 // Enable FreeType bitmap glyphs
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace ImGuiFreeType
|
||||||
|
{
|
||||||
|
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
|
||||||
|
// If you need to dynamically select between multiple builders:
|
||||||
|
// - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()'
|
||||||
|
// - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data.
|
||||||
|
IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType();
|
||||||
|
|
||||||
|
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
|
||||||
|
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
|
||||||
|
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr);
|
||||||
|
|
||||||
|
// Obsolete names (will be removed soon)
|
||||||
|
// Prefer using '#define IMGUI_ENABLE_FREETYPE'
|
||||||
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
29
misc/single_file/imgui_single_file.h
Normal file
29
misc/single_file/imgui_single_file.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// dear imgui: single-file wrapper include
|
||||||
|
// We use this to validate compiling all *.cpp files in a same compilation unit.
|
||||||
|
// Users of that technique (also called "Unity builds") can generally provide this themselves,
|
||||||
|
// so we don't really recommend you use this in your projects.
|
||||||
|
|
||||||
|
// Do this:
|
||||||
|
// #define IMGUI_IMPLEMENTATION
|
||||||
|
// Before you include this file in *one* C++ file to create the implementation.
|
||||||
|
// Using this in your project will leak the contents of imgui_internal.h and ImVec2 operators in this compilation unit.
|
||||||
|
|
||||||
|
#ifdef IMGUI_IMPLEMENTATION
|
||||||
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../../imgui.h"
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE
|
||||||
|
#include "../../misc/freetype/imgui_freetype.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IMGUI_IMPLEMENTATION
|
||||||
|
#include "../../imgui.cpp"
|
||||||
|
#include "../../imgui_demo.cpp"
|
||||||
|
#include "../../imgui_draw.cpp"
|
||||||
|
#include "../../imgui_tables.cpp"
|
||||||
|
#include "../../imgui_widgets.cpp"
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE
|
||||||
|
#include "../../misc/freetype/imgui_freetype.cpp"
|
||||||
|
#endif
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user