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