-------------------------------------------- -- http://wow.gamepedia.com/UI_Scale -- http://www.wowinterface.com/forums/showthread.php?t=31813 -------------------------------------------- local _, addon = ... addon.pixelPerfectFuncs = {} local function Round(num, numDecimalPlaces) if numDecimalPlaces and numDecimalPlaces >= 0 then local mult = 10 ^ numDecimalPlaces num = num * mult if num >= 0 then return floor(num + 0.5) / mult else return ceil(num - 0.5) / mult end end if num >= 0 then return floor(num + 0.5) else return ceil(num - 0.5) end end local Clamp = Clamp ---@class PixelPerfectFuncs local P = addon.pixelPerfectFuncs function P.GetResolution() -- return string.match(({GetScreenResolutions()})[GetCurrentResolution()], "(%d+)x(%d+)") return GetPhysicalScreenSize() end -- The UI P.Scale goes from 1 to 0.64. -- At 768y we see pixel-per-pixel accurate representation of our texture, -- and again at 1200y if at 0.64 scale. function P.GetPixelPerfectScale() local hRes, vRes = P.GetResolution() if vRes then return 768 / vRes else -- windowed mode before 8.0, or maybe something goes wrong? return 1 end end function P.GetRecommendedScale() local pScale = P.GetPixelPerfectScale() local mult if pScale >= 0.71 then -- 1080 mult = 1 elseif pScale >= 0.53 then -- 1440 mult = 1.2 else -- 2160 mult = 1.7 end return Clamp(Round(pScale / UIParent:GetScale() * mult, 2), 0.5, 2) end -- scale perfect! function P.PixelPerfectScale(frame) frame:SetScale(P.GetPixelPerfectScale()) end -- position perfect! function P.PixelPerfectPoint(frame) local left = frame:GetLeft() local top = frame:GetTop() frame:ClearAllPoints() frame:SetPoint("TOPLEFT", CellParent, "BOTTOMLEFT", math.floor(left + 0.5), math.floor(top + 0.5)) end -------------------------------------------- -- PixelUtil -------------------------------------------- -- local effectiveScale = 1 -- function P.SetRelativeScale(scale) -- effectiveScale = scale -- end -- function P.GetEffectiveScale() -- return effectiveScale -- end -- function P.SetEffectiveScale(frame) -- frame:SetScale(effectiveScale) -- end -- function P.Scale(uiUnitSize) -- if uiUnitSize == 0 then -- return 0 -- end -- local uiUnitFactor = PixelUtil.GetPixelToUIUnitFactor() -- local numPixels = Round((uiUnitSize * effectiveScale) / uiUnitFactor) -- if uiUnitSize < 0.0 then -- if numPixels > -1 then -- numPixels = -1 -- end -- else -- if numPixels < 1 then -- numPixels = 1 -- end -- end -- return numPixels * uiUnitFactor / effectiveScale -- end -------------------------------------------- -- some are stolen from ElvUI -------------------------------------------- -- local function GetUIParentScale() -- local scale = UIParent:GetScale() -- return scale - scale % 0.1 ^ 2 -- end local scale = 1 local mult = 1 ---@deprecated function P.SetRelativeScale(s) mult = 1 / s scale = s end ---@deprecated function P.GetEffectiveScale() return P.GetPixelPerfectScale() / mult end ---@deprecated function P.SetEffectiveScale(frame) frame:SetScale(P.GetEffectiveScale()) end --[[ local trunc = function(s) return s >= 0 and s-s%01 or s-s%-1 end local round = function(s) return s >= 0 and s-s%-1 or s-s%01 end function P.Scale(n) return (mult == 1 or n == 0) and n or ((mult < 1 and trunc(n/mult) or round(n/mult)) * mult) end ]] -- function P.Scale(n) -- if mult == 1 or n == 0 then -- return n -- else -- local x = mult > 1 and mult or -mult -- return n - n % (n < 0 and x or -x) -- end -- end local GetNearestPixelSize = PixelUtil.GetNearestPixelSize function P.Scale(desiredPixels) return GetNearestPixelSize(desiredPixels, CellParent:GetEffectiveScale()) end function P.Size(frame, width, height) frame.width = width frame.height = height frame:SetSize(P.Scale(width), P.Scale(height)) end function P.Width(frame, width) frame.width = width frame:SetWidth(P.Scale(width)) end function P.Height(frame, height) frame.height = height frame:SetHeight(P.Scale(height)) end function P.SetGridSize(region, gridWidth, gridHeight, gridSpacingH, gridSpacingV, columns, rows) region._size_grid = true region._gridWidth = gridWidth region._gridHeight = gridHeight region._gridSpacingH = gridSpacingH region._gridSpacingV = gridSpacingV region._rows = rows region._columns = columns if columns == 0 then region:SetWidth(0.001) else region:SetWidth(P.Scale(gridWidth) * columns + P.Scale(gridSpacingH) * (columns - 1)) end if rows == 0 then region:SetHeight(0.001) else region:SetHeight(P.Scale(gridHeight) * rows + P.Scale(gridSpacingV) * (rows - 1)) end end function P.Point(frame, ...) if not frame.points then frame.points = {} end local point, anchorTo, anchorPoint, x, y local n = select("#", ...) if n == 1 then point = ... elseif n == 3 and type(select(2, ...)) == "number" then point, x, y = ... elseif n == 4 then point, anchorTo, x, y = ... else point, anchorTo, anchorPoint, x, y = ... end tinsert(frame.points, {point, anchorTo or frame:GetParent(), anchorPoint or point, x or 0, y or 0}) local n = #frame.points frame:SetPoint(frame.points[n][1], frame.points[n][2], frame.points[n][3], P.Scale(frame.points[n][4]), P.Scale(frame.points[n][5])) end function P.ClearPoints(frame) frame:ClearAllPoints() if frame.points then wipe(frame.points) end end -------------------------------------------- -- scale changed -------------------------------------------- function P.Resize(frame) if frame._size_grid then P.SetGridSize(frame, frame._gridWidth, frame._gridHeight, frame._gridSpacingH, frame._gridSpacingV, frame._columns, frame._rows) else if frame.width then frame:SetWidth(P.Scale(frame.width)) end if frame.height then frame:SetHeight(P.Scale(frame.height)) end end end function P.Reborder(frame, ignoreSnippetVar) if not frame.backdropInfo then return end local _r, _g, _b, _a = frame:GetBackdropColor() local r, g, b, a = frame:GetBackdropBorderColor() if ignoreSnippetVar then frame.backdropInfo.edgeSize = P.Scale(1) else if CELL_BORDER_SIZE == 0 then frame.backdropInfo.edgeFile = nil frame.backdropInfo.edgeSize = nil else frame.backdropInfo.edgeSize = P.Scale(CELL_BORDER_SIZE or 1) end end frame:ApplyBackdrop() if _r then frame:SetBackdropColor(_r, _g, _b, _a) end if r then frame:SetBackdropBorderColor(r, g, b, a) end end function P.Repoint(frame) if not frame.points or #frame.points == 0 then return end frame:ClearAllPoints() for _, t in pairs(frame.points) do frame:SetPoint(t[1], t[2], t[3], P.Scale(t[4]), P.Scale(t[5])) end end -- local frames = {} -- function P.SetPixelPerfect(frame) -- tinsert(frames, frame) -- end -- function P.UpdatePixelPerfectFrames() -- for _, f in pairs(frames) do -- f:UpdatePixelPerfect() -- end -- end -------------------------------------------- -- save & load position -------------------------------------------- function P.SavePosition(frame, positionTable) wipe(positionTable) positionTable[1], positionTable[2], positionTable[3] = P.CalcPoint(frame) -- local left = math.floor(frame:GetLeft() + 0.5) -- local top = math.floor(frame:GetTop() + 0.5) -- positionTable[1], positionTable[2] = left, top end function P.LoadPosition(frame, positionTable) if type(positionTable) ~= "table" then return end if #positionTable == 2 then P.ClearPoints(frame) P.Point(frame, "TOPLEFT", UIParent, "BOTTOMLEFT", positionTable[1], positionTable[2]) return true elseif #positionTable == 3 then P.ClearPoints(frame) frame:SetPoint(positionTable[1], CellParent, positionTable[2], positionTable[3]) return true end end function P.CalcPoint(frame) local point, x, y local centerX, centerY = CellParent:GetCenter() local width = CellParent:GetRight() x, y = frame:GetCenter() if y >= centerY then point = "TOP" y = -(CellParent:GetTop() - frame:GetTop()) else point = "BOTTOM" y = frame:GetBottom() end if x >= (width * 2 / 3) then point = point.."RIGHT" x = frame:GetRight() - width elseif x <= (width / 3) then point = point.."LEFT" x = frame:GetLeft() else x = x - centerX end -- x = tonumber(string.format("%.2f", x)) -- y = tonumber(string.format("%.2f", y)) x = Round(x, 1) y = Round(y, 1) return point, x, y end --------------------------------------------------------------------- -- pixel perfect (ElvUI) --------------------------------------------------------------------- local function CheckPixelSnap(frame, snap) if (frame and not frame:IsForbidden()) and frame.PixelSnapDisabled and snap then frame.PixelSnapDisabled = nil end end local function DisablePixelSnap(frame) if (frame and not frame:IsForbidden()) and not frame.PixelSnapDisabled then if frame.SetSnapToPixelGrid then frame:SetSnapToPixelGrid(false) frame:SetTexelSnappingBias(0) frame.PixelSnapDisabled = true elseif frame.GetStatusBarTexture then local texture = frame:GetStatusBarTexture() if type(texture) == "table" and texture.SetSnapToPixelGrid then texture:SetSnapToPixelGrid(false) texture:SetTexelSnappingBias(0) frame.PixelSnapDisabled = true end end end end local function UpdateMetatable(obj) local t = getmetatable(obj).__index if not obj.DisabledPixelSnap and (t.SetSnapToPixelGrid or t.SetStatusBarTexture or t.SetColorTexture or t.SetVertexColor or t.CreateTexture or t.SetTexCoord or t.SetTexture) then if t.SetSnapToPixelGrid then hooksecurefunc(t, "SetSnapToPixelGrid", CheckPixelSnap) end if t.SetStatusBarTexture then hooksecurefunc(t, "SetStatusBarTexture", DisablePixelSnap) end if t.SetColorTexture then hooksecurefunc(t, "SetColorTexture", DisablePixelSnap) end if t.SetVertexColor then hooksecurefunc(t, "SetVertexColor", DisablePixelSnap) end if t.CreateTexture then hooksecurefunc(t, "CreateTexture", DisablePixelSnap) end if t.SetTexCoord then hooksecurefunc(t, "SetTexCoord", DisablePixelSnap) end if t.SetTexture then hooksecurefunc(t, "SetTexture", DisablePixelSnap) end t.DisabledPixelSnap = true end end local obj = CreateFrame("Frame") UpdateMetatable(CreateFrame("StatusBar")) UpdateMetatable(obj:CreateTexture()) UpdateMetatable(obj:CreateMaskTexture())