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