Bitweaver - Spritesheet management

Created by: Lester Caine, Last modification: 25 August 2025

This was pushing things a bit too much ;) Basically Mistral does not have enough process time to actually print the whole script for a super set of code. I will have to play with things myself to patch up the second half which is cobbled from the single size set of code.

<?php
// Directory containing the icons
$iconDir = 'icons/';
$spritesheetPath16 = 'spritesheet_16.png';
$spritesheetPath32 = 'spritesheet_32.png';
$outputCssFile16 = 'icon_map_16.css';
$outputCssFile32 = 'icon_map_32.css';
$outputImagePath16 = 'icon_map_16.png';
$outputImagePath32 = 'icon_map_32.png';

// Get a list of all icon files
$iconFiles16 = glob($iconDir . '16/*.png');
$iconFiles32 = glob($iconDir . '32/*.png');

// Initialize the icon maps
$iconMap16 = [];
$iconMap32 = [];

// Define the spritesheet dimensions and icon sizes
$spritesheetWidth16 = 1024; // Example width for 16x16
$spritesheetHeight16 = 1024; // Example height for 16x16
$spritesheetWidth32 = 1024; // Example width for 32x32
$spritesheetHeight32 = 1024; // Example height for 32x32
$iconWidth16 = 16; // Example width for 16x16
$iconHeight16 = 16; // Example height for 16x16
$iconWidth32 = 32; // Example width for 32x32
$iconHeight32 = 32; // Example height for 32x32

// Load the spritesheets using Imagick
$spritesheet16 = new Imagick($spritesheetPath16);
$spritesheet32 = new Imagick($spritesheetPath32);

// Calculate the position of each icon in the spritesheets
$x16 = 0;
$y16 = 0;
$x32 = 0;
$y32 = 0;

foreach ($iconFiles16 as $iconFile16) {
    $iconName = basename($iconFile16, '.png');
    $iconMap16[$iconName] = [
        "x" => $x16,
        "y" => $y16,
        "width" => $iconWidth16,
        "height" => $iconHeight16
    ];
    $x16 += $iconWidth16;
    if ($x16 >= $spritesheetWidth16) {
        $x16 = 0;
        $y16 += $iconHeight16;
    }
}

foreach ($iconFiles32 as $iconFile32) {
    $iconName = basename($iconFile32, '.png');
    $iconMap32[$iconName] = [
        "x" => $x32,
        "y" => $y32,
        "width" => $iconWidth32,
        "height" => $iconHeight32
    ];
    $x32 += $iconWidth32;
    if ($x32 >= $spritesheetWidth32) {
        $x32 = 0;
        $y32 += $iconHeight32;
    }
}

// Generate CSS code for 16x16 icons
$cssContent16 = ".icon-16 {n";
foreach ($iconMap16 as $iconName => $icon) {
    $cssContent16 .= "  .icon-16-$iconName {n";
    $cssContent16 .= "    width: 16px;n";
    $cssContent16 .= "    height: 16px;n";
    $cssContent16 .= "    background-image: url('$spritesheetPath16');n";
    $cssContent16 .= "    background-position: -{$icon['x']}px -{$icon['y']}px;n";
    $cssContent16 .= "  }n";
}
$cssContent16 .= "}n";

// Save CSS code for 16x16 icons to a file
file_put_contents($outputCssFile16, $cssContent16);

// Generate CSS code for 32x32 icons
$cssContent32 = ".icon-32 {n";
foreach ($iconMap32 as $iconName => $icon) {
    $cssContent32 .= "  .icon-32-$iconName {n";
    $cssContent32 .= "    width: 32px;n";
    $cssContent32 .= "    height: 32px;n";
    $cssContent32 .= "    background-image: url('$spritesheetPath32');n";
    $cssContent32 .= "    background-position: -{$icon['x']}px -{$icon['y']}px;n";
    $cssContent32 .= "  }n";
}
$cssContent32 .= "}n";

// Save CSS code for 32x32 icons to a file
file_put_contents($outputCssFile32, $cssContent32);

// Create a matching image using Imagick
$matchingImage = new Imagick();
$matchingImage->newImage($spritesheetWidth, $spritesheetHeight, new ImagickPixel('transparent'));
$matchingImage->setImageFormat('png');

foreach ($iconMap as $iconName => $icon) {
    $iconImage = new Imagick($iconDir . $iconName . '.png');
    $iconImage->resizeImage($iconWidth, $iconHeight, Imagick::FILTER_LANCZOS, 1);
    $iconImage->setImagePage(0, 0, 0, 0);
    $matchingImage->compositeImage($iconImage, Imagick::COMPOSITE_OVER, $icon['x'], $icon['y']);
}

$matchingImage->writeImage($outputImagePath);

// Display icons
echo "<div style='display: flex; flex-wrap: wrap;'>";
foreach ($iconMap as $iconName => $icon) {
    echo "<div class='icon icon-$iconName' style='margin: 5px;'></div>";
}
echo "</div>";

Taking a step back and just looking at a more realistic starting point

<?php
// Directory containing the icons
$iconDir = 'icons/';
$spritesheetPath = 'spritesheet.png';
$outputCssFile = 'icon_map.css';
$outputImagePath = 'icon_map.png';

// Get a list of all icon files
$iconFiles = glob($iconDir . '*.png');

// Initialize the icon map
$iconMap = [];

// Define the spritesheet dimensions and icon size
$spritesheetWidth = 1024; // Example width
$spritesheetHeight = 1024; // Example height
$iconWidth = 32; // Example width
$iconHeight = 32; // Example height

// Load the spritesheet using Imagick
$spritesheet = new Imagick($spritesheetPath);

// Calculate the position of each icon in the spritesheet
$x = 0;
$y = 0;
foreach ($iconFiles as $iconFile) {
    $iconName = basename($iconFile, '.png');
    $iconMap[$iconName] = [
        "x" => $x,
        "y" => $y,
        "width" => $iconWidth,
        "height" => $iconHeight
    ];
    $x += $iconWidth;
    if ($x >= $spritesheetWidth) {
        $x = 0;
        $y += $iconHeight;
    }
}

// Generate CSS code
$cssContent = ".icon {n";
foreach ($iconMap as $iconName => $icon) {
    $cssContent .= "  .icon-$iconName {n";
    $cssContent .= "    width: {$icon['width']}px;n";
    $cssContent .= "    height: {$icon['height']}px;n";
    $cssContent .= "    background-image: url('$spritesheetPath');n";
    $cssContent .= "    background-position: -{$icon['x']}px -{$icon['y']}px;n";
    $cssContent .= "  }n";
}
$cssContent .= "}n";

// Save CSS code to a file
file_put_contents($outputCssFile, $cssContent);

// Create a matching image using Imagick
$matchingImage = new Imagick();
$matchingImage->newImage($spritesheetWidth, $spritesheetHeight, new ImagickPixel('transparent'));
$matchingImage->setImageFormat('png');

foreach ($iconMap as $iconName => $icon) {
    $iconImage = new Imagick($iconDir . $iconName . '.png');
    $iconImage->resizeImage($iconWidth, $iconHeight, Imagick::FILTER_LANCZOS, 1);
    $iconImage->setImagePage(0, 0, 0, 0);
    $matchingImage->compositeImage($iconImage, Imagick::COMPOSITE_OVER, $icon['x'], $icon['y']);
}

$matchingImage->writeImage($outputImagePath);

// Display icons
echo "<div style='display: flex; flex-wrap: wrap;'>";
foreach ($iconMap as $iconName => $icon) {
    echo "<div class='icon icon-$iconName' style='margin: 5px;'></div>";
}
echo "</div>";