// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #pragma once #include "framebuffer.h" #include "blur_shader.h" #include "noise_texture.h" #include "imgui.h" #include #include #ifdef DRAGONX_USE_DX11 #include #endif namespace dragonx { namespace ui { namespace effects { /** * @brief Acrylic material parameters * * These parameters control the appearance of the acrylic effect, * matching Microsoft's Fluent Design system. */ struct AcrylicParams { // Tint color (RGBA, pre-multiplied alpha) ImVec4 tintColor = ImVec4(0.1f, 0.1f, 0.12f, 1.0f); // Tint opacity (0.0 = fully transparent, 1.0 = fully opaque) float tintOpacity = 0.75f; // Luminosity opacity (controls saturation pass-through) // Lower values = more desaturated/milky appearance float luminosityOpacity = 0.5f; // Blur radius in pixels (typical: 20-60) float blurRadius = 30.0f; // When true, blurRadius is used AS-IS — NOT scaled by the user's blur-strength slider // (blurRadiusMultiplier). The modal backdrop sets this so its blur is a fixed value, independent // of the global acrylic slider. bool absoluteBlurRadius = false; // Noise texture opacity (typical: 0.02-0.04) float noiseOpacity = 0.02f; // Fallback color when acrylic is disabled. // The alpha channel also controls the glass opacity of the blurred // background when acrylic IS active (lower alpha = more see-through). ImVec4 fallbackColor = ImVec4(0.15f, 0.15f, 0.18f, 0.5f); // Whether acrylic is enabled (false = use fallback) bool enabled = true; }; /** * @brief Acrylic quality levels */ enum class AcrylicQuality { Off, // Solid fallback color only Low, // Single blur pass, downsampled Medium, // Two blur passes, slight downsample High // Full quality blur }; /** * @brief Acrylic fallback modes for graceful degradation */ enum class AcrylicFallback { None, // Full acrylic (blur + tint + noise) TintedOnly, // No blur, just semi-transparent tint overlay Solid // Opaque fallback color only }; /** * @brief Acrylic settings for accessibility and performance */ struct AcrylicSettings { bool enabled = true; // Master toggle bool reducedTransparency = false; // Accessibility: use solid colors float uiOpacity = 1.0f; // Card/sidebar opacity multiplier (0.3–1.0) AcrylicQuality quality = AcrylicQuality::Medium; // Medium default for balance of quality/performance float blurRadiusMultiplier = 1.0f; // Scales all blur radii (0.5 - 2.0) float noiseOpacityMultiplier = 1.0f; // Scales noise opacity (0.0 - 2.0) // Auto-disable conditions (checked each frame) bool disableOnBattery = false; // Disable when on battery power bool disableOnLowFPS = false; // Disable if FPS drops below threshold float lowFPSThreshold = 30.0f; // FPS threshold for auto-disable }; /** * @brief System capabilities for acrylic effects */ struct AcrylicCapabilities { bool hasFramebufferSupport = false; bool hasShaderSupport = false; bool hasTextureSupport = false; int maxTextureSize = 0; std::string glVersion; std::string glRenderer; bool isLowEndGPU = false; bool isOnBattery = false; // Linux: check /sys/class/power_supply }; /** * @brief Main acrylic material rendering system * * Implements Microsoft Fluent Design's acrylic material effect: * - Gaussian blur of background content * - Tint color overlay * - Luminosity blend for depth * - Subtle noise texture for grain */ class AcrylicMaterial { public: AcrylicMaterial(); ~AcrylicMaterial(); // Non-copyable AcrylicMaterial(const AcrylicMaterial&) = delete; AcrylicMaterial& operator=(const AcrylicMaterial&) = delete; /** * @brief Initialize the acrylic system * @return true if successful */ bool init(); /** * @brief Release all resources */ void shutdown(); /** * @brief Check if system is initialized */ bool isInitialized() const { return initialized_; } /** * @brief Update internal buffers for new viewport size * @param width Viewport width * @param height Viewport height */ void resize(int width, int height); /** * @brief Capture current screen content for blur source * * Call this BEFORE rendering any acrylic surfaces. * Only recaptures when the background is marked dirty. */ void captureBackground(); /** * @brief Capture background directly from the current framebuffer. * * Intended to be called from an ImGui draw callback inserted at * the end of the BackgroundDrawList. At that point during * RenderDrawData(), only the background (gradient / image / noise) * has been rasterized — no UI elements are in the framebuffer yet. * This gives a clean background capture for acrylic blur. * * Respects the dirtyFrames_ counter so the capture and blur * are only recomputed when the background actually changes * (resize, theme change, etc.). */ void captureBackgroundDirect(); /** * @brief Capture the LIVE framebuffer (whatever has been drawn so far this * frame), bypassing the dirtyFrames_ gate. Used by a full-window modal * overlay to blur the live UI behind it: insert GetLiveCaptureCallback() * at the start of the overlay window's draw list so it fires mid-pass, * after the app UI is rasterized and before the overlay's own backdrop. * Always forces a re-blur of the fresh capture. */ void captureLiveFramebuffer(); /** * @brief Returns true once a valid background capture has been made. * * drawRect() checks this and falls back to a tinted fill on the * very first frame before the callback has fired. */ bool hasValidCapture() const { return hasValidCapture_; } /// Mark background as needing recapture (call on resize, theme change, etc.) /// Uses a 2-frame counter so the capture re-runs after the new background /// has actually been rendered (theme switch happens mid-frame, before the /// BackgroundDrawList is rebuilt with new colors/images). void markBackgroundDirty() { dirtyFrames_ = 2; } /** * @brief Render an acrylic-filled rectangle * * @param drawList ImGui draw list to add commands to * @param pMin Top-left corner in screen coordinates * @param pMax Bottom-right corner in screen coordinates * @param params Acrylic parameters * @param rounding Corner rounding */ void drawRect(ImDrawList* drawList, const ImVec2& pMin, const ImVec2& pMax, const AcrylicParams& params, float rounding = 0.0f); /** * @brief Get the blurred background texture for manual rendering */ ImTextureID getBlurredTexture() const; /** * @brief Get the noise texture */ ImTextureID getNoiseTexture() const; // ======================================================================== // Settings // ======================================================================== /** * @brief Set global acrylic quality */ void setQuality(AcrylicQuality quality); AcrylicQuality getQuality() const { return settings_.quality; } /** * @brief Enable/disable acrylic globally */ void setEnabled(bool enabled) { settings_.enabled = enabled; } bool isEnabled() const { return settings_.enabled; } /** * @brief Set blur radius multiplier (scales all blur radii) * @param multiplier Value from 0.0 to 5.0 */ void setBlurMultiplier(float multiplier) { float clamped = std::max(0.0f, std::min(5.0f, multiplier)); settings_.blurRadiusMultiplier = clamped; } float getBlurMultiplier() const { return settings_.blurRadiusMultiplier; } /** * @brief Set reduced transparency mode (accessibility) */ void setReducedTransparency(bool reduced) { settings_.reducedTransparency = reduced; } bool getReducedTransparency() const { return settings_.reducedTransparency; } /** * @brief Set UI opacity multiplier for cards/sidebar (0.3–1.0, 1=opaque) */ void setUIOpacity(float opacity) { settings_.uiOpacity = std::max(0.3f, std::min(1.0f, opacity)); } float getUIOpacity() const { return settings_.uiOpacity; } /** * @brief Set noise opacity multiplier (0.0 = no noise, 1.0 = default, 2.0 = double) */ void setNoiseOpacityMultiplier(float m) { settings_.noiseOpacityMultiplier = std::max(0.0f, std::min(5.0f, m)); } float getNoiseOpacityMultiplier() const { return settings_.noiseOpacityMultiplier; } /** * @brief Get full settings struct */ const AcrylicSettings& getSettings() const { return settings_; } void setSettings(const AcrylicSettings& settings) { settings_ = settings; } // ======================================================================== // Fallback System // ======================================================================== /** * @brief Detect system capabilities and determine fallback mode * * Checks OpenGL capabilities, GPU info, and user preferences to * determine the appropriate rendering mode. * * @return The recommended fallback mode */ AcrylicFallback detectFallback() const; /** * @brief Get current fallback mode (cached) */ AcrylicFallback getCurrentFallback() const { return currentFallback_; } /** * @brief Force a specific fallback mode */ void setForcedFallback(AcrylicFallback fallback) { forcedFallback_ = fallback; hasForcedFallback_ = true; } /** * @brief Clear forced fallback and use auto-detection */ void clearForcedFallback() { hasForcedFallback_ = false; } /** * @brief Get detected system capabilities */ const AcrylicCapabilities& getCapabilities() const { return capabilities_; } /** * @brief Refresh capability detection (call after context changes) */ void refreshCapabilities(); // ======================================================================== // Preset Parameters // ======================================================================== /** * @brief Get dark theme acrylic preset */ static AcrylicParams getDarkPreset(); /** * @brief Get light theme acrylic preset */ static AcrylicParams getLightPreset(); /** * @brief Get DragonX branded acrylic preset */ static AcrylicParams getDragonXPreset(); /** * @brief Get popup/dialog acrylic preset */ static AcrylicParams getPopupPreset(); /** * @brief Get sidebar acrylic preset */ static AcrylicParams getSidebarPreset(); private: /** * @brief Apply blur passes to captured content */ void applyBlur(float radius, bool ignoreMultiplier = false); /** * @brief Composite final acrylic appearance */ void compositeAcrylic(const ImVec2& pMin, const ImVec2& pMax, const AcrylicParams& params); /** * @brief Check if effect should be skipped (settings/quality) */ bool shouldSkipEffect() const; /** * @brief Draw tinted-only fallback (no blur) */ void drawTintedRect(ImDrawList* drawList, const ImVec2& pMin, const ImVec2& pMax, const AcrylicParams& params, float rounding); bool initialized_ = false; AcrylicSettings settings_; // All settings in one struct // Fallback system AcrylicCapabilities capabilities_; AcrylicFallback currentFallback_ = AcrylicFallback::None; AcrylicFallback forcedFallback_ = AcrylicFallback::None; bool hasForcedFallback_ = false; // Screen dimensions int viewportWidth_ = 0; int viewportHeight_ = 0; // Noise texture config int noiseTextureSize_ = 256; // Texture resolution (larger = less repetition) // Caching for performance float lastBlurRadius_ = 0.0f; bool blurCacheValid_ = false; int dirtyFrames_ = 2; // >0 means recapture needed; counts down each frame bool hasValidCapture_ = false; // true once a clean BG capture exists // Capture and blur framebuffers Framebuffer captureBuffer_; FramebufferPingPong blurBuffers_; // Shaders BlurShader blurShader_; // Fullscreen quad for post-processing FullscreenQuad quad_; // Noise texture NoiseTexture noiseTexture_; // Composite shader (tint + noise overlay) GLuint compositeShader_ = 0; GLint uCompositeTintColor_ = -1; GLint uCompositeTintOpacity_ = -1; GLint uCompositeLuminosity_ = -1; GLint uCompositeNoiseOpacity_ = -1; GLint uCompositeBlurTex_ = -1; GLint uCompositeNoiseTex_ = -1; GLint uCompositeTexScale_ = -1; #ifdef DRAGONX_USE_DX11 // ---- DX11 acrylic resources (used instead of GL objects above) ---- ID3D11Device* dx_device_ = nullptr; ID3D11DeviceContext* dx_context_ = nullptr; // Capture buffer (full viewport resolution) ID3D11Texture2D* dx_captureTex_ = nullptr; ID3D11ShaderResourceView* dx_captureSRV_ = nullptr; ID3D11RenderTargetView* dx_captureRTV_ = nullptr; // Blur ping-pong buffers (possibly downscaled) ID3D11Texture2D* dx_blurTex_[2] = {}; ID3D11ShaderResourceView* dx_blurSRV_[2] = {}; ID3D11RenderTargetView* dx_blurRTV_[2] = {}; int dx_blurWidth_ = 0; int dx_blurHeight_ = 0; int dx_blurCurrent_ = 0; // which buffer is "source" // Shaders & pipeline objects ID3D11VertexShader* dx_blurVS_ = nullptr; ID3D11PixelShader* dx_blurPS_ = nullptr; ID3D11InputLayout* dx_inputLayout_ = nullptr; ID3D11Buffer* dx_blurCB_ = nullptr; ID3D11Buffer* dx_vertexBuf_ = nullptr; ID3D11SamplerState* dx_sampler_ = nullptr; ID3D11RasterizerState* dx_blurRS_ = nullptr; ID3D11BlendState* dx_blurBS_ = nullptr; ID3D11DepthStencilState* dx_blurDSS_ = nullptr; // Noise texture (DX11) ID3D11Texture2D* dx_noiseTex_ = nullptr; ID3D11ShaderResourceView* dx_noiseSRV_ = nullptr; // Internal helpers bool dx_initPipeline(); void dx_releasePipeline(); void dx_createRenderTarget(ID3D11Texture2D*& tex, ID3D11ShaderResourceView*& srv, ID3D11RenderTargetView*& rtv, int w, int h, DXGI_FORMAT format); void dx_releaseRenderTarget(ID3D11Texture2D*& tex, ID3D11ShaderResourceView*& srv, ID3D11RenderTargetView*& rtv); #endif // DRAGONX_USE_DX11 }; // ============================================================================ // Global Acrylic Instance // ============================================================================ /** * @brief Get the global acrylic material instance */ AcrylicMaterial& getAcrylicMaterial(); } // namespace effects } // namespace ui } // namespace dragonx