1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "ImGuiLayer.h" 9 10 #include "SkCanvas.h" 11 #include "SkImage.h" 12 #include "SkPixmap.h" 13 #include "SkSurface.h" 14 #include "SkSwizzle.h" 15 #include "SkTime.h" 16 #include "SkVertices.h" 17 18 #include "imgui.h" 19 20 #include <stdlib.h> 21 #include <map> 22 23 using namespace sk_app; 24 25 ImGuiLayer::ImGuiLayer() { 26 // ImGui initialization: 27 ImGui::CreateContext(); 28 ImGuiIO& io = ImGui::GetIO(); 29 30 // Keymap... 31 io.KeyMap[ImGuiKey_Tab] = (int)Window::Key::kTab; 32 io.KeyMap[ImGuiKey_LeftArrow] = (int)Window::Key::kLeft; 33 io.KeyMap[ImGuiKey_RightArrow] = (int)Window::Key::kRight; 34 io.KeyMap[ImGuiKey_UpArrow] = (int)Window::Key::kUp; 35 io.KeyMap[ImGuiKey_DownArrow] = (int)Window::Key::kDown; 36 io.KeyMap[ImGuiKey_PageUp] = (int)Window::Key::kPageUp; 37 io.KeyMap[ImGuiKey_PageDown] = (int)Window::Key::kPageDown; 38 io.KeyMap[ImGuiKey_Home] = (int)Window::Key::kHome; 39 io.KeyMap[ImGuiKey_End] = (int)Window::Key::kEnd; 40 io.KeyMap[ImGuiKey_Delete] = (int)Window::Key::kDelete; 41 io.KeyMap[ImGuiKey_Backspace] = (int)Window::Key::kBack; 42 io.KeyMap[ImGuiKey_Enter] = (int)Window::Key::kOK; 43 io.KeyMap[ImGuiKey_Escape] = (int)Window::Key::kEscape; 44 io.KeyMap[ImGuiKey_A] = (int)Window::Key::kA; 45 io.KeyMap[ImGuiKey_C] = (int)Window::Key::kC; 46 io.KeyMap[ImGuiKey_V] = (int)Window::Key::kV; 47 io.KeyMap[ImGuiKey_X] = (int)Window::Key::kX; 48 io.KeyMap[ImGuiKey_Y] = (int)Window::Key::kY; 49 io.KeyMap[ImGuiKey_Z] = (int)Window::Key::kZ; 50 51 int w, h; 52 unsigned char* pixels; 53 io.Fonts->GetTexDataAsAlpha8(&pixels, &w, &h); 54 SkImageInfo info = SkImageInfo::MakeA8(w, h); 55 SkPixmap pmap(info, pixels, info.minRowBytes()); 56 SkMatrix localMatrix = SkMatrix::MakeScale(1.0f / w, 1.0f / h); 57 auto fontImage = SkImage::MakeFromRaster(pmap, nullptr, nullptr); 58 auto fontShader = fontImage->makeShader(&localMatrix); 59 fFontPaint.setShader(fontShader); 60 fFontPaint.setColor(SK_ColorWHITE); 61 fFontPaint.setFilterQuality(kLow_SkFilterQuality); 62 io.Fonts->TexID = &fFontPaint; 63 } 64 65 ImGuiLayer::~ImGuiLayer() { 66 ImGui::DestroyContext(); 67 } 68 69 void ImGuiLayer::onAttach(Window* window) { 70 fWindow = window; 71 } 72 73 bool ImGuiLayer::onMouse(int x, int y, Window::InputState state, uint32_t modifiers) { 74 ImGuiIO& io = ImGui::GetIO(); 75 io.MousePos.x = static_cast<float>(x); 76 io.MousePos.y = static_cast<float>(y); 77 if (Window::kDown_InputState == state) { 78 io.MouseDown[0] = true; 79 } else if (Window::kUp_InputState == state) { 80 io.MouseDown[0] = false; 81 } 82 return io.WantCaptureMouse; 83 } 84 85 bool ImGuiLayer::onMouseWheel(float delta, uint32_t modifiers) { 86 ImGuiIO& io = ImGui::GetIO(); 87 io.MouseWheel += delta; 88 return true; 89 } 90 91 void ImGuiLayer::skiaWidget(const ImVec2& size, SkiaWidgetFunc func) { 92 intptr_t funcIndex = fSkiaWidgetFuncs.count(); 93 fSkiaWidgetFuncs.push_back(func); 94 ImGui::Image((ImTextureID)funcIndex, size); 95 } 96 97 void ImGuiLayer::onPrePaint() { 98 // Update ImGui input 99 ImGuiIO& io = ImGui::GetIO(); 100 101 static double previousTime = 0.0; 102 double currentTime = SkTime::GetSecs(); 103 io.DeltaTime = static_cast<float>(currentTime - previousTime); 104 previousTime = currentTime; 105 106 io.DisplaySize.x = static_cast<float>(fWindow->width()); 107 io.DisplaySize.y = static_cast<float>(fWindow->height()); 108 109 io.KeyAlt = io.KeysDown[static_cast<int>(Window::Key::kOption)]; 110 io.KeyCtrl = io.KeysDown[static_cast<int>(Window::Key::kCtrl)]; 111 io.KeyShift = io.KeysDown[static_cast<int>(Window::Key::kShift)]; 112 113 ImGui::NewFrame(); 114 } 115 116 void ImGuiLayer::onPaint(SkSurface* surface) { 117 // This causes ImGui to rebuild vertex/index data based on all immediate-mode commands 118 // (widgets, etc...) that have been issued 119 ImGui::Render(); 120 121 // Then we fetch the most recent data, and convert it so we can render with Skia 122 const ImDrawData* drawData = ImGui::GetDrawData(); 123 SkTDArray<SkPoint> pos; 124 SkTDArray<SkPoint> uv; 125 SkTDArray<SkColor> color; 126 127 auto canvas = surface->getCanvas(); 128 129 for (int i = 0; i < drawData->CmdListsCount; ++i) { 130 const ImDrawList* drawList = drawData->CmdLists[i]; 131 132 // De-interleave all vertex data (sigh), convert to Skia types 133 pos.rewind(); uv.rewind(); color.rewind(); 134 for (int j = 0; j < drawList->VtxBuffer.size(); ++j) { 135 const ImDrawVert& vert = drawList->VtxBuffer[j]; 136 pos.push_back(SkPoint::Make(vert.pos.x, vert.pos.y)); 137 uv.push_back(SkPoint::Make(vert.uv.x, vert.uv.y)); 138 color.push_back(vert.col); 139 } 140 // ImGui colors are RGBA 141 SkSwapRB(color.begin(), color.begin(), color.count()); 142 143 int indexOffset = 0; 144 145 // Draw everything with canvas.drawVertices... 146 for (int j = 0; j < drawList->CmdBuffer.size(); ++j) { 147 const ImDrawCmd* drawCmd = &drawList->CmdBuffer[j]; 148 149 SkAutoCanvasRestore acr(canvas, true); 150 151 // TODO: Find min/max index for each draw, so we know how many vertices (sigh) 152 if (drawCmd->UserCallback) { 153 drawCmd->UserCallback(drawList, drawCmd); 154 } else { 155 intptr_t idIndex = (intptr_t)drawCmd->TextureId; 156 if (idIndex < fSkiaWidgetFuncs.count()) { 157 // Small image IDs are actually indices into a list of callbacks. We directly 158 // examing the vertex data to deduce the image rectangle, then reconfigure the 159 // canvas to be clipped and translated so that the callback code gets to use 160 // Skia to render a widget in the middle of an ImGui panel. 161 ImDrawIdx rectIndex = drawList->IdxBuffer[indexOffset]; 162 SkPoint tl = pos[rectIndex], br = pos[rectIndex + 2]; 163 canvas->clipRect(SkRect::MakeLTRB(tl.fX, tl.fY, br.fX, br.fY)); 164 canvas->translate(tl.fX, tl.fY); 165 fSkiaWidgetFuncs[idIndex](canvas); 166 } else { 167 SkPaint* paint = static_cast<SkPaint*>(drawCmd->TextureId); 168 SkASSERT(paint); 169 170 canvas->clipRect(SkRect::MakeLTRB(drawCmd->ClipRect.x, drawCmd->ClipRect.y, 171 drawCmd->ClipRect.z, drawCmd->ClipRect.w)); 172 auto vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 173 drawList->VtxBuffer.size(), 174 pos.begin(), uv.begin(), color.begin(), 175 drawCmd->ElemCount, 176 drawList->IdxBuffer.begin() + indexOffset); 177 canvas->drawVertices(vertices, SkBlendMode::kModulate, *paint); 178 } 179 indexOffset += drawCmd->ElemCount; 180 } 181 } 182 } 183 184 fSkiaWidgetFuncs.reset(); 185 } 186 187 bool ImGuiLayer::onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers) { 188 ImGuiIO& io = ImGui::GetIO(); 189 io.KeysDown[static_cast<int>(key)] = (Window::kDown_InputState == state); 190 return io.WantCaptureKeyboard; 191 } 192 193 bool ImGuiLayer::onChar(SkUnichar c, uint32_t modifiers) { 194 ImGuiIO& io = ImGui::GetIO(); 195 if (io.WantTextInput) { 196 if (c > 0 && c < 0x10000) { 197 io.AddInputCharacter(c); 198 } 199 return true; 200 } 201 return false; 202 } 203