$ErrorActionPreference = 'Stop' Add-Type -AssemblyName System.Drawing $root = Split-Path -Parent $PSScriptRoot $guiDir = Join-Path $root 'src\main\resources\assets\bagtabs\textures\gui' $itemDir = Join-Path $root 'src\main\resources\assets\bagtabs\textures\item' $outDir = Join-Path $root 'art\mockups' New-Item -ItemType Directory -Force -Path $outDir | Out-Null function New-Bitmap { param([int]$Width, [int]$Height) return [System.Drawing.Bitmap]::new($Width, $Height, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb) } function Load-Bitmap { param([string]$Path) return [System.Drawing.Bitmap]::new($Path) } function Get-Half { param( [System.Drawing.Bitmap]$Image, [bool]$RightHalf = $false ) $halfWidth = [int]($Image.Width / 2) $sourceX = if ($RightHalf) { $halfWidth } else { 0 } $bmp = New-Bitmap $halfWidth $Image.Height $graphics = [System.Drawing.Graphics]::FromImage($bmp) try { $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::NearestNeighbor $graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::Half $graphics.DrawImage( $Image, [System.Drawing.Rectangle]::new(0, 0, $halfWidth, $Image.Height), [System.Drawing.Rectangle]::new($sourceX, 0, $halfWidth, $Image.Height), [System.Drawing.GraphicsUnit]::Pixel ) } finally { $graphics.Dispose() } return $bmp } function Tint-Bitmap { param( [System.Drawing.Bitmap]$Image, [Nullable[int]]$Rgb ) $bmp = New-Bitmap $Image.Width $Image.Height $tr = if ($Rgb.HasValue) { (($Rgb.Value -shr 16) -band 0xFF) / 255.0 } else { 1.0 } $tg = if ($Rgb.HasValue) { (($Rgb.Value -shr 8) -band 0xFF) / 255.0 } else { 1.0 } $tb = if ($Rgb.HasValue) { ($Rgb.Value -band 0xFF) / 255.0 } else { 1.0 } for ($x = 0; $x -lt $Image.Width; $x++) { for ($y = 0; $y -lt $Image.Height; $y++) { $src = $Image.GetPixel($x, $y) if ($src.A -eq 0) { $bmp.SetPixel($x, $y, $src) continue } $r = [Math]::Min(255, [int]($src.R * $tr)) $g = [Math]::Min(255, [int]($src.G * $tg)) $b = [Math]::Min(255, [int]($src.B * $tb)) $bmp.SetPixel($x, $y, [System.Drawing.Color]::FromArgb($src.A, $r, $g, $b)) } } return $bmp } function Compose-Layered { param( [System.Drawing.Bitmap]$Base, [System.Drawing.Bitmap]$Overlay ) $bmp = New-Bitmap $Base.Width $Base.Height $graphics = [System.Drawing.Graphics]::FromImage($bmp) try { $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::NearestNeighbor $graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::Half $graphics.DrawImage($Base, 0, 0, $Base.Width, $Base.Height) $graphics.DrawImage($Overlay, 0, 0, $Overlay.Width, $Overlay.Height) } finally { $graphics.Dispose() } return $bmp } function Draw-ScaledBitmap { param( [System.Drawing.Graphics]$Graphics, [System.Drawing.Bitmap]$Bitmap, [int]$X, [int]$Y, [int]$Width, [int]$Height ) $Graphics.DrawImage( $Bitmap, [System.Drawing.Rectangle]::new($X, $Y, $Width, $Height), [System.Drawing.Rectangle]::new(0, 0, $Bitmap.Width, $Bitmap.Height), [System.Drawing.GraphicsUnit]::Pixel ) } function Draw-Pin { param([System.Drawing.Graphics]$Graphics, [int]$X, [int]$Y) $yellow = [System.Drawing.SolidBrush]::new([System.Drawing.Color]::FromArgb(255, 235, 205, 86)) $red = [System.Drawing.SolidBrush]::new([System.Drawing.Color]::FromArgb(255, 205, 55, 44)) try { $Graphics.FillRectangle($yellow, $X + 1, $Y + 1, 3, 5) $Graphics.FillRectangle($yellow, $X + 2, $Y + 6, 2, 1) $Graphics.FillRectangle($red, $X, $Y + 2, 1, 1) } finally { $yellow.Dispose() $red.Dispose() } } function Draw-Fullness { param( [System.Drawing.Graphics]$Graphics, [int]$X, [int]$Y, [int]$Width, [int]$Height, [double]$Percent ) $back = [System.Drawing.SolidBrush]::new([System.Drawing.Color]::FromArgb(255, 40, 40, 40)) $fill = [System.Drawing.SolidBrush]::new([System.Drawing.Color]::FromArgb(255, 105, 180, 90)) try { $Graphics.FillRectangle($back, $X, $Y, $Width, $Height) $fillHeight = [Math]::Max(1, [int]([Math]::Round($Height * $Percent))) $Graphics.FillRectangle($fill, $X, $Y + ($Height - $fillHeight), $Width, $fillHeight) } finally { $back.Dispose() $fill.Dispose() } } function Draw-BagItem { param( [System.Drawing.Graphics]$Graphics, [System.Drawing.Bitmap]$BagBase, [System.Drawing.Bitmap]$BagOverlay, [Nullable[int]]$Color, [int]$X, [int]$Y, [int]$Size ) $tinted = Tint-Bitmap $BagBase $Color $bag = Compose-Layered $tinted $BagOverlay try { Draw-ScaledBitmap $Graphics $bag $X $Y $Size $Size } finally { $tinted.Dispose() $bag.Dispose() } } function Draw-CompactText { param( [System.Drawing.Graphics]$Graphics, [string]$Line1, [string]$Line2, [int]$X, [int]$Y, [int]$Width, [int]$Height, [System.Drawing.Color]$Color ) $font = [System.Drawing.Font]::new('Microsoft Sans Serif', 5.5, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Pixel) $brush = [System.Drawing.SolidBrush]::new($Color) $format = [System.Drawing.StringFormat]::new() $format.Alignment = [System.Drawing.StringAlignment]::Center $format.LineAlignment = [System.Drawing.StringAlignment]::Center try { $Graphics.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::SingleBitPerPixelGridFit $Graphics.DrawString($Line1, $font, $brush, [System.Drawing.RectangleF]::new($X, $Y - 1, $Width, [int]($Height / 2)), $format) $Graphics.DrawString($Line2, $font, $brush, [System.Drawing.RectangleF]::new($X, $Y + [int]($Height / 2) - 2, $Width, [int]($Height / 2) + 2), $format) } finally { $font.Dispose() $brush.Dispose() $format.Dispose() } } function New-Canvas { param([int]$Width, [int]$Height) $bmp = New-Bitmap $Width $Height $graphics = [System.Drawing.Graphics]::FromImage($bmp) $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::NearestNeighbor $graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::Half $graphics.Clear([System.Drawing.Color]::FromArgb(255, 48, 48, 52)) return @{ Bitmap = $bmp; Graphics = $graphics } } $bagNamer = Load-Bitmap (Join-Path $guiDir 'bag_namer.png') $tabBase = Get-Half (Load-Bitmap (Join-Path $guiDir 'bag_tabs_base.png')) $tabOverlay = Get-Half (Load-Bitmap (Join-Path $guiDir 'bag_tabs_overlay.png')) $compactBase = Get-Half (Load-Bitmap (Join-Path $guiDir 'bag_tabs_compact_base.png')) $compactOverlay = Get-Half (Load-Bitmap (Join-Path $guiDir 'bag_tabs_compact_overlay.png')) $dockTab = Load-Bitmap (Join-Path $guiDir 'bag_tabs_dock.png') $gear = Load-Bitmap (Join-Path $guiDir 'dock_gear.png') $lock = Load-Bitmap (Join-Path $guiDir 'dock_lock.png') $prev = Load-Bitmap (Join-Path $guiDir 'dock_prev.png') $next = Load-Bitmap (Join-Path $guiDir 'dock_next.png') $bagBase = Load-Bitmap (Join-Path $itemDir 'bag.png') $bagOverlay = Load-Bitmap (Join-Path $itemDir 'bag_overlay.png') $bagColors = @($null, 0x8032A8, 0xE4E5DE) $bagFullness = @(0.82, 0.56, 0.31) $compactNames = @( @{ Line1 = 'Canvas'; Line2 = 'Bag' }, @{ Line1 = 'Purple'; Line2 = 'Bag' }, @{ Line1 = 'Pale'; Line2 = 'Bag' } ) function Draw-StandardStrip { param( [System.Drawing.Graphics]$Graphics, [int]$X, [int]$Y, [bool]$Compact ) Draw-ScaledBitmap $Graphics $dockTab $X $Y 18 22 Draw-ScaledBitmap $Graphics $gear ($X + 5) ($Y + 2) 8 8 Draw-ScaledBitmap $Graphics $lock ($X + 5) ($Y + 12) 8 8 $tabStep = 21 $tabX = $X + 17 for ($i = 0; $i -lt 3; $i++) { if ($Compact) { $tintedBase = Tint-Bitmap $compactBase $bagColors[$i] $tab = Compose-Layered $tintedBase $compactOverlay try { Draw-ScaledBitmap $Graphics $tab ($tabX + ($i * $tabStep)) ($Y + 11) 22 11 } finally { $tintedBase.Dispose() $tab.Dispose() } $textColor = if ($bagColors[$i] -eq $null -or $bagColors[$i] -gt 0x7F7F7F) { [System.Drawing.Color]::FromArgb(255, 30, 30, 30) } else { [System.Drawing.Color]::White } Draw-CompactText $Graphics $compactNames[$i].Line1 $compactNames[$i].Line2 ($tabX + ($i * $tabStep) + 2) ($Y + 11) 18 10 $textColor Draw-Fullness $Graphics ($tabX + ($i * $tabStep) + 18) ($Y + 12) 2 8 $bagFullness[$i] } else { $tintedBase = Tint-Bitmap $tabBase $bagColors[$i] $tab = Compose-Layered $tintedBase $tabOverlay try { Draw-ScaledBitmap $Graphics $tab ($tabX + ($i * $tabStep)) $Y 22 22 } finally { $tintedBase.Dispose() $tab.Dispose() } Draw-BagItem $Graphics $bagBase $bagOverlay $bagColors[$i] ($tabX + ($i * $tabStep) + 3) ($Y + 2) 16 Draw-Fullness $Graphics ($tabX + ($i * $tabStep) + 18) ($Y + 3) 2 15 $bagFullness[$i] } } Draw-Pin $Graphics ($tabX + 1) ($Y + 1) $scrollX = $tabX + (3 * $tabStep) Draw-ScaledBitmap $Graphics $dockTab $scrollX $Y 18 22 Draw-ScaledBitmap $Graphics $prev ($scrollX + 5) ($Y + 2) 8 8 Draw-ScaledBitmap $Graphics $next ($scrollX + 5) ($Y + 12) 8 8 } function Save-Mockup { param( [string]$Name, [bool]$Compact, [bool]$Floating ) $canvas = New-Canvas 260 220 $bmp = $canvas.Bitmap $graphics = $canvas.Graphics try { $guiX = 42 $guiY = 28 Draw-ScaledBitmap $graphics $bagNamer $guiX $guiY $bagNamer.Width $bagNamer.Height if ($Floating) { $stripX = $guiX + 18 $stripY = $guiY + $bagNamer.Height + 12 } else { $stripX = $guiX + 2 $stripY = if ($Compact) { $guiY + $bagNamer.Height - 2 } else { $guiY + $bagNamer.Height - 3 } } Draw-StandardStrip $graphics $stripX $stripY $Compact $bmp.Save((Join-Path $outDir $Name), [System.Drawing.Imaging.ImageFormat]::Png) } finally { $graphics.Dispose() $bmp.Dispose() } } Save-Mockup 'rename_docked_normal.png' $false $false Save-Mockup 'rename_docked_compact.png' $true $false Save-Mockup 'rename_floating_horizontal_normal.png' $false $true Save-Mockup 'rename_floating_horizontal_compact.png' $true $true $bagNamer.Dispose() $tabBase.Dispose() $tabOverlay.Dispose() $compactBase.Dispose() $compactOverlay.Dispose() $dockTab.Dispose() $gear.Dispose() $lock.Dispose() $prev.Dispose() $next.Dispose() $bagBase.Dispose() $bagOverlay.Dispose()