mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-18 14:08:19 +00:00
Merge pull request #7744 from Robyt3/Editor-Color-Palette-Pipette
Add color palette and pipette to editor
This commit is contained in:
commit
8dee975d66
|
@ -313,8 +313,9 @@ bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand)
|
|||
{
|
||||
m_IsOpenGLES = pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL_ES;
|
||||
|
||||
TGLBackendReadPresentedImageData &ReadPresentedImgDataFunc = *pCommand->m_pReadPresentedImageDataFunc;
|
||||
ReadPresentedImgDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) { return GetPresentedImageData(Width, Height, Format, vDstData); };
|
||||
*pCommand->m_pReadPresentedImageDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) {
|
||||
return GetPresentedImageData(Width, Height, Format, vDstData);
|
||||
};
|
||||
|
||||
const char *pVendorString = (const char *)glGetString(GL_VENDOR);
|
||||
dbg_msg("opengl", "Vendor string: %s", pVendorString);
|
||||
|
@ -983,10 +984,27 @@ void CCommandProcessorFragment_OpenGL::Cmd_Render(const CCommandBuffer::SCommand
|
|||
#endif
|
||||
}
|
||||
|
||||
void CCommandProcessorFragment_OpenGL::Cmd_ReadPixel(const CCommandBuffer::SCommand_TrySwapAndReadPixel *pCommand)
|
||||
{
|
||||
// get size of viewport
|
||||
GLint aViewport[4] = {0, 0, 0, 0};
|
||||
glGetIntegerv(GL_VIEWPORT, aViewport);
|
||||
const int h = aViewport[3];
|
||||
|
||||
// fetch the pixel
|
||||
uint8_t aPixelData[3];
|
||||
GLint Alignment;
|
||||
glGetIntegerv(GL_PACK_ALIGNMENT, &Alignment);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(pCommand->m_Position.x, h - 1 - pCommand->m_Position.y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, aPixelData);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, Alignment);
|
||||
|
||||
// fill in the information
|
||||
*pCommand->m_pColor = ColorRGBA(aPixelData[0] / 255.0f, aPixelData[1] / 255.0f, aPixelData[2] / 255.0f, 1.0f);
|
||||
}
|
||||
|
||||
void CCommandProcessorFragment_OpenGL::Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand)
|
||||
{
|
||||
*pCommand->m_pSwapped = false;
|
||||
|
||||
// fetch image data
|
||||
GLint aViewport[4] = {0, 0, 0, 0};
|
||||
glGetIntegerv(GL_VIEWPORT, aViewport);
|
||||
|
@ -1068,6 +1086,9 @@ ERunCommandReturnTypes CCommandProcessorFragment_OpenGL::RunCommand(const CComma
|
|||
case CCommandBuffer::CMD_RENDER_TEX3D:
|
||||
Cmd_RenderTex3D(static_cast<const CCommandBuffer::SCommand_RenderTex3D *>(pBaseCommand));
|
||||
break;
|
||||
case CCommandBuffer::CMD_TRY_SWAP_AND_READ_PIXEL:
|
||||
Cmd_ReadPixel(static_cast<const CCommandBuffer::SCommand_TrySwapAndReadPixel *>(pBaseCommand));
|
||||
break;
|
||||
case CCommandBuffer::CMD_TRY_SWAP_AND_SCREENSHOT:
|
||||
Cmd_Screenshot(static_cast<const CCommandBuffer::SCommand_TrySwapAndScreenshot *>(pBaseCommand));
|
||||
break;
|
||||
|
|
|
@ -99,6 +99,7 @@ protected:
|
|||
virtual void Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand);
|
||||
virtual void Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand);
|
||||
virtual void Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderTex3D"); }
|
||||
virtual void Cmd_ReadPixel(const CCommandBuffer::SCommand_TrySwapAndReadPixel *pCommand);
|
||||
virtual void Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand);
|
||||
|
||||
virtual void Cmd_Update_Viewport(const CCommandBuffer::SCommand_Update_Viewport *pCommand);
|
||||
|
|
|
@ -15,26 +15,22 @@
|
|||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL_video.h>
|
||||
#include <SDL_vulkan.h>
|
||||
|
@ -904,6 +900,7 @@ class CCommandProcessorFragment_Vulkan : public CCommandProcessorFragment_GLBase
|
|||
|
||||
uint32_t m_MinUniformAlign;
|
||||
|
||||
std::vector<uint8_t> m_vReadPixelHelper;
|
||||
std::vector<uint8_t> m_vScreenshotHelper;
|
||||
|
||||
SDeviceMemoryBlock m_GetPresentedImgDataHelperMem;
|
||||
|
@ -1278,6 +1275,7 @@ protected:
|
|||
|
||||
m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_VSYNC)] = {false, [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_VSync(static_cast<const CCommandBuffer::SCommand_VSync *>(pBaseCommand)); }};
|
||||
m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_MULTISAMPLING)] = {false, [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_MultiSampling(static_cast<const CCommandBuffer::SCommand_MultiSampling *>(pBaseCommand)); }};
|
||||
m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_TRY_SWAP_AND_READ_PIXEL)] = {false, [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_ReadPixel(static_cast<const CCommandBuffer::SCommand_TrySwapAndReadPixel *>(pBaseCommand)); }};
|
||||
m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_TRY_SWAP_AND_SCREENSHOT)] = {false, [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Screenshot(static_cast<const CCommandBuffer::SCommand_TrySwapAndScreenshot *>(pBaseCommand)); }};
|
||||
|
||||
m_aCommandCallbacks[CommandBufferCMDOff(CCommandBuffer::CMD_UPDATE_VIEWPORT)] = {false, [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_Update_Viewport_FillExecuteBuffer(ExecBuffer, static_cast<const CCommandBuffer::SCommand_Update_Viewport *>(pBaseCommand)); }, [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Update_Viewport(static_cast<const CCommandBuffer::SCommand_Update_Viewport *>(pBaseCommand)); }};
|
||||
|
@ -1378,15 +1376,29 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool GetPresentedImageDataImpl(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData, bool ResetAlpha)
|
||||
[[nodiscard]] bool GetPresentedImageDataImpl(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData, bool ResetAlpha, std::optional<ivec2> PixelOffset)
|
||||
{
|
||||
bool IsB8G8R8A8 = m_VKSurfFormat.format == VK_FORMAT_B8G8R8A8_UNORM;
|
||||
bool UsesRGBALikeFormat = m_VKSurfFormat.format == VK_FORMAT_R8G8B8A8_UNORM || IsB8G8R8A8;
|
||||
if(UsesRGBALikeFormat && m_LastPresentedSwapChainImageIndex != std::numeric_limits<decltype(m_LastPresentedSwapChainImageIndex)>::max())
|
||||
{
|
||||
auto Viewport = m_VKSwapImgAndViewportExtent.GetPresentedImageViewport();
|
||||
Width = Viewport.width;
|
||||
Height = Viewport.height;
|
||||
VkOffset3D SrcOffset;
|
||||
if(PixelOffset.has_value())
|
||||
{
|
||||
SrcOffset.x = PixelOffset.value().x;
|
||||
SrcOffset.y = PixelOffset.value().y;
|
||||
Width = 1;
|
||||
Height = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
SrcOffset.x = 0;
|
||||
SrcOffset.y = 0;
|
||||
Width = Viewport.width;
|
||||
Height = Viewport.height;
|
||||
}
|
||||
SrcOffset.z = 0;
|
||||
Format = CImageInfo::FORMAT_RGBA;
|
||||
|
||||
const size_t ImageTotalSize = (size_t)Width * Height * CImageInfo::PixelSize(Format);
|
||||
|
@ -1414,9 +1426,11 @@ protected:
|
|||
BlitSize.x = Width;
|
||||
BlitSize.y = Height;
|
||||
BlitSize.z = 1;
|
||||
|
||||
VkImageBlit ImageBlitRegion{};
|
||||
ImageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
ImageBlitRegion.srcSubresource.layerCount = 1;
|
||||
ImageBlitRegion.srcOffsets[0] = SrcOffset;
|
||||
ImageBlitRegion.srcOffsets[1] = BlitSize;
|
||||
ImageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
ImageBlitRegion.dstSubresource.layerCount = 1;
|
||||
|
@ -1436,6 +1450,7 @@ protected:
|
|||
VkImageCopy ImageCopyRegion{};
|
||||
ImageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
ImageCopyRegion.srcSubresource.layerCount = 1;
|
||||
ImageCopyRegion.srcOffset = SrcOffset;
|
||||
ImageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
ImageCopyRegion.dstSubresource.layerCount = 1;
|
||||
ImageCopyRegion.extent.width = Width;
|
||||
|
@ -1458,7 +1473,6 @@ protected:
|
|||
|
||||
VkSubmitInfo SubmitInfo{};
|
||||
SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
|
||||
SubmitInfo.commandBufferCount = 1;
|
||||
SubmitInfo.pCommandBuffers = &CommandBuffer;
|
||||
|
||||
|
@ -1525,7 +1539,7 @@ protected:
|
|||
|
||||
[[nodiscard]] bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) override
|
||||
{
|
||||
return GetPresentedImageDataImpl(Width, Height, Format, vDstData, false);
|
||||
return GetPresentedImageDataImpl(Width, Height, Format, vDstData, false, {});
|
||||
}
|
||||
|
||||
/************************
|
||||
|
@ -6523,8 +6537,9 @@ public:
|
|||
|
||||
m_MultiSamplingCount = (g_Config.m_GfxFsaaSamples & 0xFFFFFFFE); // ignore the uneven bit, only even multi sampling works
|
||||
|
||||
TGLBackendReadPresentedImageData &ReadPresentedImgDataFunc = *pCommand->m_pReadPresentedImageDataFunc;
|
||||
ReadPresentedImgDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) { return GetPresentedImageData(Width, Height, Format, vDstData); };
|
||||
*pCommand->m_pReadPresentedImageDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) {
|
||||
return GetPresentedImageData(Width, Height, Format, vDstData);
|
||||
};
|
||||
|
||||
m_pWindow = pCommand->m_pWindow;
|
||||
|
||||
|
@ -6748,18 +6763,39 @@ public:
|
|||
return RenderStandard<CCommandBuffer::SVertex, false>(ExecBuffer, pCommand->m_State, pCommand->m_PrimType, pCommand->m_pVertices, pCommand->m_PrimCount);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand)
|
||||
[[nodiscard]] bool Cmd_ReadPixel(const CCommandBuffer::SCommand_TrySwapAndReadPixel *pCommand)
|
||||
{
|
||||
if(!NextFrame())
|
||||
if(!*pCommand->m_pSwapped && !NextFrame())
|
||||
return false;
|
||||
*pCommand->m_pSwapped = true;
|
||||
|
||||
uint32_t Width;
|
||||
uint32_t Height;
|
||||
CImageInfo::EImageFormat Format;
|
||||
if(GetPresentedImageDataImpl(Width, Height, Format, m_vScreenshotHelper, true))
|
||||
if(GetPresentedImageDataImpl(Width, Height, Format, m_vReadPixelHelper, false, pCommand->m_Position))
|
||||
{
|
||||
size_t ImgSize = (size_t)Width * (size_t)Height * (size_t)4;
|
||||
*pCommand->m_pColor = ColorRGBA(m_vReadPixelHelper[0] / 255.0f, m_vReadPixelHelper[1] / 255.0f, m_vReadPixelHelper[2] / 255.0f, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
*pCommand->m_pColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand)
|
||||
{
|
||||
if(!*pCommand->m_pSwapped && !NextFrame())
|
||||
return false;
|
||||
*pCommand->m_pSwapped = true;
|
||||
|
||||
uint32_t Width;
|
||||
uint32_t Height;
|
||||
CImageInfo::EImageFormat Format;
|
||||
if(GetPresentedImageDataImpl(Width, Height, Format, m_vScreenshotHelper, true, {}))
|
||||
{
|
||||
const size_t ImgSize = (size_t)Width * (size_t)Height * CImageInfo::PixelSize(Format);
|
||||
pCommand->m_pImage->m_pData = malloc(ImgSize);
|
||||
mem_copy(pCommand->m_pImage->m_pData, m_vScreenshotHelper.data(), ImgSize);
|
||||
}
|
||||
|
|
|
@ -809,19 +809,21 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
bool CGraphics_Threaded::ScreenshotDirect()
|
||||
void CGraphics_Threaded::ScreenshotDirect(bool *pSwapped)
|
||||
{
|
||||
// add swap command
|
||||
CImageInfo Image;
|
||||
if(!m_DoScreenshot)
|
||||
return;
|
||||
m_DoScreenshot = false;
|
||||
if(!WindowActive())
|
||||
return;
|
||||
|
||||
bool DidSwap = false;
|
||||
CImageInfo Image;
|
||||
|
||||
CCommandBuffer::SCommand_TrySwapAndScreenshot Cmd;
|
||||
Cmd.m_pImage = &Image;
|
||||
Cmd.m_pSwapped = &DidSwap;
|
||||
Cmd.m_pSwapped = pSwapped;
|
||||
AddCmd(Cmd);
|
||||
|
||||
// kick the buffer and wait for the result
|
||||
KickCommandBuffer();
|
||||
WaitForIdle();
|
||||
|
||||
|
@ -829,8 +831,6 @@ bool CGraphics_Threaded::ScreenshotDirect()
|
|||
{
|
||||
m_pEngine->AddJob(std::make_shared<CScreenshotSaveJob>(m_pStorage, m_pConsole, m_aScreenshotName, Image.m_Width, Image.m_Height, Image.m_pData));
|
||||
}
|
||||
|
||||
return DidSwap;
|
||||
}
|
||||
|
||||
void CGraphics_Threaded::TextureSet(CTextureHandle TextureID)
|
||||
|
@ -2780,6 +2780,32 @@ void CGraphics_Threaded::NotifyWindow()
|
|||
return m_pBackend->NotifyWindow();
|
||||
}
|
||||
|
||||
void CGraphics_Threaded::ReadPixel(ivec2 Position, ColorRGBA *pColor)
|
||||
{
|
||||
dbg_assert(Position.x >= 0 && Position.x < ScreenWidth(), "ReadPixel position x out of range");
|
||||
dbg_assert(Position.y >= 0 && Position.y < ScreenHeight(), "ReadPixel position y out of range");
|
||||
|
||||
m_ReadPixelPosition = Position;
|
||||
m_pReadPixelColor = pColor;
|
||||
}
|
||||
|
||||
void CGraphics_Threaded::ReadPixelDirect(bool *pSwapped)
|
||||
{
|
||||
if(m_pReadPixelColor == nullptr)
|
||||
return;
|
||||
|
||||
CCommandBuffer::SCommand_TrySwapAndReadPixel Cmd;
|
||||
Cmd.m_Position = m_ReadPixelPosition;
|
||||
Cmd.m_pColor = m_pReadPixelColor;
|
||||
Cmd.m_pSwapped = pSwapped;
|
||||
AddCmd(Cmd);
|
||||
|
||||
KickCommandBuffer();
|
||||
WaitForIdle();
|
||||
|
||||
m_pReadPixelColor = nullptr;
|
||||
}
|
||||
|
||||
void CGraphics_Threaded::TakeScreenshot(const char *pFilename)
|
||||
{
|
||||
// TODO: screenshot support
|
||||
|
@ -2806,23 +2832,16 @@ void CGraphics_Threaded::Swap()
|
|||
}
|
||||
}
|
||||
|
||||
bool TookScreenshotAndSwapped = false;
|
||||
bool Swapped = false;
|
||||
ScreenshotDirect(&Swapped);
|
||||
ReadPixelDirect(&Swapped);
|
||||
|
||||
if(m_DoScreenshot)
|
||||
if(!Swapped)
|
||||
{
|
||||
if(WindowActive())
|
||||
TookScreenshotAndSwapped = ScreenshotDirect();
|
||||
m_DoScreenshot = false;
|
||||
}
|
||||
|
||||
if(!TookScreenshotAndSwapped)
|
||||
{
|
||||
// add swap command
|
||||
CCommandBuffer::SCommand_Swap Cmd;
|
||||
AddCmd(Cmd);
|
||||
}
|
||||
|
||||
// kick the command buffer
|
||||
KickCommandBuffer();
|
||||
// TODO: Remove when https://github.com/libsdl-org/SDL/issues/5203 is fixed
|
||||
#ifdef CONF_PLATFORM_MACOS
|
||||
|
|
|
@ -130,6 +130,7 @@ public:
|
|||
// misc
|
||||
CMD_MULTISAMPLING,
|
||||
CMD_VSYNC,
|
||||
CMD_TRY_SWAP_AND_READ_PIXEL,
|
||||
CMD_TRY_SWAP_AND_SCREENSHOT,
|
||||
CMD_UPDATE_VIEWPORT,
|
||||
|
||||
|
@ -462,12 +463,21 @@ public:
|
|||
void *m_pOffset;
|
||||
};
|
||||
|
||||
struct SCommand_TrySwapAndReadPixel : public SCommand
|
||||
{
|
||||
SCommand_TrySwapAndReadPixel() :
|
||||
SCommand(CMD_TRY_SWAP_AND_READ_PIXEL) {}
|
||||
ivec2 m_Position;
|
||||
SColorf *m_pColor; // processor will fill this out
|
||||
bool *m_pSwapped; // processor may set this to true, must be initialized to false by the caller
|
||||
};
|
||||
|
||||
struct SCommand_TrySwapAndScreenshot : public SCommand
|
||||
{
|
||||
SCommand_TrySwapAndScreenshot() :
|
||||
SCommand(CMD_TRY_SWAP_AND_SCREENSHOT) {}
|
||||
CImageInfo *m_pImage; // processor will fill this out, the one who adds this command must free the data as well
|
||||
bool *m_pSwapped;
|
||||
bool *m_pSwapped; // processor may set this to true, must be initialized to false by the caller
|
||||
};
|
||||
|
||||
struct SCommand_Swap : public SCommand
|
||||
|
@ -917,6 +927,11 @@ class CGraphics_Threaded : public IEngineGraphics
|
|||
|
||||
void AdjustViewport(bool SendViewportChangeToBackend);
|
||||
|
||||
ivec2 m_ReadPixelPosition = ivec2(0, 0);
|
||||
ColorRGBA *m_pReadPixelColor = nullptr;
|
||||
void ReadPixelDirect(bool *pSwapped);
|
||||
void ScreenshotDirect(bool *pSwapped);
|
||||
|
||||
int IssueInit();
|
||||
int InitWindow();
|
||||
|
||||
|
@ -976,8 +991,6 @@ public:
|
|||
void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, size_t FullWidth, size_t FullHeight, size_t PixelSize, size_t SubOffsetX, size_t SubOffsetY, size_t SubCopyWidth, size_t SubCopyHeight) override;
|
||||
void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, size_t DestWidth, size_t DestHeight, uint8_t *pSourceBuffer, size_t SrcWidth, size_t SrcHeight, size_t PixelSize, size_t SrcSubOffsetX, size_t SrcSubOffsetY, size_t SrcSubCopyWidth, size_t SrcSubCopyHeight) override;
|
||||
|
||||
bool ScreenshotDirect();
|
||||
|
||||
void TextureSet(CTextureHandle TextureID) override;
|
||||
|
||||
void Clear(float r, float g, float b, bool ForceClearNow = false) override;
|
||||
|
@ -1244,6 +1257,7 @@ public:
|
|||
int Init() override;
|
||||
void Shutdown() override;
|
||||
|
||||
void ReadPixel(ivec2 Position, ColorRGBA *pColor) override;
|
||||
void TakeScreenshot(const char *pFilename) override;
|
||||
void TakeCustomScreenshot(const char *pFilename) override;
|
||||
void Swap() override;
|
||||
|
|
|
@ -514,6 +514,15 @@ public:
|
|||
virtual void ChangeColorOfCurrentQuadVertices(float r, float g, float b, float a) = 0;
|
||||
virtual void ChangeColorOfQuadVertices(size_t QuadOffset, unsigned char r, unsigned char g, unsigned char b, unsigned char a) = 0;
|
||||
|
||||
/**
|
||||
* Reads the color at the specified position from the backbuffer once,
|
||||
* after the next swap operation.
|
||||
*
|
||||
* @param Position The pixel position to read.
|
||||
* @param pColor Pointer that will receive the read pixel color.
|
||||
* The pointer must be valid until the next swap operation.
|
||||
*/
|
||||
virtual void ReadPixel(ivec2 Position, ColorRGBA *pColor) = 0;
|
||||
virtual void TakeScreenshot(const char *pFilename) = 0;
|
||||
virtual void TakeCustomScreenshot(const char *pFilename) = 0;
|
||||
virtual int GetVideoModes(CVideoMode *pModes, int MaxModes, int Screen) = 0;
|
||||
|
|
|
@ -132,6 +132,7 @@ MAYBE_UNUSED static const char *FONT_ICON_CIRCLE_PLAY = "\xEF\x85\x84";
|
|||
MAYBE_UNUSED static const char *FONT_ICON_BORDER_ALL = "\xEF\xA1\x8C";
|
||||
MAYBE_UNUSED static const char *FONT_ICON_EYE = "\xEF\x81\xAE";
|
||||
MAYBE_UNUSED static const char *FONT_ICON_EYE_SLASH = "\xEF\x81\xB0";
|
||||
MAYBE_UNUSED static const char *FONT_ICON_EYE_DROPPER = "\xEF\x87\xBB";
|
||||
|
||||
MAYBE_UNUSED static const char *FONT_ICON_DICE_ONE = "\xEF\x94\xA5";
|
||||
MAYBE_UNUSED static const char *FONT_ICON_DICE_TWO = "\xEF\x94\xA8";
|
||||
|
|
|
@ -1248,6 +1248,38 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
|
|||
pLayer->BrushRotate(s_RotationAmount / 360.0f * pi * 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Color pipette and palette
|
||||
{
|
||||
const float PipetteButtonWidth = 30.0f;
|
||||
const float ColorPickerButtonWidth = 20.0f;
|
||||
const float Spacing = 2.0f;
|
||||
const size_t NumColorsShown = clamp<int>(round_to_int((TB_Top.w - PipetteButtonWidth - 40.0f) / (ColorPickerButtonWidth + Spacing)), 1, std::size(m_aSavedColors));
|
||||
|
||||
CUIRect ColorPalette;
|
||||
TB_Top.VSplitRight(NumColorsShown * (ColorPickerButtonWidth + Spacing) + PipetteButtonWidth, &TB_Top, &ColorPalette);
|
||||
|
||||
// Pipette button
|
||||
static char s_PipetteButton;
|
||||
ColorPalette.VSplitLeft(PipetteButtonWidth, &Button, &ColorPalette);
|
||||
ColorPalette.VSplitLeft(Spacing, nullptr, &ColorPalette);
|
||||
if(DoButton_FontIcon(&s_PipetteButton, FONT_ICON_EYE_DROPPER, m_ColorPipetteActive ? 1 : 0, &Button, 0, "[Ctrl+Shift+C] Color pipette. Pick a color from the screen by clicking on it.", IGraphics::CORNER_ALL) ||
|
||||
(CLineInput::GetActiveInput() == nullptr && ModPressed && ShiftPressed && Input()->KeyPress(KEY_C)))
|
||||
{
|
||||
m_ColorPipetteActive = !m_ColorPipetteActive;
|
||||
}
|
||||
|
||||
// Palette color pickers
|
||||
for(size_t i = 0; i < NumColorsShown; ++i)
|
||||
{
|
||||
ColorPalette.VSplitLeft(ColorPickerButtonWidth, &Button, &ColorPalette);
|
||||
ColorPalette.VSplitLeft(Spacing, nullptr, &ColorPalette);
|
||||
const auto &&SetColor = [&](ColorRGBA NewColor) {
|
||||
m_aSavedColors[i] = NewColor;
|
||||
};
|
||||
DoColorPickerButton(&m_aSavedColors[i], &Button, m_aSavedColors[i], SetColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom line buttons
|
||||
|
@ -8202,9 +8234,17 @@ void CEditor::Render()
|
|||
|
||||
MapView()->UpdateZoom();
|
||||
|
||||
// Cancel color pipette with escape before closing popup menus with escape
|
||||
if(m_ColorPipetteActive && UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
|
||||
{
|
||||
m_ColorPipetteActive = false;
|
||||
}
|
||||
|
||||
UI()->RenderPopupMenus();
|
||||
FreeDynamicPopupMenus();
|
||||
|
||||
UpdateColorPipette();
|
||||
|
||||
if(m_Dialog == DIALOG_NONE && !m_PopupEventActivated && UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
|
||||
{
|
||||
OnClose();
|
||||
|
@ -8274,22 +8314,105 @@ void CEditor::FreeDynamicPopupMenus()
|
|||
}
|
||||
}
|
||||
|
||||
void CEditor::UpdateColorPipette()
|
||||
{
|
||||
if(!m_ColorPipetteActive)
|
||||
return;
|
||||
|
||||
static char s_PipetteScreenButton;
|
||||
if(UI()->HotItem() == &s_PipetteScreenButton)
|
||||
{
|
||||
// Read color one pixel to the top and left as we would otherwise not read the correct
|
||||
// color due to the cursor sprite being rendered over the current mouse position.
|
||||
const int PixelX = clamp<int>(round_to_int((UI()->MouseX() - 1.0f) / UI()->Screen()->w * Graphics()->ScreenWidth()), 0, Graphics()->ScreenWidth() - 1);
|
||||
const int PixelY = clamp<int>(round_to_int((UI()->MouseY() - 1.0f) / UI()->Screen()->h * Graphics()->ScreenHeight()), 0, Graphics()->ScreenHeight() - 1);
|
||||
Graphics()->ReadPixel(ivec2(PixelX, PixelY), &m_PipetteColor);
|
||||
}
|
||||
|
||||
// Simulate button overlaying the entire screen to intercept all clicks for color pipette.
|
||||
const int ButtonResult = DoButton_Editor_Common(&s_PipetteScreenButton, "", 0, UI()->Screen(), 0, "Left click to pick a color from the screen. Right click to cancel pipette mode.");
|
||||
// Don't handle clicks if we are panning, so the pipette stays active while panning.
|
||||
// Checking m_pContainerPanned alone is not enough, as this variable is reset when
|
||||
// panning ends before this function is called.
|
||||
if(m_pContainerPanned == nullptr && m_pContainerPannedLast == nullptr)
|
||||
{
|
||||
if(ButtonResult == 1)
|
||||
{
|
||||
char aClipboard[9];
|
||||
str_format(aClipboard, sizeof(aClipboard), "%08X", m_PipetteColor.PackAlphaLast());
|
||||
Input()->SetClipboardText(aClipboard);
|
||||
|
||||
// Check if any of the saved colors is equal to the picked color and
|
||||
// bring it to the front of the list instead of adding a duplicate.
|
||||
int ShiftEnd = (int)std::size(m_aSavedColors) - 1;
|
||||
for(int i = 0; i < (int)std::size(m_aSavedColors); ++i)
|
||||
{
|
||||
if(m_aSavedColors[i].Pack() == m_PipetteColor.Pack())
|
||||
{
|
||||
ShiftEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(int i = ShiftEnd; i > 0; --i)
|
||||
{
|
||||
m_aSavedColors[i] = m_aSavedColors[i - 1];
|
||||
}
|
||||
m_aSavedColors[0] = m_PipetteColor;
|
||||
}
|
||||
if(ButtonResult > 0)
|
||||
{
|
||||
m_ColorPipetteActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CEditor::RenderMousePointer()
|
||||
{
|
||||
if(!m_ShowMousePointer)
|
||||
return;
|
||||
|
||||
constexpr float CursorSize = 16.0f;
|
||||
|
||||
// Cursor
|
||||
Graphics()->WrapClamp();
|
||||
Graphics()->TextureSet(m_aCursorTextures[m_CursorType]);
|
||||
Graphics()->QuadsBegin();
|
||||
if(m_CursorType == CURSOR_RESIZE_V)
|
||||
Graphics()->QuadsSetRotation(pi / 2);
|
||||
{
|
||||
Graphics()->QuadsSetRotation(pi / 2.0f);
|
||||
}
|
||||
if(ms_pUiGotContext == UI()->HotItem())
|
||||
Graphics()->SetColor(1, 0, 0, 1);
|
||||
IGraphics::CQuadItem QuadItem(UI()->MouseX(), UI()->MouseY(), 16.0f, 16.0f);
|
||||
{
|
||||
Graphics()->SetColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
IGraphics::CQuadItem QuadItem(UI()->MouseX(), UI()->MouseY(), CursorSize, CursorSize);
|
||||
Graphics()->QuadsDrawTL(&QuadItem, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
Graphics()->WrapNormal();
|
||||
|
||||
// Pipette color
|
||||
if(m_ColorPipetteActive)
|
||||
{
|
||||
CUIRect PipetteRect = {UI()->MouseX() + CursorSize, UI()->MouseY() + CursorSize, 80.0f, 20.0f};
|
||||
if(PipetteRect.x + PipetteRect.w + 2.0f > UI()->Screen()->w)
|
||||
{
|
||||
PipetteRect.x = UI()->MouseX() - PipetteRect.w - CursorSize / 2.0f;
|
||||
}
|
||||
if(PipetteRect.y + PipetteRect.h + 2.0f > UI()->Screen()->h)
|
||||
{
|
||||
PipetteRect.y = UI()->MouseY() - PipetteRect.h - CursorSize / 2.0f;
|
||||
}
|
||||
PipetteRect.Draw(ColorRGBA(0.2f, 0.2f, 0.2f, 0.7f), IGraphics::CORNER_ALL, 3.0f);
|
||||
|
||||
CUIRect Pipette, Label;
|
||||
PipetteRect.VSplitLeft(PipetteRect.h, &Pipette, &Label);
|
||||
Pipette.Margin(2.0f, &Pipette);
|
||||
Pipette.Draw(m_PipetteColor, IGraphics::CORNER_ALL, 3.0f);
|
||||
|
||||
char aLabel[8];
|
||||
str_format(aLabel, sizeof(aLabel), "#%06X", m_PipetteColor.PackAlphaLast(false));
|
||||
UI()->DoLabel(&Label, aLabel, 10.0f, TEXTALIGN_MC);
|
||||
}
|
||||
}
|
||||
|
||||
void CEditor::Reset(bool CreateDefault)
|
||||
|
@ -8320,6 +8443,7 @@ void CEditor::Reset(bool CreateDefault)
|
|||
m_MouseDeltaWx = 0;
|
||||
m_MouseDeltaWy = 0;
|
||||
m_pContainerPanned = nullptr;
|
||||
m_pContainerPannedLast = nullptr;
|
||||
|
||||
m_Map.m_Modified = false;
|
||||
m_Map.m_ModifiedAuto = false;
|
||||
|
@ -8658,6 +8782,8 @@ void CEditor::OnUpdate()
|
|||
Reset();
|
||||
}
|
||||
|
||||
m_pContainerPannedLast = m_pContainerPanned;
|
||||
|
||||
// handle key presses
|
||||
for(size_t i = 0; i < Input()->NumEvents(); i++)
|
||||
{
|
||||
|
@ -8735,6 +8861,8 @@ void CEditor::OnWindowResize()
|
|||
|
||||
void CEditor::OnClose()
|
||||
{
|
||||
m_ColorPipetteActive = false;
|
||||
|
||||
if(m_ToolbarPreviewSound >= 0 && Sound()->IsPlaying(m_ToolbarPreviewSound))
|
||||
Sound()->Pause(m_ToolbarPreviewSound);
|
||||
if(m_FilePreviewSound >= 0 && Sound()->IsPlaying(m_FilePreviewSound))
|
||||
|
|
|
@ -396,6 +396,11 @@ public:
|
|||
m_QuadKnifeCount = 0;
|
||||
mem_zero(m_aQuadKnifePoints, sizeof(m_aQuadKnifePoints));
|
||||
|
||||
for(size_t i = 0; i < std::size(m_aSavedColors); ++i)
|
||||
{
|
||||
m_aSavedColors[i] = color_cast<ColorRGBA>(ColorHSLA(i / (float)std::size(m_aSavedColors), 1.0f, 0.5f));
|
||||
}
|
||||
|
||||
m_CheckerTexture.Invalidate();
|
||||
m_BackgroundTexture.Invalidate();
|
||||
for(int i = 0; i < NUM_CURSORS; i++)
|
||||
|
@ -467,6 +472,7 @@ public:
|
|||
void RenderPressedKeys(CUIRect View);
|
||||
void RenderSavingIndicator(CUIRect View);
|
||||
void FreeDynamicPopupMenus();
|
||||
void UpdateColorPipette();
|
||||
void RenderMousePointer();
|
||||
|
||||
std::vector<CQuad *> GetSelectedQuads();
|
||||
|
@ -686,7 +692,8 @@ public:
|
|||
float m_MouseDeltaY;
|
||||
float m_MouseDeltaWx;
|
||||
float m_MouseDeltaWy;
|
||||
void *m_pContainerPanned;
|
||||
const void *m_pContainerPanned;
|
||||
const void *m_pContainerPannedLast;
|
||||
|
||||
enum EShowTile
|
||||
{
|
||||
|
@ -745,6 +752,11 @@ public:
|
|||
int m_QuadKnifeCount;
|
||||
vec2 m_aQuadKnifePoints[4];
|
||||
|
||||
// Color palette and pipette
|
||||
ColorRGBA m_aSavedColors[8];
|
||||
ColorRGBA m_PipetteColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
bool m_ColorPipetteActive = false;
|
||||
|
||||
IGraphics::CTextureHandle m_CheckerTexture;
|
||||
IGraphics::CTextureHandle m_BackgroundTexture;
|
||||
|
||||
|
|
Loading…
Reference in a new issue