From 8b4e527008c5826b6a2df4df36d17ec1d51608b9 Mon Sep 17 00:00:00 2001 From: Andy Vandenberghe Date: Thu, 24 Sep 2026 01:05:04 +0200 Subject: [PATCH] Normalise undersized race icons to the set's 44x44 (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Togruta and Nautolan (male and female) shipped at 20x20 against every other race icon's 44x44, so those rows misaligned — the roster renders these files at their natural size, with no width/height attributes. Each is centred on a transparent 44x44 canvas rather than upscaled: the original pixels stay crisp and the change is reversible, at the cost of the artwork still reading smaller than its neighbours. Replacing them with real 44x44 art remains the better fix. test_race_images_are_all_44x44 / _class_images_ guard both sets against another odd-sized icon slipping in. swtor_unknown.png (class_id 0) is still missing, so #3 stays open. Refs #3 Co-Authored-By: Claude Opus 5 (1M context) --- images/race_images/swtor_nautolan_female.png | Bin 1104 -> 1313 bytes images/race_images/swtor_nautolan_male.png | Bin 1104 -> 1313 bytes images/race_images/swtor_togruta_female.png | Bin 1166 -> 1384 bytes images/race_images/swtor_togruta_male.png | Bin 1166 -> 1384 bytes tests/game/swtor_icon_dimensions_test.php | 68 +++++++++++++++++++ 5 files changed, 68 insertions(+) create mode 100644 tests/game/swtor_icon_dimensions_test.php diff --git a/images/race_images/swtor_nautolan_female.png b/images/race_images/swtor_nautolan_female.png index d974bec3bbac022bba2c096bca1b7a374ec7ad7a..393a1d7068b45de7d11bec467c992d37ea0ebe9d 100644 GIT binary patch literal 1313 zcmV++1>X9JP)27&On$b2k2mu0zk&GRPg9(EPmWwaE18?95@iX{j0%EKO+|jg&S2 zzP+=MgSDt|k~avYaU2Ij2%2in<}2sexO9%Stt~b-Iy^l*zzAJ_{qAo$zux7uTmR(F zt=r5eHM!bD*bcH$G-~mL7Zxt^IZ_$V*G4P6R-0e_;rEQ^1p<@0lDKiit2b|8`8Kc*H&k}t8j~$-v?x8W++4%z1)Ce=8)9F#B6AaTld116hHwuN|_~?j_|2m<`6M7f6 z+5Yi0zL5>1exEOH{R^s`X_6AHZ6I|+5U*0C8M9=Hp&Nu!CoinEdR`bbvetArx0oL8 z^Vvs#V>B4i+uY*Gf4|~rcMmBg7cOogO+mZmQGRUqF1~AkYEYa)!omRA)Re^x z%l5ec`IiXO>^h0stGbDS{5@19{i8gq0oM9Buv^GRnJb)Fz1iDQCN`s9U^$q*ql z27UmCC&gmZ>P}PdnwV2IIn@A;T6zH-9t*FWp z8hP>ww}Jp61R71F1zKxt$ELS_o-8kL;}xo83ign*8$GVQ_7l=%z}NR4;y4k0)TXKm z%1WVMU`s5Vun-`d1~3o^3`3C5Ms&`sGaig6Mt$Tgp}Q9I)0;m-mMLG|xkC`FAj%Oz z7~nbqq1UDrt)6_sb))e-n_ed(@J#~GB5wI8VWE}a{Kbp7j!7;pEZ61X{d;6-$@WVc zLJwhRif;>>6{k?Tjk6w`y%@Bls%nH%44&+AJb6Gm9@CB2X!|y|zr4%Qqpukq9$;XR zW+}b3h~a2V7<%Nz1R+mh51(E2NhTA7FtIF?<7olU?sIf}Oq!N_{Lx=%$19}c38;o* znju_^!MI|*8v+8;w6LuPeHzcpsw{DB8?6PdrTO0XuhO}6jX!<(XIAVC%l1i%ir3$H zmwEDx`KO=o>JKk4pA5+>iDg+hwn<%^IIcMP(pjd7RszESS2t|Gyp5@Al+yg}H*eBZ z6~5!JwZ6h^Hst!{4nOZ!Y?yO=&%v=wEX&3)4Lr{~dEr?$Bh9B^KvRMfce(qYyWBXx z%6or&i@$&RJ|BMY9x~5}TM>clQxr9Waq;|?RGR)^OqQl(>1_E)U9x1!k|j%)oGSkZ XgDzM4jnY5_00000NkvXXu0mjfA~|^O literal 1104 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 XgDzM4jnY5_00000NkvXXu0mjfA~|^O literal 1104 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'race_images', 44); + } + + public function test_class_images_are_all_44x44(): void + { + $this->assertAllSquare('class_images', 44); + } + + + /** + * @return array filename => [width, height] + */ + private function dimensions(string $dir): array + { + $path = dirname(__DIR__, 2) . '/images/' . $dir; + $out = array(); + + foreach (glob($path . '/*.png') as $file) + { + $size = getimagesize($file); + $out[basename($file)] = array($size[0], $size[1]); + } + + ksort($out); + + return $out; + } + + private function assertAllSquare(string $dir, int $expected): void + { + $wrong = array(); + + foreach ($this->dimensions($dir) as $name => $size) + { + if ($size !== array($expected, $expected)) + { + $wrong[$name] = $size[0] . 'x' . $size[1]; + } + } + + $this->assertSame( + array(), + $wrong, + sprintf('%s icons not %dx%d: %s', $dir, $expected, $expected, json_encode($wrong)) + ); + } +}