local ipairs = ipairs local table = table local timer = timer local ceil = math.ceil local log = math.log local pow = math.pow local tblconcat = table.concat local cache = {} local downloads = {} local styles = {} local embedHtml = [[ ]] local UpdateTimerName = "HtmlMatUpdate" local TimerRunning = false local function updateCache(download) download.browser:UpdateHTMLTexture() cache[download.key] = download.browser:GetHTMLMaterial() end local function updateMaterials() for _, download in ipairs(downloads) do updateCache(download) end end local function onImageLoaded(key, browser) local idx for k, v in pairs(downloads) do if v.key == key then idx = k break end end if idx > 0 then -- html materials are unique to each browser; re-using a browser will -- result in previous materials being updated. Therefore, used browsers -- must be destroyed rather than pooled. local download = downloads[idx] browserpool.release(browser, true) table.remove(downloads, idx) end if #downloads == 0 and TimerRunning then timer.Remove(UpdateTimerName) TimerRunning = false end end local DefaultMat = Material("vgui/white") local DefaultWidth = 128 local DefaultStyle = {} local function enqueueUrl( url, styleName, key, callback ) cache[key] = DefaultMat browserpool.get(function(browser) local style = styles[styleName] or DefaultStyle local w = style.width or DefaultWidth local h = style.height or w browser:SetSize( w, h ) local download = { url = url, key = key, browser = browser } table.insert(downloads, download) browser:AddFunction("gmod", "imageLoaded", function() updateCache(download) onImageLoaded(key, browser) if isfunction(callback) then callback( cache[key] ) end end) if not TimerRunning then timer.Create(UpdateTimerName, 0.05, 0, updateMaterials) timer.Start(UpdateTimerName) TimerRunning = true end local html = (style.html or embedHtml):format(url, style.css or '') browser:SetHTML( html ) end) end -- cached for performance local MAT_STR_TABLE = { '', '@', '' } --- -- Renders a URL as a material. -- -- @param url URL. -- @param style HTMLMaterial style. -- function HTMLMaterial( url, style, callback ) if not url then return DefaultMat end local key -- Build unique key for material if style then MAT_STR_TABLE[1] = url MAT_STR_TABLE[3] = style key = tblconcat( MAT_STR_TABLE ) else key = url end -- Enqueue the URL to be downloaded if it hasn't been loaded yet. if cache[key] == nil then enqueueUrl( url, style, key, callback ) elseif callback then callback( cache[key] ) end -- Return cached URL return cache[key] end local SetDrawColor = surface.SetDrawColor local SetMaterial = surface.SetMaterial local DrawTexturedRect = surface.DrawTexturedRect function CeilPower2(n) return pow(2, ceil(log(n) / log(2))) end function DrawHTMLMaterial( url, styleName, w, h ) local mat = HTMLMaterial( url, styleName ) local style = styles[styleName] or DefaultStyle -- Desired dimensions local width = style.width or DefaultWidth local height = style.height or w -- Convert to scalar w = w / width h = h / height -- Fix for non-power-of-two html panel size width = CeilPower2(width) height = CeilPower2(height) SetDrawColor( color_white ) SetMaterial( mat ) DrawTexturedRect( 0, 0, w * width, h * height ) end --- -- Registers a style that can be used with `HTMLMaterial` -- -- @param name Style name. -- @param params Table of style parameters. -- function AddHTMLMaterialStyle( name, params, base ) params = params or {} if base then table.Merge( params, table.Copy( styles[base] or {} ) ) end styles[name] = params end HTMLMAT_STYLE_BLUR = 'htmlmat.style.blur' HTMLMAT_STYLE_GRAYSCALE = 'htmlmat.style.grayscale' HTMLMAT_STYLE_SEPIA = 'htmlmat.style.sepia' HTMLMAT_STYLE_INVERT = 'htmlmat.style.invert' HTMLMAT_STYLE_CIRCLE = 'htmlmat.style.circle' HTMLMAT_STYLE_COVER = 'htmlmat.style.cover' HTMLMAT_STYLE_COVER_IMG = 'htmlmat.style.coverimg' AddHTMLMaterialStyle( HTMLMAT_STYLE_BLUR, { css = [[img { -webkit-filter: blur(8px); -webkit-transform: scale(1.1, 1.1); }]] }) AddHTMLMaterialStyle( HTMLMAT_STYLE_GRAYSCALE, { css = [[img { -webkit-filter: grayscale(1); }]] }) AddHTMLMaterialStyle( HTMLMAT_STYLE_SEPIA, { css = [[img { -webkit-filter: sepia(1); }]] }) AddHTMLMaterialStyle( HTMLMAT_STYLE_INVERT, { css = [[img { -webkit-filter: invert(1); }]] }) AddHTMLMaterialStyle( HTMLMAT_STYLE_CIRCLE, { css = [[img { border-radius: 50%; }]] }) AddHTMLMaterialStyle( HTMLMAT_STYLE_COVER, { html = [[
]] }) -- Use this if you want to use -webkit-filter blur on the image; -- you'll also need to use a transform to scale it a bit. This prevents -- the edges from blurring as seen with background-size cover. AddHTMLMaterialStyle( HTMLMAT_STYLE_COVER_IMG, { html = [[ ]] })