diff --git a/CMakeLists.txt b/CMakeLists.txt index 74d622bea..82094ce00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -495,6 +495,7 @@ src/gui/editing.cpp src/gui/editControls.cpp src/gui/effectList.cpp src/gui/findReplace.cpp +src/gui/gradient.cpp src/gui/insEdit.cpp src/gui/log.cpp src/gui/mixer.cpp diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 0626c3552..d9d3ddd49 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -34,36 +34,93 @@ void FurnaceGUI::drawChanOsc() { } if (!chanOscOpen) return; ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen,globalWinFlags)) { + if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen,globalWinFlags|((chanOscOptions)?0:ImGuiWindowFlags_NoTitleBar))) { bool centerSettingReset=false; - if (ImGui::BeginTable("ChanOscSettings",3)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Columns"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##COSColumns",&chanOscCols,1,1)) { - if (chanOscCols<1) chanOscCols=1; - if (chanOscCols>64) chanOscCols=64; + if (chanOscOptions) { + if (ImGui::BeginTable("ChanOscSettings",3)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Columns"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##COSColumns",&chanOscCols,1,1)) { + if (chanOscCols<1) chanOscCols=1; + if (chanOscCols>64) chanOscCols=64; + } + + ImGui::TableNextColumn(); + ImGui::Text("Size (ms)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputFloat("##COSWinSize",&chanOscWindowSize,1.0f,1.0f)) { + if (chanOscWindowSize<1.0f) chanOscWindowSize=1.0f; + if (chanOscWindowSize>50.0f) chanOscWindowSize=50.0f; + } + + ImGui::TableNextColumn(); + if (ImGui::Checkbox("Center waveform",&chanOscWaveCorr)) { + centerSettingReset=true; + } + + ImGui::EndTable(); } - ImGui::TableNextColumn(); - ImGui::Text("Size (ms)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputFloat("##COSWinSize",&chanOscWindowSize,1.0f,1.0f)) { - if (chanOscWindowSize<1.0f) chanOscWindowSize=1.0f; - if (chanOscWindowSize>50.0f) chanOscWindowSize=50.0f; + ImGui::Text("Gradient"); + + if (chanOscGradTex==NULL) { + chanOscGradTex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STREAMING,chanOscGrad.width,chanOscGrad.height); + + if (chanOscGradTex==NULL) { + logE("error while creating gradient texture! %s",SDL_GetError()); + } else { + updateChanOscGradTex=true; + } } - ImGui::TableNextColumn(); - if (ImGui::Checkbox("Center waveform",&chanOscWaveCorr)) { - centerSettingReset=true; - } + if (ImGui::BeginTable("ChanOscGradSet",2)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); - ImGui::EndTable(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (chanOscGradTex!=NULL) { + if (updateChanOscGradTex) { + chanOscGrad.render(); + if (SDL_UpdateTexture(chanOscGradTex,NULL,chanOscGrad.grad.get(),chanOscGrad.width*4)==0) { + updateChanOscGradTex=false; + } else { + logE("error while updating gradient texture! %s",SDL_GetError()); + } + } + + ImGui::ImageButton(chanOscGradTex,ImVec2(400.0f*dpiScale,400.0f*dpiScale)); + ImVec2 gradLeft=ImGui::GetItemRectMin(); + ImVec2 gradSize=ImGui::GetItemRectSize(); + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + chanOscGrad.points.push_back(Gradient2DPoint( + (ImGui::GetMousePos().x-gradLeft.x)/gradSize.x, + (ImGui::GetMousePos().y-gradLeft.y)/gradSize.y + )); + updateChanOscGradTex=true; + logI("a point inserted"); + } + + ImVec2 oldCurPos=ImGui::GetCursorPos(); + for (Gradient2DPoint& i: chanOscGrad.points) { + ImGui::SetCursorPos(ImVec2(gradLeft.x+i.x*gradSize.x,gradLeft.y+i.y*gradSize.y)); + ImGui::Text("Here"); + } + ImGui::SetCursorPos(oldCurPos); + } + + ImGui::TableNextColumn(); + if (ImGui::ColorEdit4("Background",(float*)&chanOscGrad.bgColor)) { + updateChanOscGradTex=true; + } + + ImGui::EndTable(); + } } - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f)); float availY=ImGui::GetContentRegionAvail().y; @@ -232,6 +289,10 @@ void FurnaceGUI::drawChanOsc() { } } ImGui::EndTable(); + + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + chanOscOptions=!chanOscOptions; + } } ImGui::PopStyleVar(); } diff --git a/src/gui/gradient.cpp b/src/gui/gradient.cpp new file mode 100644 index 000000000..c9b26fb80 --- /dev/null +++ b/src/gui/gradient.cpp @@ -0,0 +1,70 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "imgui.h" +#include + +void Gradient2D::render() { + ImU32* g=grad.get(); + ImU32 bgColorU=ImGui::ColorConvertFloat4ToU32(bgColor); + + // 1. fill with background color + for (size_t i=0; i=pDistSquared) continue; + + float dist=(1.0-(sqrt(distSquared)/i.distance))-i.spread; + if (dist<0) dist=0; + if (dist>1) dist=1; + + ImU32 shadeColor=ImGui::ColorConvertFloat4ToU32( + ImVec4( + i.color.x*i.color.w*dist, + i.color.y*i.color.w*dist, + i.color.z*i.color.w*dist, + 1.0f + ) + ); + + ImU32 origColor=g[j*width+k]; + g[j*width+k]=( + (MIN( 0xff, (origColor&0xff) + (shadeColor&0xff) )) | // R + (MIN( 0xff00, (origColor&0xff00) + (shadeColor&0xff00) )) | // G + (MIN(0xff0000,(origColor&0xff0000)+(shadeColor&0xff0000))) | // B + (origColor&0xff000000) // A + ); + } + } + } +} \ No newline at end of file diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index bb33073a3..fbfc2b4d9 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4575,6 +4575,10 @@ FurnaceGUI::FurnaceGUI(): chanOscCols(3), chanOscWindowSize(20.0f), chanOscWaveCorr(true), + chanOscOptions(false), + updateChanOscGradTex(true), + chanOscGrad(64,64), + chanOscGradTex(NULL), followLog(true), #ifdef IS_MOBILE pianoOctaves(7), @@ -4664,4 +4668,6 @@ FurnaceGUI::FurnaceGUI(): memset(queryReplaceEffectVal,0,sizeof(int)*8); memset(queryReplaceEffectDo,0,sizeof(bool)*8); memset(queryReplaceEffectValDo,0,sizeof(bool)*8); + + chanOscGrad.bgColor=ImVec4(0.0f,0.0f,0.0f,1.0f); } diff --git a/src/gui/gui.h b/src/gui/gui.h index 271b8eaa4..a538c5ba5 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -757,6 +758,45 @@ struct TouchPoint { z(pressure) {} }; +struct Gradient2DPoint { + ImVec4 color; + float x, y; + float spread, distance; + bool selected, grab; + Gradient2DPoint(float xPos, float yPos): + color(1,1,1,1), + x(xPos), + y(yPos), + spread(0.0f), + distance(0.5f), + selected(false), + grab(false) {} + Gradient2DPoint(): + color(1,1,1,1), + x(0.0f), + y(0.0f), + spread(0.0f), + distance(0.5f), + selected(false), + grab(false) {} +}; + +struct Gradient2D { + ImVec4 bgColor; + std::vector points; + std::unique_ptr grad; + size_t width, height; + + void render(); + ImU32 get(float x, float y); + Gradient2D(size_t w, size_t h): + bgColor(0.0f,0.0f,0.0f,0.0f), + width(w), + height(h) { + grad=std::make_unique(width*height); + } +}; + struct FurnaceGUISysDef { const char* name; std::vector definition; @@ -1344,7 +1384,9 @@ class FurnaceGUI { // per-channel oscilloscope int chanOscCols; float chanOscWindowSize; - bool chanOscWaveCorr; + bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex; + Gradient2D chanOscGrad; + SDL_Texture* chanOscGradTex; float chanOscLP0[DIV_MAX_CHANS]; float chanOscLP1[DIV_MAX_CHANS]; unsigned short lastNeedlePos[DIV_MAX_CHANS];