Image optimalization (#343)

I have minify images and convert them to webp format that reduce size
and keep quality.
Reduce size of bg image since its already blured by css so no need to
have it that big.

Reduce size of thumbs and helper images in helper modals should be fine
since they will not exceed the container size. Even that make them
larger than containter just to be safe
This commit is contained in:
Mittanicz
2025-03-26 10:05:45 -07:00
committed by GitHub
parent 6b07446965
commit 6678d6e36e
420 changed files with 845 additions and 55144 deletions
+9 -9
View File
@@ -73,7 +73,7 @@ export class HelpModal extends LitElement {
<div class="flex flex-col md:flex-row gap-4">
<div class="flex flex-col items-center">
<div data-i18n="help_modal.ui_leaderboard" class="text-gray-300">Leaderboard</div>
<img src="/images/helpModal/leaderboard.png" alt="Leaderboard" title="Leaderboard" />
<img src="/images/helpModal/leaderboard.webp" alt="Leaderboard" title="Leaderboard" />
</div>
<div>
<p data-i18n="help_modal.ui_leaderboard_desc">Shows the top players of the game and their names, % owned land and gold.</p>
@@ -85,7 +85,7 @@ export class HelpModal extends LitElement {
<div class="flex flex-col md:flex-row gap-4">
<div class="flex flex-col items-center w-full md:w-[80%]">
<div data-i18n="help_modal.ui_control" class="text-gray-300">Control panel</div>
<img src="/images/helpModal/controlPanel.png" alt="Control panel" title="Control panel" />
<img src="/images/helpModal/controlPanel.webp" alt="Control panel" title="Control panel" />
</div>
<div>
<p data-i18n="help_modal.ui_control_desc" class="mb-4">The control panel contains the following elements:</p>
@@ -103,7 +103,7 @@ export class HelpModal extends LitElement {
<div class="flex flex-col md:flex-row gap-4">
<div class="flex flex-col items-center">
<div data-i18n="help_modal.ui_options" class="text-gray-300">Options</div>
<img src="/images/helpModal/options.png" alt="Options" title="Options" />
<img src="/images/helpModal/options.webp" alt="Options" title="Options" />
</div>
<div>
<p data-i18n="help_modal.ui_options_desc" class="mb-4">The following elements can be found inside:</p>
@@ -121,7 +121,7 @@ export class HelpModal extends LitElement {
<div data-i18n="radial_title" class="text-2xl font-bold text-center">Radial menu</div>
<div class="flex flex-col md:flex-row gap-4">
<img src="/images/helpModal/radialMenu.png" alt="Radial menu" title="Radial menu", class="radial-menu-image" />
<img src="/images/helpModal/radialMenu.webp" alt="Radial menu" title="Radial menu", class="radial-menu-image" />
<div>
<p data-i18n="help_modal.radial_desc" class="mb-4">Right clicking (or touch on mobile) opens the radial menu. From there you can:</p>
<ul>
@@ -141,7 +141,7 @@ export class HelpModal extends LitElement {
<div class="flex flex-col md:flex-row gap-4 mt-2">
<div class="flex flex-col items-center w-full md:w-[80%]">
<div data-i18n="help_modal.info_enemy_panel" class="text-gray-300">Enemy info panel</div>
<img src="/images/helpModal/infoMenu.png" alt="Enemy info panel" title="Enemy info panel" />
<img src="/images/helpModal/infoMenu.webp" alt="Enemy info panel" title="Enemy info panel" />
</div>
<div class="pt-4">
<p data-i18n="help_modal.info_enemy_desc" class="mb-4">
@@ -160,7 +160,7 @@ export class HelpModal extends LitElement {
<div class="flex flex-col md:flex-row gap-4">
<div class="flex flex-col items-center w-full md:w-[62%]">
<div data-i18n="help_modal.info_ally_panel" class="text-gray-300">Ally info panel</div>
<img src="/images/helpModal/infoMenuAlly.png" alt="Ally info panel" title="Ally info panel" />
<img src="/images/helpModal/infoMenuAlly.webp" alt="Ally info panel" title="Ally info panel" />
</div>
<div class="pt-4">
<p data-i18n="help_modal.info_ally_desc" class="mb-4">
@@ -262,17 +262,17 @@ export class HelpModal extends LitElement {
<div class="flex flex-col md:flex-row gap-4 mt-2">
<div class="flex flex-col items-center">
<div data-i18n="help_modal.icon_crown" class="text-gray-300">Crown - This is the number 1 player in the leaderboard</div>
<img src="/images/helpModal/number1.png" alt="Number 1 player" title="Number 1 player" />
<img src="/images/helpModal/number1.webp" alt="Number 1 player" title="Number 1 player" />
</div>
<div class="flex flex-col items-center">
<div data-i18n="help_modal.icon_traitor" class="text-gray-300">Crossed swords - Traitor. This player attacked an ally.</div>
<img src="/images/helpModal/traitor.png" alt="Traitor" title="Traitor" />
<img src="/images/helpModal/traitor.webp" alt="Traitor" title="Traitor" />
</div>
<div class="flex flex-col items-center">
<div data-i18n="help_modal.icon_ally" class="text-gray-300">Handshake - Ally. This player is your ally.</div>
<img src="/images/helpModal/ally.png" alt="Ally" title="Ally" />
<img src="/images/helpModal/ally.webp" alt="Ally" title="Ally" />
</div>
</div>
</div>
+1 -1
View File
@@ -7,7 +7,7 @@ import "./components/Difficulties";
import "./components/baseComponents/Modal";
import { DifficultyDescription } from "./components/Difficulties";
import "./components/Maps";
import randomMap from "../../resources/images/RandomMap.png";
import randomMap from "../../resources/images/RandomMap.webp";
import { generateID } from "../core/Util";
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
import { JoinLobbyEvent } from "./Main";
+1 -1
View File
@@ -8,7 +8,7 @@ import "./components/baseComponents/Modal";
import "./components/baseComponents/Button";
import { DifficultyDescription } from "./components/Difficulties";
import "./components/Maps";
import randomMap from "../../resources/images/RandomMap.png";
import randomMap from "../../resources/images/RandomMap.webp";
import { GameInfo } from "../core/Schemas";
import { JoinLobbyEvent } from "./Main";
import { translateText } from "../client/Utils";
+1 -1
View File
@@ -28,7 +28,7 @@
.bg-image {
content: "";
background-image: url("/images/EuropeBackground.svg");
background-image: url("/images/EuropeBackground.webp");
background-position: center;
background-attachment: fixed;
background-size: cover;
+15 -15
View File
@@ -1,18 +1,18 @@
import world from "../../../resources/maps/WorldMapThumb.png";
import oceania from "../../../resources/maps/OceaniaThumb.png";
import europe from "../../../resources/maps/EuropeThumb.png";
import mena from "../../../resources/maps/MenaThumb.png";
import northAmerica from "../../../resources/maps/NorthAmericaThumb.png";
import southAmerica from "../../../resources/maps/SouthAmericaThumb.png";
import blackSea from "../../../resources/maps/BlackSeaThumb.png";
import africa from "../../../resources/maps/AfricaThumb.png";
import pangaea from "../../../resources/maps/PangaeaThumb.png";
import asia from "../../../resources/maps/AsiaThumb.png";
import mars from "../../../resources/maps/MarsThumb.png";
import britannia from "../../../resources/maps/BritanniaThumb.png";
import gatewayToTheAtlantic from "../../../resources/maps/GatewayToTheAtlanticThumb.png";
import australia from "../../../resources/maps/AustraliaThumb.png";
import iceland from "../../../resources/maps/IcelandThumb.png";
import world from "../../../resources/maps/WorldMapThumb.webp";
import oceania from "../../../resources/maps/OceaniaThumb.webp";
import europe from "../../../resources/maps/EuropeThumb.webp";
import mena from "../../../resources/maps/MenaThumb.webp";
import northAmerica from "../../../resources/maps/NorthAmericaThumb.webp";
import southAmerica from "../../../resources/maps/SouthAmericaThumb.webp";
import blackSea from "../../../resources/maps/BlackSeaThumb.webp";
import africa from "../../../resources/maps/AfricaThumb.webp";
import pangaea from "../../../resources/maps/PangaeaThumb.webp";
import asia from "../../../resources/maps/AsiaThumb.webp";
import mars from "../../../resources/maps/MarsThumb.webp";
import britannia from "../../../resources/maps/BritanniaThumb.webp";
import gatewayToTheAtlantic from "../../../resources/maps/GatewayToTheAtlanticThumb.webp";
import australia from "../../../resources/maps/AustraliaThumb.webp";
import iceland from "../../../resources/maps/IcelandThumb.webp";
import { GameMapType } from "../../core/game/Game";
+5
View File
@@ -7,6 +7,11 @@ declare module "*.jpg" {
export default value;
}
declare module "*.webp" {
const value: string;
export default value;
}
declare module "*.jpeg" {
const value: string;
export default value;
+23 -7
View File
@@ -338,15 +338,31 @@ function getNeighborCoords(x: number, y: number, map: Terrain[][]): Coord[] {
return coords;
}
async function createMapThumbnail(map: Terrain[][]): Promise<Bitmap> {
async function createMapThumbnail(
map: Terrain[][],
quality: number = 0.5,
): Promise<Bitmap> {
console.log("creating thumbnail");
const bitmap = new Bitmap(map.length, map[0].length);
for (let x = 0; x < bitmap.width; x++) {
for (let y = 0; y < bitmap.height; y++) {
const rgba = getThumbnailColor(map[x][y]);
const srcWidth = map.length;
const srcHeight = map[0].length;
const targetWidth = Math.max(1, Math.floor(srcWidth * quality));
const targetHeight = Math.max(1, Math.floor(srcHeight * quality));
const bitmap = new Bitmap(targetWidth, targetHeight);
for (let x = 0; x < targetWidth; x++) {
for (let y = 0; y < targetHeight; y++) {
const srcX = Math.floor(x / quality);
const srcY = Math.floor(y / quality);
const terrain =
map[Math.min(srcX, srcWidth - 1)][Math.min(srcY, srcHeight - 1)];
const rgba = getThumbnailColor(terrain);
bitmap.setPixelRGBA_i(x, y, rgba.r, rgba.g, rgba.b, rgba.a);
}
}
return bitmap;
}
@@ -357,9 +373,9 @@ function getThumbnailColor(t: Terrain): {
a: number;
} {
if (t.type === TerrainType.Water) {
//shoreline water
// Shoreline water
if (t.shoreline) return { r: 100, g: 143, b: 255, a: 0 };
//all other water
// All other water: adjust based on magnitude
const waterAdjRGB: number = 11 - Math.min(t.magnitude / 2, 10) - 10;
return {
r: Math.max(70 + waterAdjRGB, 0),
+11 -8
View File
@@ -1,8 +1,7 @@
import { encodePNGToStream } from "pureimage";
import sharp from "sharp";
import { generateMap } from "./TerrainMapGenerator.js";
import path from "path";
import fs from "fs/promises";
import { WriteStream, createWriteStream } from "fs";
const maps = [
"Africa",
@@ -56,17 +55,21 @@ async function loadTerrainMaps() {
process.cwd(),
"resources",
"maps",
map + "Thumb.png",
);
const thumbOutStream: WriteStream = createWriteStream(
thumbOutputPath,
"binary",
map + "Thumb.webp",
);
await Promise.all([
fs.writeFile(outputPath, mainMap),
fs.writeFile(miniOutputPath, miniMap),
encodePNGToStream(thumb, thumbOutStream),
sharp(Buffer.from(thumb.data), {
raw: {
width: thumb.width,
height: thumb.height,
channels: 4,
},
})
.webp({ quality: 45 })
.toFile(thumbOutputPath),
]);
}),
);