open-tibia-tools 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/effectFramesGenerator.html +42 -0
- package/effectFramesGenerator.ts +95 -0
- package/itemImageFramesGenerator.html +45 -0
- package/itemImageFramesGenerator.ts +140 -0
- package/itemImageGenerator.html +40 -0
- package/itemImageGenerator.ts +161 -0
- package/missileFramesGenerator.html +42 -0
- package/missileFramesGenerator.ts +95 -0
- package/otbEditor.html +21 -0
- package/otbEditor.ts +106 -0
- package/outfitImageGenerator.html +38 -0
- package/outfitImageGenerator.ts +109 -0
- package/outfitImagePhpGeneratorCode.ts +19 -0
- package/package.json +28 -0
- package/tests.html +29 -0
- package/tests.ts +218 -0
- package/tools/colored-outfit-images-generator/README.md +7 -0
- package/tools/colored-outfit-images-generator/abuse_warning.png +0 -0
- package/tools/colored-outfit-images-generator/animoutfit.php +103 -0
- package/tools/colored-outfit-images-generator/cacheGenerator.php +40 -0
- package/tools/colored-outfit-images-generator/config.php +25 -0
- package/tools/colored-outfit-images-generator/filesAsJson.php +22 -0
- package/tools/colored-outfit-images-generator/index.php +21 -0
- package/tools/colored-outfit-images-generator/libs/gifCreator.php +343 -0
- package/tools/colored-outfit-images-generator/libs/outfitter.php +312 -0
- package/tools/colored-outfit-images-generator/outfit.php +77 -0
- package/tools/colored-outfit-images-generator/release.php +40 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/1.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/1.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/10.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/10.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/2.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/2.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/3.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/3.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/4.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/4.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/5.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/5.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/6.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/6.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/7.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/7.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/8.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/8.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/9.gif +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/1-10/9.png +0 -0
- package/tools/item-image-frames-to-animated-gif-converter/README.md +20 -0
- package/tools/item-image-frames-to-animated-gif-converter/cli_convert.php +42 -0
- package/tools/item-image-frames-to-animated-gif-converter/generated-zip-archives/.gitignore +3 -0
- package/tools/item-image-frames-to-animated-gif-converter/generated-zip-archives/index.html +1 -0
- package/tools/item-image-frames-to-animated-gif-converter/index.php +97 -0
- package/tools/item-image-frames-to-animated-gif-converter/lib/abstractPngConverterAnimation.php +87 -0
- package/tools/item-image-frames-to-animated-gif-converter/lib/apngCreator.php +247 -0
- package/tools/item-image-frames-to-animated-gif-converter/lib/converterPngToApngAnimation.php +93 -0
- package/tools/item-image-frames-to-animated-gif-converter/lib/converterPngToGifAnimation.php +90 -0
- package/tools/item-image-frames-to-animated-gif-converter/lib/gifCreator.php +339 -0
- package/tsconfig.json +12 -0
- package/webpack.config.js +53 -0
- package/websiteImageGeneratorBase.ts +156 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
require_once('./config.php');
|
|
4
|
+
|
|
5
|
+
if (!file_exists('./cache.generated.txt')) {
|
|
6
|
+
require_once('./cacheGenerator.php');
|
|
7
|
+
exit;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
header('Cache-control: max-age=' . (60 * 60 * 24 * 365));
|
|
11
|
+
header('Expires: ' . gmdate(DATE_RFC1123, time() + 60 * 60 * 24 * 365));
|
|
12
|
+
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', 1337) . ' GMT');
|
|
13
|
+
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
|
14
|
+
header('HTTP/1.0 304 Not Modified');
|
|
15
|
+
header('Cache-Control: public');
|
|
16
|
+
header('Pragma: cache');
|
|
17
|
+
exit;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Block sites that hotlink your host and overload it
|
|
21
|
+
$abusersList = array('aurera-global.com', 'bad-server.com');
|
|
22
|
+
if (isset($_SERVER['HTTP_REFERER']) && (in_array(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), $abusersList) ||
|
|
23
|
+
in_array(substr(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), 4), $abusersList))
|
|
24
|
+
) {
|
|
25
|
+
header('Content-Type: image/png');
|
|
26
|
+
readfile('abuse_warning.png');
|
|
27
|
+
exit;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
require('libs/outfitter.php');
|
|
31
|
+
Outfitter::$outfitPath = $outfitImagesPath;
|
|
32
|
+
Outfitter::setResizeAllOutfitsTo64px($resizeAllOutfitsTo64px);
|
|
33
|
+
|
|
34
|
+
$id = 0;
|
|
35
|
+
if (isset($_GET['id'])) {
|
|
36
|
+
$id = (int)$_GET['id'];
|
|
37
|
+
}
|
|
38
|
+
if (!Outfitter::loadData($id, false)) {
|
|
39
|
+
exit('Outfit does not exist or file cache is not generated.');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
$mount = 0;
|
|
43
|
+
if (isset($_GET['mount'])) {
|
|
44
|
+
$mount = (int)$_GET['mount'];
|
|
45
|
+
}
|
|
46
|
+
if ($mount > 0 && !Outfitter::loadData($mount, true)) {
|
|
47
|
+
exit('Mount outfit does not exist or file cache is not generated.');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$head = 0;
|
|
51
|
+
if (isset($_GET['head'])) {
|
|
52
|
+
$head = (int)$_GET['head'];
|
|
53
|
+
}
|
|
54
|
+
$body = 0;
|
|
55
|
+
if (isset($_GET['body'])) {
|
|
56
|
+
$body = (int)$_GET['body'];
|
|
57
|
+
}
|
|
58
|
+
$legs = 0;
|
|
59
|
+
if (isset($_GET['legs'])) {
|
|
60
|
+
$legs = (int)$_GET['legs'];
|
|
61
|
+
}
|
|
62
|
+
$feet = 0;
|
|
63
|
+
if (isset($_GET['feet'])) {
|
|
64
|
+
$feet = (int)$_GET['feet'];
|
|
65
|
+
}
|
|
66
|
+
$addons = 0;
|
|
67
|
+
if (isset($_GET['addons'])) {
|
|
68
|
+
$addons = (int)$_GET['addons'];
|
|
69
|
+
}
|
|
70
|
+
$direction = 3;
|
|
71
|
+
if (isset($_GET['direction'])) {
|
|
72
|
+
$direction = (int)$_GET['direction'];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
$animationFrame = 1;
|
|
76
|
+
header('Content-type: image/gif');
|
|
77
|
+
imagegif(Outfitter::instance()->outfit($id, $addons, $head, $body, $legs, $feet, $mount, $direction, $animationFrame));
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* Script to pack PHP files into JSON format for outfitImagePhpGeneratorCode.ts
|
|
4
|
+
*
|
|
5
|
+
* Usage: php release.php > data.json
|
|
6
|
+
*
|
|
7
|
+
* This script reads selected files from directory,
|
|
8
|
+
* encodes their content in base64, and outputs a formatted JSON array
|
|
9
|
+
* with file paths and base64-encoded contents.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
$files = [
|
|
13
|
+
"libs/gifCreator.php",
|
|
14
|
+
"libs/outfitter.php",
|
|
15
|
+
"abuse_warning.png",
|
|
16
|
+
"animoutfit.php",
|
|
17
|
+
"cacheGenerator.php",
|
|
18
|
+
"config.php",
|
|
19
|
+
"index.php",
|
|
20
|
+
"outfit.php",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
$output = [];
|
|
24
|
+
|
|
25
|
+
foreach ($files as $relativePath) {
|
|
26
|
+
$fullPath = __DIR__ . '/' . $relativePath;
|
|
27
|
+
|
|
28
|
+
if (!file_exists($fullPath)) {
|
|
29
|
+
echo "Warning: File not found: $fullPath\n";
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
$content = file_get_contents($fullPath);
|
|
34
|
+
$base64Content = base64_encode($content);
|
|
35
|
+
|
|
36
|
+
$output[$relativePath] = $base64Content;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
echo json_encode($output, JSON_PRETTY_PRINT);
|
|
40
|
+
echo "\n";
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## PNG to APNG/GIF converter
|
|
2
|
+
|
|
3
|
+
In this folder are PHP files to generate animated item images from item images exported by `itemImageFramesGenerator.html`.
|
|
4
|
+
|
|
5
|
+
Put `items.zip` in this folder and run with the command line:
|
|
6
|
+
```
|
|
7
|
+
php cli_convert.php APNG items.zip
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
.zip unpacking in PHP is very slow on Windows (9616 sec for 50k images).
|
|
11
|
+
It's up to 200 times faster (43 sec) - with SSD disk -,
|
|
12
|
+
if you unpack `items.zip` first and then execute command with unpacked folder name as parameter ex.:
|
|
13
|
+
```
|
|
14
|
+
php cli_convert.php APNG items
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
In `index.php` is version for hosting online PNG to GIF converter.
|
|
18
|
+
|
|
19
|
+
Official host is:
|
|
20
|
+
https://item-images.ots.me/png-to-gif-converter/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
include_once(__DIR__ . '/lib/converterPngToApngAnimation.php');
|
|
4
|
+
include_once(__DIR__ . '/lib/converterPngToGifAnimation.php');
|
|
5
|
+
|
|
6
|
+
if (!isset($argv[1]) || !in_array($argv[1], ['GIF', 'APNG'])) {
|
|
7
|
+
echo 'Missing animation format: GIF or APNG' . PHP_EOL;
|
|
8
|
+
echo 'CLI usage example: php cli_convert.php APNG items.zip' . PHP_EOL;
|
|
9
|
+
echo 'CLI usage example: php cli_convert.php GIF items.zip' . PHP_EOL;
|
|
10
|
+
exit;
|
|
11
|
+
}
|
|
12
|
+
if (!isset($argv[2])) {
|
|
13
|
+
echo 'Missing path to items.zip file.' . PHP_EOL;
|
|
14
|
+
echo 'CLI usage example: php cli_convert.php APNG items.zip' . PHP_EOL;
|
|
15
|
+
echo 'CLI usage example: php cli_convert.php GIF items.zip' . PHP_EOL;
|
|
16
|
+
exit;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
$exportFormat = $argv[1];
|
|
20
|
+
$zipFilePath = $argv[2];
|
|
21
|
+
|
|
22
|
+
$startTime = time();
|
|
23
|
+
|
|
24
|
+
echo 'Started converter - it may take few minutes to generate APNGs/GIFs!' . PHP_EOL;
|
|
25
|
+
$loadFolder = substr($zipFilePath, -4, 4) !== '.zip';
|
|
26
|
+
if ($exportFormat == 'APNG') {
|
|
27
|
+
$converter = new ConverterPngToApngAnimation($zipFilePath, true, $loadFolder);
|
|
28
|
+
} else {
|
|
29
|
+
$converter = new ConverterPngToGifAnimation($zipFilePath, true, $loadFolder);
|
|
30
|
+
}
|
|
31
|
+
$animatedImagesZipArchivePath = './generated-zip-archives/items_' . microtime(true) . '.zip';
|
|
32
|
+
try {
|
|
33
|
+
$converter->convert($animatedImagesZipArchivePath, 0.2);
|
|
34
|
+
} catch (Exception $exception) {
|
|
35
|
+
exit('Exception occurred during APNG/GIF generation: ' . $exception->getMessage());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
echo 'Peak memory allocated: ' . round(memory_get_peak_usage(false) / 1024 / 1024, 2) . ' MB' . PHP_EOL;
|
|
39
|
+
echo 'Peak memory usage (real): ' . round(memory_get_peak_usage(true) / 1024 / 1024, 2) . ' MB' . PHP_EOL;
|
|
40
|
+
echo 'Execution time: ' . (time() - $startTime) . ' seconds' . PHP_EOL;
|
|
41
|
+
|
|
42
|
+
echo 'APNG/GIF IMAGES AND ANIMATIONS SAVED TO: ' . $animatedImagesZipArchivePath;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
NO INDEX
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
include_once(__DIR__ . '/lib/converterPngToApngAnimation.php');
|
|
4
|
+
include_once(__DIR__ . '/lib/converterPngToGifAnimation.php');
|
|
5
|
+
|
|
6
|
+
$generatedArchivesPath = './generated-zip-archives/';
|
|
7
|
+
|
|
8
|
+
// clean up downloads directory
|
|
9
|
+
$list = scandir($generatedArchivesPath);
|
|
10
|
+
$archiveFilesList = [];
|
|
11
|
+
foreach ($list as $file) {
|
|
12
|
+
if (substr($file, -4, 4) === '.zip') {
|
|
13
|
+
$archiveFilesList[$file] = $file;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
sort($archiveFilesList);
|
|
17
|
+
foreach ($archiveFilesList as $archiveFile) {
|
|
18
|
+
$archiveFilePath = $generatedArchivesPath . $archiveFile;
|
|
19
|
+
$timeSinceFileCreation = time() - filemtime($archiveFilePath);
|
|
20
|
+
if ($timeSinceFileCreation > 3600) {
|
|
21
|
+
unlink($archiveFilePath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
while (count($archiveFilesList) > 10) {
|
|
25
|
+
$archiveFile = array_shift($archiveFilesList);
|
|
26
|
+
$archiveFilePath = $generatedArchivesPath . $archiveFile;
|
|
27
|
+
unlink($archiveFilePath);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (isset($_FILES['items_zip']['tmp_name']) && is_uploaded_file($_FILES['items_zip']['tmp_name'])) {
|
|
31
|
+
$zipFilePath = $_FILES['items_zip']['tmp_name'];
|
|
32
|
+
|
|
33
|
+
$startTime = time();
|
|
34
|
+
|
|
35
|
+
echo 'Started converter - it may take few minutes to generate APNGs/GIFs!<br />';
|
|
36
|
+
if (isset($_POST['export_format']) && $_POST['export_format'] == 'APNG') {
|
|
37
|
+
$converter = new ConverterPngToApngAnimation($zipFilePath, true);
|
|
38
|
+
} else {
|
|
39
|
+
$converter = new ConverterPngToGifAnimation($zipFilePath, true);
|
|
40
|
+
}
|
|
41
|
+
$animatedImagesZipArchivePath = $generatedArchivesPath . 'items_' . microtime(true) . '.zip';
|
|
42
|
+
$framesInterval = 0.2;
|
|
43
|
+
if (isset($_POST['frames_interval']) && ((float)$_POST['frames_interval']) > 0.01) {
|
|
44
|
+
$framesInterval = (float)$_POST['frames_interval'];
|
|
45
|
+
echo 'Frames interval: ' . $framesInterval . '<br />';
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
$converter->convert($animatedImagesZipArchivePath, $framesInterval);
|
|
49
|
+
} catch (Exception $exception) {
|
|
50
|
+
exit('Exception occurred during APNG/GIF generation: ' . $exception->getMessage());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
echo 'Peak memory allocated: ' . round(memory_get_peak_usage(false) / 1024 / 1024, 2) . ' MB<br />';
|
|
54
|
+
echo 'Peak memory usage (real): ' . round(memory_get_peak_usage(true) / 1024 / 1024, 2) . ' MB<br />';
|
|
55
|
+
echo 'Execution time: ' . (time() - $startTime) . ' seconds<br />';
|
|
56
|
+
|
|
57
|
+
echo 'APNG/GIF IMAGES AND ANIMATIONS SAVED TO: ' . $animatedImagesZipArchivePath . '<br />';
|
|
58
|
+
echo '<h2>DOWNLOAD LINK: <a href="' . $animatedImagesZipArchivePath . '">' . $animatedImagesZipArchivePath . '</a></h2>';
|
|
59
|
+
exit;
|
|
60
|
+
}
|
|
61
|
+
?>
|
|
62
|
+
<html lang="en">
|
|
63
|
+
<head>
|
|
64
|
+
<title>PNG to APNG/GIF converter</title>
|
|
65
|
+
</head>
|
|
66
|
+
<body>
|
|
67
|
+
<h2>PNG to APNG/GIF converter</h2>
|
|
68
|
+
<h4>Converts PNG images from Item Image Frames Generator into APNG/GIF animations.</h4>
|
|
69
|
+
<h4>Also works with not animated PNG images from Item Images Generator - just converts them to APNG/GIF, without
|
|
70
|
+
animation.</h4>
|
|
71
|
+
<blockquote>
|
|
72
|
+
How to use:<br/><br/>
|
|
73
|
+
1. Generate items.zip using Item Images Generator:<br/>
|
|
74
|
+
<a href="https://item-images.ots.me/generator/">https://item-images.ots.me/generator/</a><br/>
|
|
75
|
+
<a href="https://item-images.ots.me/generator-animated-items/">https://item-images.ots.me/generator-animated-items/</a><br/>
|
|
76
|
+
2. Select generated file in upload form below.<br/>
|
|
77
|
+
3. Click "Convert to APNG/GIF"<br/>
|
|
78
|
+
4. Wait... it can take few minutes.<br/>
|
|
79
|
+
5. Click generated download link.<br/>
|
|
80
|
+
<br/>
|
|
81
|
+
<b>Server can process around 5 MB of items.zip per minute. Execution time limit is 5 minutes, so items.zip bigger
|
|
82
|
+
than 20 MB will probably fail.</b>
|
|
83
|
+
<br/>
|
|
84
|
+
</blockquote>
|
|
85
|
+
<form action="index.php" method="post" enctype="multipart/form-data">
|
|
86
|
+
Select file <b>items.zip</b> to upload:<br/>
|
|
87
|
+
<input type="file" name="items_zip"><br/>
|
|
88
|
+
Frames interval (0.2 = 5 frames per second):<br/>
|
|
89
|
+
<input type="text" name="frames_interval" value="0.2"><br/>
|
|
90
|
+
<select name="export_format">
|
|
91
|
+
<option value="APNG">APNG - animated PNG</option>
|
|
92
|
+
<option>GIF</option>
|
|
93
|
+
</select><br/>
|
|
94
|
+
<input type="submit" value="Convert to APNG/GIF" name="submit">
|
|
95
|
+
</form>
|
|
96
|
+
</body>
|
|
97
|
+
</html>
|
package/tools/item-image-frames-to-animated-gif-converter/lib/abstractPngConverterAnimation.php
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
include_once(__DIR__ . '/apngCreator.php');
|
|
4
|
+
|
|
5
|
+
abstract class AbstractPngConverterAnimation
|
|
6
|
+
{
|
|
7
|
+
protected static $transparentBackgroundColor = [255, 255, 255, 127];
|
|
8
|
+
/**
|
|
9
|
+
* @var string
|
|
10
|
+
*/
|
|
11
|
+
protected $inputPath;
|
|
12
|
+
/**
|
|
13
|
+
* @var bool
|
|
14
|
+
*/
|
|
15
|
+
protected $printProgress;
|
|
16
|
+
/**
|
|
17
|
+
* @var bool
|
|
18
|
+
*/
|
|
19
|
+
private $loadFolder;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param string $inputPath
|
|
23
|
+
*/
|
|
24
|
+
public function __construct($inputPath, $printProgress = true, $loadFolder = false)
|
|
25
|
+
{
|
|
26
|
+
$this->inputPath = $inputPath;
|
|
27
|
+
$this->printProgress = $printProgress;
|
|
28
|
+
$this->loadFolder = $loadFolder;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param string $saveToPath
|
|
33
|
+
* @param float $animationFrameDurationInSeconds
|
|
34
|
+
* @throws Exception
|
|
35
|
+
*/
|
|
36
|
+
public abstract function convert($saveToPath, $animationFrameDurationInSeconds = 0.2);
|
|
37
|
+
|
|
38
|
+
protected function getInputFiles()
|
|
39
|
+
{
|
|
40
|
+
if ($this->loadFolder) {
|
|
41
|
+
foreach ($this->getFilesFromFolder() as $fileName => $fileContents) {
|
|
42
|
+
yield $fileName => $fileContents;
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
foreach ($this->getFilesFromZip() as $fileName => $fileContents) {
|
|
46
|
+
yield $fileName => $fileContents;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected function getFilesFromZip()
|
|
52
|
+
{
|
|
53
|
+
$zip = new ZipArchive();
|
|
54
|
+
if ($zip->open($this->inputPath) === true) {
|
|
55
|
+
for ($i = 0; $i < $zip->numFiles; $i++) {
|
|
56
|
+
$fileName = $zip->getNameIndex($i);
|
|
57
|
+
|
|
58
|
+
$fileHandler = $zip->getStream($fileName);
|
|
59
|
+
if ($fileHandler === false) {
|
|
60
|
+
throw new InvalidArgumentException('Failed to read ZIP file: ' . $fileName);
|
|
61
|
+
}
|
|
62
|
+
$fileContents = stream_get_contents($fileHandler);
|
|
63
|
+
if ($fileContents === false) {
|
|
64
|
+
throw new InvalidArgumentException('Failed to read ZIP file contents: ' . $fileName);
|
|
65
|
+
}
|
|
66
|
+
fclose($fileHandler);
|
|
67
|
+
|
|
68
|
+
yield $fileName => $fileContents;
|
|
69
|
+
}
|
|
70
|
+
$zip->close();
|
|
71
|
+
} else {
|
|
72
|
+
throw new InvalidArgumentException('Failed to open ZIP archive.');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
protected function getFilesFromFolder()
|
|
77
|
+
{
|
|
78
|
+
$recursiveDirectoryIterator = new RecursiveDirectoryIterator($this->inputPath);
|
|
79
|
+
$recursiveIterator = new RecursiveIteratorIterator($recursiveDirectoryIterator);
|
|
80
|
+
|
|
81
|
+
foreach ($recursiveIterator as $file) {
|
|
82
|
+
if (is_file($file)) {
|
|
83
|
+
yield $file->getPathname() => file_get_contents($file);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/*
|
|
3
|
+
Animated PNG Creator
|
|
4
|
+
Version: 1.6.2
|
|
5
|
+
Licence: GNU Lesser General Public License
|
|
6
|
+
http://www.gnu.org/licenses/lgpl.html
|
|
7
|
+
|
|
8
|
+
Feedback, bug report, feature request: apng@zoznam.sk
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
class ApngCreator {
|
|
12
|
+
/*
|
|
13
|
+
The array containing the animation frames and it's properties.
|
|
14
|
+
Data structure:
|
|
15
|
+
$images[i] : array(
|
|
16
|
+
IMAGE => a GD resource of an image,
|
|
17
|
+
DELAY => the delay fraction before displaying the frame (in seconds) : array(
|
|
18
|
+
[0] => numerator,
|
|
19
|
+
[1] => denominator
|
|
20
|
+
),
|
|
21
|
+
DISPOSE => specifies how the output buffer should be changed at the end of the delay (before rendering the next frame)
|
|
22
|
+
BLEND => specifies whether the frame is to be alpha blended into the current output buffer content, or whether it should completely replace its region in the output buffer
|
|
23
|
+
)
|
|
24
|
+
*/
|
|
25
|
+
protected $gd_data = null;
|
|
26
|
+
protected $images = array();
|
|
27
|
+
// Contains the width and the height of the image, respectively the biggest dimensions in the animation.
|
|
28
|
+
protected $image_data = array();
|
|
29
|
+
public $save_alpha = true;
|
|
30
|
+
public $save_time = false;
|
|
31
|
+
// Defines the play count for the animation. Value 0 (zero) means infinite playing.
|
|
32
|
+
public $play_count = 0;
|
|
33
|
+
|
|
34
|
+
public $background_color = array(255, 255, 255, 127);
|
|
35
|
+
public $transparent_color = false;
|
|
36
|
+
|
|
37
|
+
// The PNG signature
|
|
38
|
+
const SIGNATURE = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
|
|
39
|
+
// Format strings for the pack() and unpack() functions representing some APNG chunk datas.
|
|
40
|
+
// To understant this formats please read the APNG specification.
|
|
41
|
+
const IHDR_data = "NNCCCCC";
|
|
42
|
+
const acTL_data = "NN";
|
|
43
|
+
const fcTL_data = "NNNNNnncc";
|
|
44
|
+
const tIME = "nccccc";
|
|
45
|
+
|
|
46
|
+
// The version of the class.
|
|
47
|
+
const VERSION = "1.6.2";
|
|
48
|
+
|
|
49
|
+
function APNG_Creator() {
|
|
50
|
+
$this->gd_data = gd_info();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
Returns IDAT chunk's data fields.
|
|
55
|
+
*/
|
|
56
|
+
protected function get_png_idat_data($img) {
|
|
57
|
+
ob_start();
|
|
58
|
+
imagepng($img, null);
|
|
59
|
+
$data = ob_get_contents();
|
|
60
|
+
$idat_data = "";
|
|
61
|
+
$chunk_data = "";
|
|
62
|
+
$idat_offset = 0;
|
|
63
|
+
ob_end_clean();
|
|
64
|
+
// There can be more IDAT chunks!!!
|
|
65
|
+
do {
|
|
66
|
+
$idat_offset = strpos($data, "IDAT", $idat_offset);
|
|
67
|
+
if ($idat_offset === false) return $idat_data;
|
|
68
|
+
$chunk_data = substr($data, $idat_offset - 4);
|
|
69
|
+
$chunk = unpack("Nlength", $chunk_data);
|
|
70
|
+
$chunk_data = substr($chunk_data, 8, $chunk['length']);
|
|
71
|
+
$idat_data .= $chunk_data;
|
|
72
|
+
$idat_offset += 4;
|
|
73
|
+
} while ($idat_offset !== false);
|
|
74
|
+
return $idat_data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
$name : the name of the chunk (for example IDAT, fdAT, tEXt, ...)
|
|
79
|
+
$data : the data fields of the chunk
|
|
80
|
+
Returns a complete chunk, in the standard format:
|
|
81
|
+
Length (4 bytes) - Name (4 bytes) - Data (length bytes) - CRC (4 bytes)
|
|
82
|
+
*/
|
|
83
|
+
protected function complete_chunk($name, $data) {
|
|
84
|
+
$name_data = $name . $data;
|
|
85
|
+
return (pack("N", strlen($data)) . $name_data . pack("N", crc32($name_data)));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
Calculates the x and y offsets of a frame regarding to the buffer (see documentation).
|
|
90
|
+
The values are returned in an array.
|
|
91
|
+
*/
|
|
92
|
+
protected function calculate_position($image, $position = "MIDDLE_CENTER") {
|
|
93
|
+
$x = imagesx($image);
|
|
94
|
+
$y = imagesy($image);
|
|
95
|
+
if (!(is_array($position))) {
|
|
96
|
+
switch ($position) {
|
|
97
|
+
case "TOP_LEFT":
|
|
98
|
+
$position = array(0, 0);
|
|
99
|
+
break;
|
|
100
|
+
case "TOP_CENTER":
|
|
101
|
+
$position = array(($this->image_data['WIDTH'] - $x) / 2, 0);
|
|
102
|
+
break;
|
|
103
|
+
case "TOP_RIGHT":
|
|
104
|
+
$position = array($this->image_data['WIDTH'] - $x, 0);
|
|
105
|
+
break;
|
|
106
|
+
case "BOTTOM_LEFT":
|
|
107
|
+
$position = array(0, $this->image_data['HEIGHT'] - $y);
|
|
108
|
+
break;
|
|
109
|
+
case "BOTTOM_CENTER":
|
|
110
|
+
$position = array(($this->image_data['WIDTH'] - $x) / 2, $this->image_data['HEIGHT'] - $y);
|
|
111
|
+
break;
|
|
112
|
+
case "BOTTOM_RIGHT":
|
|
113
|
+
$position = array($this->image_data['WIDTH'] - $x, $this->image_data['HEIGHT'] - $y);
|
|
114
|
+
break;
|
|
115
|
+
case "MIDDLE_LEFT":
|
|
116
|
+
$position = array(0, ($this->image_data['HEIGHT'] - $y) / 2);
|
|
117
|
+
break;
|
|
118
|
+
case "MIDDLE_RIGHT":
|
|
119
|
+
$position = array($this->image_data['WIDTH'] - $x, ($this->image_data['HEIGHT'] - $y) / 2);
|
|
120
|
+
break;
|
|
121
|
+
case "MIDDLE_CENTER":
|
|
122
|
+
default:
|
|
123
|
+
$position = array(($this->image_data['WIDTH'] - $x) / 2, ($this->image_data['HEIGHT'] - $y) / 2);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if ($position[0] + $x > $this->image_data['WIDTH']) $position[0] = $this->image_data['WIDTH'] - $x;
|
|
127
|
+
if ($position[0] < 0) $position[0] = 0;
|
|
128
|
+
if ($position[1] + $y > $this->image_data['HEIGHT']) $position[1] = $this->image_data['HEIGHT'] - $y;
|
|
129
|
+
if ($position[1] < 0) $position[1] = 0;
|
|
130
|
+
$position[0] = intval($position[0]);
|
|
131
|
+
$position[1] = intval($position[1]);
|
|
132
|
+
return $position;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public function destroy_images() {
|
|
136
|
+
foreach ($this->images as $frame) {
|
|
137
|
+
imagedestroy($frame['IMAGE']);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public function add_image($img, $position = "MIDDLE_CENTER", $delay = 1000, $dispose_op = 0, $blend_op = 0, $part_of_anim = true) {
|
|
142
|
+
$width = imagesx($img);
|
|
143
|
+
$height = imagesy($img);
|
|
144
|
+
if (!($width > 0 && $height > 0)) return false;
|
|
145
|
+
if (!(is_array($delay))) {
|
|
146
|
+
$delay = array(
|
|
147
|
+
intval($delay),
|
|
148
|
+
1000
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
$dispose_options = array("APNG_DISPOSE_OP_NONE", "APNG_DISPOSE_OP_BACKGROUND", "APNG_DISPOSE_OP_PREVIOUS");
|
|
152
|
+
$blend_options = array("APNG_BLEND_OP_SOURCE", "APNG_BLEND_OP_OVER");
|
|
153
|
+
if (is_string($dispose_op)) $dispose_op = array_search($dispose_op, $dispose_options);
|
|
154
|
+
if (is_string($blend_op)) $blend_op = array_search($blend_op, $blend_options);
|
|
155
|
+
$is_default_image = (count($this->images) === 0);
|
|
156
|
+
$this->images[] = array(
|
|
157
|
+
"IMAGE" => $img,
|
|
158
|
+
"DELAY" => $delay,
|
|
159
|
+
"DISPOSE" => intval($dispose_op) % 3,
|
|
160
|
+
"BLEND" => intval($blend_op) % 2,
|
|
161
|
+
"POSITION" => $position,
|
|
162
|
+
"PART_OF_ANIM" => (!($is_default_image && (! $part_of_anim)))
|
|
163
|
+
);
|
|
164
|
+
if (!isset($this->image_data['WIDTH']) || $width > $this->image_data['WIDTH']) $this->image_data['WIDTH'] = $width;
|
|
165
|
+
if (!isset($this->image_data['HEIGHT']) || $height > $this->image_data['HEIGHT']) $this->image_data['HEIGHT'] = $height;
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public function getAPNG() {
|
|
170
|
+
$fileContent = '';
|
|
171
|
+
$out = "";
|
|
172
|
+
$out .= self::SIGNATURE;
|
|
173
|
+
// IHDR
|
|
174
|
+
$out .= $this->complete_chunk("IHDR", pack(self::IHDR_data, $this->image_data['WIDTH'], $this->image_data['HEIGHT'], 8, ($this->save_alpha ? 6 : 2), 0, 0, 0));
|
|
175
|
+
// Saving time data
|
|
176
|
+
if ($this->save_time) {
|
|
177
|
+
$out .= $this->complete_chunk("tEXt", "Creation Time" . chr(0) . date("D, d M Y H:i:s O"));
|
|
178
|
+
}
|
|
179
|
+
// tRNS - transparent color
|
|
180
|
+
if ($this->transparent_color && !($this->save_alpha)) {
|
|
181
|
+
$out .= $this->complete_chunk("tRNS", pack("nnn", $this->transparent_color[0], $this->transparent_color[1], $this->transparent_color[2]));
|
|
182
|
+
}
|
|
183
|
+
// acTL
|
|
184
|
+
$out .= $this->complete_chunk("acTL", pack(self::acTL_data, count($this->images), abs(intval($this->play_count))));
|
|
185
|
+
$fileContent .= $out;
|
|
186
|
+
$sequence_number = 0;
|
|
187
|
+
$is_default_image = true;
|
|
188
|
+
foreach ($this->images as $frame) {
|
|
189
|
+
$image = $frame['IMAGE'];
|
|
190
|
+
$x = imagesx($image);
|
|
191
|
+
$y = imagesy($image);
|
|
192
|
+
$position = $this->calculate_position($image, $frame['POSITION']);
|
|
193
|
+
$destroy_image = false;
|
|
194
|
+
$is_tc = imageistruecolor($image);
|
|
195
|
+
/*
|
|
196
|
+
According to the APNG specification the default image can't be positioned via the fcTL
|
|
197
|
+
x_offset and y_offset properties. So the script in case of the default image
|
|
198
|
+
creates a new resized frame, fills it with the specified background (default color is fully
|
|
199
|
+
transparent - if the save_alpha option is set to true) and copyes the frame to it's position.
|
|
200
|
+
*/
|
|
201
|
+
$align_needed = ($is_default_image && ($x < $this->image_data['WIDTH'] || $y < $this->image_data['HEIGHT']));
|
|
202
|
+
if ((! $is_tc) || $align_needed) {
|
|
203
|
+
if ($align_needed) {
|
|
204
|
+
$i2x = $this->image_data['WIDTH'];
|
|
205
|
+
$i2y = $this->image_data['HEIGHT'];
|
|
206
|
+
$i2_offsx = ($this->image_data['WIDTH'] - $x) / 2;
|
|
207
|
+
$i2_offsy = ($this->image_data['HEIGHT'] - $y) / 2;
|
|
208
|
+
$position = array(0, 0);
|
|
209
|
+
} else {
|
|
210
|
+
$i2x = $x;
|
|
211
|
+
$i2y = $y;
|
|
212
|
+
$i2_offsx = 0;
|
|
213
|
+
$i2_offsy = 0;
|
|
214
|
+
}
|
|
215
|
+
$image2 = imagecreatetruecolor($i2x, $i2y);
|
|
216
|
+
$bckg = imagecolorallocatealpha($image2, $this->background_color[0], $this->background_color[1], $this->background_color[2], $this->background_color[3]);
|
|
217
|
+
imagefill($image2, 0, 0, $bckg);
|
|
218
|
+
imagecopy($image2, $image, $i2_offsx, $i2_offsy, 0, 0, $x, $y);
|
|
219
|
+
$image = $image2;
|
|
220
|
+
$x = $i2x;
|
|
221
|
+
$y = $i2y;
|
|
222
|
+
$destroy_image = true;
|
|
223
|
+
}
|
|
224
|
+
imagesavealpha($image, !(!($this->save_alpha)));
|
|
225
|
+
$out = "";
|
|
226
|
+
// fcTL
|
|
227
|
+
if ($frame['PART_OF_ANIM']) {
|
|
228
|
+
$out .= $this->complete_chunk("fcTL", pack(self::fcTL_data, $sequence_number, $x, $y, $position[0], $position[1], $frame['DELAY'][0], $frame['DELAY'][1], $frame['DISPOSE'], $frame['BLEND']));
|
|
229
|
+
$sequence_number++;
|
|
230
|
+
}
|
|
231
|
+
// IDAT, fdAT
|
|
232
|
+
if ($is_default_image) {
|
|
233
|
+
$out .= $this->complete_chunk("IDAT", $this->get_png_idat_data($image));
|
|
234
|
+
} else {
|
|
235
|
+
$out .= $this->complete_chunk("fdAT", pack("N", $sequence_number) . $this->get_png_idat_data($image));
|
|
236
|
+
$sequence_number++;
|
|
237
|
+
}
|
|
238
|
+
$fileContent .= $out;
|
|
239
|
+
if ($destroy_image) imagedestroy($image);
|
|
240
|
+
$is_default_image = false;
|
|
241
|
+
}
|
|
242
|
+
$IEND = $this->complete_chunk("IEND", "");
|
|
243
|
+
$fileContent .= $IEND;
|
|
244
|
+
|
|
245
|
+
return $fileContent;
|
|
246
|
+
}
|
|
247
|
+
}
|