|
| 1 | +DFPixelUtil.lua documentation |
| 2 | + |
| 3 | +===================================================================== |
| 4 | +Overview |
| 5 | +===================================================================== |
| 6 | + |
| 7 | +DFPixelUtil is a DetailsFramework polyfill for Blizzard's built-in |
| 8 | +PixelUtil API. It provides pixel-perfect sizing and positioning of |
| 9 | +UI elements by snapping measurements to the nearest physical pixel |
| 10 | +boundary. |
| 11 | + |
| 12 | +WoW's UI coordinate system uses abstract "UI units" rather than |
| 13 | +physical pixels. At different screen resolutions and UI scale |
| 14 | +settings, a single UI unit may span a fractional number of pixels, |
| 15 | +causing blurry edges on thin borders or 1px lines. DFPixelUtil |
| 16 | +solves this by rounding all sizes and offsets to exact pixel |
| 17 | +boundaries. |
| 18 | + |
| 19 | +The module is loaded early via load.xml (line 3) and consumed |
| 20 | +throughout the framework via the alias pattern: |
| 21 | + |
| 22 | + local PixelUtil = PixelUtil or DFPixelUtil |
| 23 | + |
| 24 | +This means Blizzard's native PixelUtil is used when available |
| 25 | +(Retail), and DFPixelUtil serves as the fallback on Classic-era |
| 26 | +clients where PixelUtil may not exist. |
| 27 | + |
| 28 | +Global table |
| 29 | + DFPixelUtil = {} |
| 30 | + |
| 31 | +Consumers (non-exhaustive) |
| 32 | + - cooltip.lua (line 32) |
| 33 | + - fw.lua (line 102) — border rendering, template sizing |
| 34 | + - buildmenu.lua — widget positioning and sizing |
| 35 | + - core/plugins.lua (line 4) — plugin frame layout |
| 36 | + - loadconditions.lua (line 29) |
| 37 | + |
| 38 | + |
| 39 | +===================================================================== |
| 40 | +1) GetPixelToUIUnitFactor() |
| 41 | +===================================================================== |
| 42 | + |
| 43 | +Location |
| 44 | + DFPixelUtil.lua line 3 |
| 45 | + |
| 46 | +Signature |
| 47 | + DFPixelUtil.GetPixelToUIUnitFactor() |
| 48 | + |
| 49 | +Purpose |
| 50 | + Returns the conversion factor from physical pixels to UI units. |
| 51 | + This is the foundational value used by all other functions in |
| 52 | + the module. |
| 53 | + |
| 54 | +Parameters |
| 55 | + None. |
| 56 | + |
| 57 | +Returns |
| 58 | + (number) The UI-unit size of one physical pixel. |
| 59 | + |
| 60 | +Behavior |
| 61 | + Calls GetPhysicalScreenSize() to get the monitor's native |
| 62 | + resolution, then computes: |
| 63 | + |
| 64 | + factor = 768.0 / physicalHeight |
| 65 | + |
| 66 | + The value 768 is WoW's reference UI height — the UI is |
| 67 | + designed so that at 768 physical pixels tall, 1 UI unit = 1 |
| 68 | + pixel (factor = 1.0). At higher resolutions, the factor |
| 69 | + shrinks (e.g., 768/1080 ≈ 0.711), meaning each physical pixel |
| 70 | + is smaller than one UI unit. |
| 71 | + |
| 72 | +Example |
| 73 | + -- On a 1920×1080 monitor: |
| 74 | + local factor = DFPixelUtil.GetPixelToUIUnitFactor() |
| 75 | + -- factor ≈ 0.7111 (768 / 1080) |
| 76 | + |
| 77 | + -- On a 2560×1440 monitor: |
| 78 | + -- factor ≈ 0.5333 (768 / 1440) |
| 79 | + |
| 80 | + |
| 81 | +===================================================================== |
| 82 | +2) GetNearestPixelSize() |
| 83 | +===================================================================== |
| 84 | + |
| 85 | +Location |
| 86 | + DFPixelUtil.lua line 8 |
| 87 | + |
| 88 | +Signature |
| 89 | + DFPixelUtil.GetNearestPixelSize(uiUnitSize, layoutScale, |
| 90 | + minPixels) |
| 91 | + |
| 92 | +Purpose |
| 93 | + Converts a desired UI-unit size into the nearest size that maps |
| 94 | + to an exact integer number of physical pixels. This eliminates |
| 95 | + sub-pixel rendering artifacts. |
| 96 | + |
| 97 | +Parameters |
| 98 | + uiUnitSize (number, required) |
| 99 | + The desired size in UI units. |
| 100 | + |
| 101 | + layoutScale (number, required) |
| 102 | + The effective scale of the region being sized. Typically |
| 103 | + obtained via region:GetEffectiveScale(). |
| 104 | + |
| 105 | + minPixels (number|nil, optional) |
| 106 | + Minimum number of physical pixels the result must occupy. |
| 107 | + Guarantees visibility of thin elements (e.g., 1px borders). |
| 108 | + |
| 109 | +Returns |
| 110 | + (number) The snapped size in UI units. |
| 111 | + |
| 112 | +Behavior |
| 113 | + 1. Short-circuit: if uiUnitSize == 0 and minPixels is nil or 0, |
| 114 | + returns 0 immediately. |
| 115 | + |
| 116 | + 2. Gets the pixel-to-UI-unit factor via GetPixelToUIUnitFactor(). |
| 117 | + |
| 118 | + 3. Converts to pixel count: |
| 119 | + numPixels = Round((uiUnitSize * layoutScale) / factor) |
| 120 | + |
| 121 | + 4. Applies minPixels enforcement: |
| 122 | + - If uiUnitSize is negative and numPixels > -minPixels: |
| 123 | + clamps to -minPixels. |
| 124 | + - If uiUnitSize is positive and numPixels < minPixels: |
| 125 | + clamps to minPixels. |
| 126 | + |
| 127 | + 5. Converts back to UI units: |
| 128 | + result = numPixels * factor / layoutScale |
| 129 | + |
| 130 | +Math breakdown |
| 131 | + Given a 1080p screen (factor = 0.7111) and effective scale 1.0: |
| 132 | + |
| 133 | + GetNearestPixelSize(0.5, 1.0) |
| 134 | + numPixels = Round(0.5 / 0.7111) = Round(0.703) = 1 |
| 135 | + result = 1 * 0.7111 / 1.0 = 0.7111 |
| 136 | + |
| 137 | + The input 0.5 UI units would land between pixels. The function |
| 138 | + rounds up to 1 physical pixel and returns the UI-unit size for |
| 139 | + exactly 1 pixel. |
| 140 | + |
| 141 | +Example |
| 142 | + -- Snap a 2px border to exact pixels: |
| 143 | + local snapped = DFPixelUtil.GetNearestPixelSize(2, frame:GetEffectiveScale(), 1) |
| 144 | + |
| 145 | + |
| 146 | +===================================================================== |
| 147 | +3) SetWidth() |
| 148 | +===================================================================== |
| 149 | + |
| 150 | +Location |
| 151 | + DFPixelUtil.lua line 31 |
| 152 | + |
| 153 | +Signature |
| 154 | + DFPixelUtil.SetWidth(region, width, minPixels) |
| 155 | + |
| 156 | +Purpose |
| 157 | + Sets a region's width snapped to the nearest physical pixel. |
| 158 | + |
| 159 | +Parameters |
| 160 | + region (region/frame, required) |
| 161 | + Any WoW UI region (frame, texture, fontstring, etc.). |
| 162 | + |
| 163 | + width (number, required) |
| 164 | + Desired width in UI units. |
| 165 | + |
| 166 | + minPixels (number|nil, optional) |
| 167 | + Minimum pixel width. Pass 1 to guarantee at least 1px. |
| 168 | + |
| 169 | +Behavior |
| 170 | + Calls: |
| 171 | + region:SetWidth(GetNearestPixelSize(width, |
| 172 | + region:GetEffectiveScale(), minPixels)) |
| 173 | + |
| 174 | +Example |
| 175 | + -- Set a border texture to exactly 1 pixel wide: |
| 176 | + DFPixelUtil.SetWidth(borderTexture, 1, 1) |
| 177 | + |
| 178 | + |
| 179 | +===================================================================== |
| 180 | +4) SetHeight() |
| 181 | +===================================================================== |
| 182 | + |
| 183 | +Location |
| 184 | + DFPixelUtil.lua line 35 |
| 185 | + |
| 186 | +Signature |
| 187 | + DFPixelUtil.SetHeight(region, height, minPixels) |
| 188 | + |
| 189 | +Purpose |
| 190 | + Sets a region's height snapped to the nearest physical pixel. |
| 191 | + |
| 192 | +Parameters |
| 193 | + region (region/frame, required) |
| 194 | + height (number, required) Desired height in UI units. |
| 195 | + minPixels (number|nil, optional) Minimum pixel height. |
| 196 | + |
| 197 | +Behavior |
| 198 | + Calls: |
| 199 | + region:SetHeight(GetNearestPixelSize(height, |
| 200 | + region:GetEffectiveScale(), minPixels)) |
| 201 | + |
| 202 | +Example |
| 203 | + DFPixelUtil.SetHeight(separatorLine, 1, 1) |
| 204 | + |
| 205 | + |
| 206 | +===================================================================== |
| 207 | +5) SetSize() |
| 208 | +===================================================================== |
| 209 | + |
| 210 | +Location |
| 211 | + DFPixelUtil.lua line 39 |
| 212 | + |
| 213 | +Signature |
| 214 | + DFPixelUtil.SetSize(region, width, height, minWidthPixels, |
| 215 | + minHeightPixels) |
| 216 | + |
| 217 | +Purpose |
| 218 | + Sets both width and height snapped to nearest physical pixels. |
| 219 | + |
| 220 | +Parameters |
| 221 | + region (region/frame, required) |
| 222 | + width (number, required) |
| 223 | + height (number, required) |
| 224 | + minWidthPixels (number|nil, optional) |
| 225 | + minHeightPixels (number|nil, optional) |
| 226 | + |
| 227 | +Behavior |
| 228 | + Calls SetWidth then SetHeight. |
| 229 | + |
| 230 | +Example |
| 231 | + -- 32×32 icon snapped to exact pixels: |
| 232 | + DFPixelUtil.SetSize(iconTexture, 32, 32) |
| 233 | + |
| 234 | + -- Highlight frame with minimum 1px in each dimension: |
| 235 | + DFPixelUtil.SetSize(highlightFrame, widgetWidth, height, 1, 1) |
| 236 | + |
| 237 | + |
| 238 | +===================================================================== |
| 239 | +6) SetPoint() |
| 240 | +===================================================================== |
| 241 | + |
| 242 | +Location |
| 243 | + DFPixelUtil.lua line 44 |
| 244 | + |
| 245 | +Signature |
| 246 | + DFPixelUtil.SetPoint(region, point, relativeTo, relativePoint, |
| 247 | + offsetX, offsetY, minOffsetXPixels, minOffsetYPixels) |
| 248 | + |
| 249 | +Purpose |
| 250 | + Anchors a region with offsets snapped to the nearest physical |
| 251 | + pixel. Prevents sub-pixel positioning that causes blurry or |
| 252 | + misaligned elements. |
| 253 | + |
| 254 | +Parameters |
| 255 | + region (region/frame, required) |
| 256 | + point (string, required) Anchor point on region |
| 257 | + ("TOPLEFT", "CENTER", etc.). |
| 258 | + relativeTo (frame, required) The reference frame. |
| 259 | + relativePoint (string, required) Anchor point on the |
| 260 | + reference frame. |
| 261 | + offsetX (number, required) X offset in UI units. |
| 262 | + offsetY (number, required) Y offset in UI units. |
| 263 | + minOffsetXPixels (number|nil, optional) Minimum X offset |
| 264 | + in pixels. |
| 265 | + minOffsetYPixels (number|nil, optional) Minimum Y offset |
| 266 | + in pixels. |
| 267 | + |
| 268 | +Behavior |
| 269 | + Calls region:SetPoint() with both offsets individually snapped: |
| 270 | + region:SetPoint(point, relativeTo, relativePoint, |
| 271 | + GetNearestPixelSize(offsetX, scale, minOffsetXPixels), |
| 272 | + GetNearestPixelSize(offsetY, scale, minOffsetYPixels)) |
| 273 | + |
| 274 | +Example |
| 275 | + -- Position a widget at an exact pixel offset: |
| 276 | + DFPixelUtil.SetPoint(label, "topleft", parent, "topleft", 10, -5) |
| 277 | + |
| 278 | + -- Border texture with guaranteed 1px offset: |
| 279 | + DFPixelUtil.SetPoint(border, "topleft", frame, "topleft", |
| 280 | + -1, 1, 1, 1) |
| 281 | + |
| 282 | + |
| 283 | +===================================================================== |
| 284 | +7) SetStatusBarValue() |
| 285 | +===================================================================== |
| 286 | + |
| 287 | +Location |
| 288 | + DFPixelUtil.lua line 51 |
| 289 | + |
| 290 | +Signature |
| 291 | + DFPixelUtil.SetStatusBarValue(statusBar, value) |
| 292 | + |
| 293 | +Purpose |
| 294 | + Sets a StatusBar's value such that the fill edge lands on an |
| 295 | + exact pixel boundary. Without this, partial-fill bars can have |
| 296 | + a blurry or jittering right edge. |
| 297 | + |
| 298 | +Parameters |
| 299 | + statusBar (StatusBar frame, required) |
| 300 | + value (number, required) The value to set. |
| 301 | + |
| 302 | +Behavior |
| 303 | + 1. Gets the bar's current width. If width is 0 or nil, falls |
| 304 | + back to plain statusBar:SetValue(value). |
| 305 | + |
| 306 | + 2. Computes the fill percentage: |
| 307 | + percent = ClampedPercentageBetween(value, min, max) |
| 308 | + |
| 309 | + 3. Edge cases: if percent is exactly 0.0 or 1.0 (empty or |
| 310 | + full), sets the value directly — no rounding needed. |
| 311 | + |
| 312 | + 4. Otherwise, snaps the fill width to the nearest pixel: |
| 313 | + numPixels = GetNearestPixelSize(width * percent, scale) |
| 314 | + |
| 315 | + 5. Reverse-maps back to a bar value: |
| 316 | + roundedValue = Lerp(min, max, numPixels / width) |
| 317 | + |
| 318 | + 6. Sets statusBar:SetValue(roundedValue). |
| 319 | + |
| 320 | + This ensures the bar's fill texture always ends on a pixel |
| 321 | + boundary, producing a clean edge. |
| 322 | + |
| 323 | +Example |
| 324 | + -- Update a health bar with pixel-perfect fill: |
| 325 | + DFPixelUtil.SetStatusBarValue(healthBar, currentHP) |
| 326 | + |
| 327 | + |
| 328 | +===================================================================== |
| 329 | +Function quick reference |
| 330 | +===================================================================== |
| 331 | + |
| 332 | + ┌─────────────────────────┬────────────────────────────────────┐ |
| 333 | + │ Function │ Purpose │ |
| 334 | + ├─────────────────────────┼────────────────────────────────────┤ |
| 335 | + │ GetPixelToUIUnitFactor │ 768 / screenHeight conversion │ |
| 336 | + │ GetNearestPixelSize │ Snap UI units to pixel boundary │ |
| 337 | + │ SetWidth │ Pixel-perfect width │ |
| 338 | + │ SetHeight │ Pixel-perfect height │ |
| 339 | + │ SetSize │ Pixel-perfect width + height │ |
| 340 | + │ SetPoint │ Pixel-perfect anchor offsets │ |
| 341 | + │ SetStatusBarValue │ Pixel-perfect bar fill edge │ |
| 342 | + └─────────────────────────┴────────────────────────────────────┘ |
0 commit comments