diff --git a/config/config.sample.php b/config/config.sample.php
index 2d76c7f..22f045f 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -29,4 +29,5 @@ define('ACTIVITYPUB_ENABLED', false);
define('CURL_ENABLED', true);
define('AUTH_ENABLED', false);
define('AUTH_REGISTER_ENABLED', false);
-define('COPYRIGHT_YEAR', '2018-'.date('Y'));
\ No newline at end of file
+define('COPYRIGHT_YEAR', '2018-'.date('Y'));
+define('DEBUG_MODE', false);
\ No newline at end of file
diff --git a/newpost.php b/newpost.php
index c25066f..770859d 100644
--- a/newpost.php
+++ b/newpost.php
@@ -2,7 +2,8 @@
if (!isset($argv[1])) die('usage: php newpost.php [slug]');
if (file_exists("blog/{$argv[1]}.md")) die("エラー: ファイル「blog/{$argv[1]}.md」は既に存在します。\n");
-define('CURL_ENABLED', false); // 黙れ・・・
+define('ROOT', realpath(__DIR__));
+require_once ROOT.'/config/config.php';
include('util.php');
$post = fopen("blog/{$argv[1]}.md", "w");
fwrite($post, "title: 【】\n");
@@ -13,5 +14,4 @@ fwrite($post, "thumbnail: \n");
fwrite($post, "thumborient: center\n");
fwrite($post, "category: \n");
fwrite($post, "----\n");
-fclose($post);
-?>
+fclose($post);
\ No newline at end of file
diff --git a/route.php b/route.php
index 539659f..2ffa1e8 100644
--- a/route.php
+++ b/route.php
@@ -1,6 +1,4 @@
Gif::class,
+ 'image/jpeg' => Jpeg::class,
+ 'image/png' => Png::class,
+ 'image/x-tga' => Targa::class,
+ ];
+
public function __construct(string $file) {
$file = ROOT.$file;
- // $file = ROOT.'/public/static/article/mock-screenshot.jpg';
+ // $file = ROOT.'/public/static/article/mock-screenshot.tga';
if (!file_exists($file)) return;
$fp = fopen($file, 'rb');
if (!$fp) return;
-
$extBytes = fread($fp, 15);
fclose($fp);
+
$this->bytes = filesize($file);
$this->size = $this->formatBytes($this->bytes);
$this->extension = pathinfo($file, PATHINFO_EXTENSION);
$this->filename = str_replace('.'.$this->extension, '', array_last(explode('/', $file)));
- if ($extBytes === false) return;
- else if (substr($extBytes, 0, 3) === "\xff\xd8\xff") {
- $this->type = 'image/jpeg';
- $this->parseJPEG($file);
- } else if (substr($extBytes, 0, 15) === "\x52\x49\x46\x46\x1a\x28\x00\x00\x57\x45\x42\x50\x56\x50\x38") {
- $this->type = 'image/webp';
- // $this->parseWEBP($file);
- } else if (substr($extBytes, 0, 3) === "\x42\x4d\x36") {
- $this->type = 'image/bmp';
- // $this->parseBMP($file);
- } else if (substr($extBytes, 0, 6) === "\x89\x50\x4e\x47\x0d\x0a") {
- $this->type = 'image/png';
- $this->fullInfo = $this->parsePNG($file);
- } else if (substr($extBytes, 0, 6) === "\x47\x49\x46\x38\x37\x61" || substr($extBytes, 0, 6) === "\x47\x49\x46\x38\x39\x61") {
- $this->type = 'image/gif';
- $this->fullInfo = $this->parseGIF($file);
- } else if (str_ends_with($file, '.tga')) {
- $this->type = 'image/tga';
- // $this->parseTGA($file);
- }
+ if (substr($extBytes, 0, 3) === "\xff\xd8\xff") $this->type = 'image/jpeg';
+ else if (substr($extBytes, 0, 15) === "\x52\x49\x46\x46\x1a\x28\x00\x00\x57\x45\x42\x50\x56\x50\x38") $this->type = 'image/webp';
+ else if (substr($extBytes, 0, 3) === "\x42\x4d\x36") $this->type = 'image/bmp';
+ else if (substr($extBytes, 0, 6) === "\x89\x50\x4e\x47\x0d\x0a") $this->type = 'image/png';
+ else if (substr($extBytes, 0, 6) === "\x47\x49\x46\x38\x37\x61" || substr($extBytes, 0, 6) === "\x47\x49\x46\x38\x39\x61") $this->type = 'image/gif';
+ else if (str_ends_with($file, '.tga')) $this->type = 'image/x-tga';
+
+ $this->fullInfo = $this->createHandler($this->type)->parse($file);
+ $this->fullInfo->type = $this->type;
+ $this->fullInfo->size = $this->size;
+ $this->fullInfo->bytes = $this->bytes;
}
- private function parseJPEG(string $file): \stdClass {
- $jpg = new \stdClass;
- $fp = fopen($file, 'rb');
+ ///////////////
- $i = 0;
- while (!feof($fp)) {
- $byte = fread($fp, 1);
- if ($byte !== "\xFF") continue;
-
- $marker = ord(fread($fp, 1));
- if ($marker >= 0xC0 && $marker <= 0xC3) { // 0xC0 = SOF0, 0xC1 = SOF1, 0xC2 = SOF2, 0xC3 = SOF3
- fread($fp, 2);
- $this->bpp = ord(fread($fp, 1));
- // n = ビッグエンディアン(2ビット)
- $this->height = unpack('n', fread($fp, 2))[1];
- $this->width = unpack('n', fread($fp, 2))[1];
- $jpg->width = $this->width;
- $jpg->height = $this->height;
- $jpg->bpp = $this->bpp;
- } else if ($marker == 0xC4) { // DHT (Huffmanテーブル(複数可))
- //
- } else if ($marker == 0xDB) { // DQT (量子化テーブル(複数可))
- //
- } else if ($marker == 0xDD) { // DRI (リセット期間)
- } else if ($marker == 0xDA) { // SOS(画像開始)
- } else if ($marker >= 0xD0 && $marker <= 0xD7) { // RST
- } else if ($marker >= 0xE1 && $marker <= 0xEF) { // APP
- } else if ($marker == 0xFE) { // COM
- } else if ($marker == 0xD9) { // EOI(画像終了)
- break;
- }
- }
-
- // kys($jpg);
-
- $i++;
- fclose($fp);
- return $jpg;
- }
-
- /**
- * TODO: 現在、IHDRヘッダーのみを受け取る
- */
- private function parsePNG(string $file): \stdClass {
- $png = new \stdClass();
- $fp = fopen($file, 'rb');
-
- fread($fp, 8); // マジックのスキップ
- unpack('N', fread($fp, 4))[1];
- $type = fread($fp, 4);
- if ($type !== 'IHDR') {
- fclose($fp);
- return $png;
- }
-
- // IHDRデータ
- $data = fread($fp, 13);
-
- // N = ビッグエンディアン(4ビット)
- $this->width = unpack('N', substr($data, 0, 4))[1];
- $this->height = unpack('N', substr($data, 4, 4))[1];
- $this->bitDepth = ord($data[8]);
- $this->numColors = 1 << $this->bitDepth;
- $this->colorType = ord($data[9]);
- $this->compression = 'DEFLATE';
- $this->filter = ord($data[11]);
- $this->interlace = ord($data[12]); // 0 = なし, 1 = Adam7
- $this->bpp = $this->bitDepth * $this->getSamplesPerPixel($this->colorType);
- $this->delayTime = 0;
- $this->hasAlpha = ($this->colorType === 4 || $this->colorType === 6);
-
- $png->width = $this->width;
- $png->height = $this->height;
- $png->bitDepth = $this->bitDepth;
- $png->numColors = $this->numColors;
- $png->colorType = $this->colorType;
- $png->compression = $this->compression;
- $png->filter = $this->filter;
- $png->interlace = $this->interlace;
- $png->bpp = $this->bpp;
- $png->delayTime = $this->delayTime;
- $png->hasAlpha = $this->hasAlpha;
-
- fclose($fp);
-
- return $png;
- }
-
- /**
- * TODO: 現在、インタレースした画像が未対応です。
- */
- private function parseGIF(string $file): \stdClass {
- $gif = new \stdClass();
- $fp = fopen($file, 'rb');
- $magic = fread($fp, 6); // マジックのスキップ
-
- // v = リトルエンディアン(2ビット)
- $this->width = unpack('v', fread($fp, 2))[1];
- $this->height = unpack('v', fread($fp, 2))[1];
- $packed = ord(fread($fp, 1));
- $this->bitDepth = ($packed & 0x07) + 1;
- $this->numColors = 1 << $this->bitDepth;
- $hasGct = $this->bitDepth === 8;
-
- $gif->width = $this->width;
- $gif->height = $this->height;
- $gif->bitDepth = $this->bitDepth;
- $gif->numColors = $this->numColors;
- if ($magic === 'GIF87a' || !$hasGct) return $gif; // それ以外情報がない
-
- $this->bgColor = (int)bin2hex(fread($fp, 1));
- $gif->bgColor = $this->bgColor;
- bin2hex(fread($fp, 1)); // いつでも 0:0?
-
- $gceLen = 0;
- while (!feof($fp)) {
- $skip = bin2hex(fread($fp, 3));
- $color = new RGB((int)($skip[0].$skip[1]), (int)($skip[2].$skip[3]), (int)($skip[4].$skip[5]));
- $this->globalColorTable[] = $color->rgb;
- if ($skip[0].$skip[1] === '21' && $skip[2].$skip[3] === 'f9') {
- $gceLen = (int)($skip[4].$skip[5]);
- break;
- }
- }
-
- // GCE開始
- $this->hasAlpha = (ord(fread($fp, 1)) & 0x01) === 0x01;
- $this->delayTime = (unpack('v', fread($fp, 2))[1]) / 100.0;
- $this->numTransparentPixels = ord(fread($fp, 1));
- $gif->hasAlpha = $this->hasAlpha;
- $gif->delayTime = $this->delayTime;
- $gif->numTransparentPixels = $this->numTransparentPixels;
- fread($fp, 1); // GCE終了
- fread($fp, 1); // 画像指定子開始
- fread($fp, 4); // 画面のXとY、いつでも(0, 0)
- fread($fp, 4); // 画像のサイズ、関数の開始で既に保存した
- $pack = ord(fread($fp, 1));
- $this->interlace = ($pack & 0x40); // 画像のサイズ、関数の開始で既に保存した
-
- $this->lzwMinCodeSize = ord(fread($fp, 1)); // 画像データ開始
- $gif->lzwMinCodeSize = $this->lzwMinCodeSize;
- $imageData = '';
- while (true) {
- $blockSize = ord(fread($fp, 1));
- if ($blockSize === 0) break;
- $imageData .= fread($fp, 1);
- }
-
- $this->imageData = base64_encode($imageData);
- $this->colorType = 3;
- $this->compression = 'LZW';
- $gif->colorType = $this->colorType;
- $gif->compression = $this->compression;
- $gif->globalColorTable = $this->globalColorTable;
- $gif->imageData = $this->imageData;
-
- fclose($fp);
- return $gif;
+ private function createHandler(string $ext): ImageInterface {
+ $class = $this->supported[$ext] ?? null;
+ if (!$class) throw new \RuntimeException("形式のハンドルが存在しない: {$ext}");
+ return new $class();
}
private function formatBytes(int $size, int $precision = 2): string {
@@ -240,15 +76,4 @@ class Image {
for ($i = 0; $size > 1024 && $i < count($units) - 1; ++$i) $size /= 1024;
return round($size, $precision).' '.$units[$i];
}
-
- private function getSamplesPerPixel(int $colorType): int {
- return match($colorType) {
- 0 => 1, // グレースケール
- 2 => 3, // RGB
- 3 => 1, // 指標した色(ペレット)
- 4 => 2, // グレースケール+アルファ
- 6 => 4, // RGBA
- default => 1,
- };
- }
}
\ No newline at end of file
diff --git a/src/Site/Lib/Image/Gif.php b/src/Site/Lib/Image/Gif.php
new file mode 100644
index 0000000..dc10ffa
--- /dev/null
+++ b/src/Site/Lib/Image/Gif.php
@@ -0,0 +1,91 @@
+width = unpack('v', fread($fp, 2))[1];
+ $this->height = unpack('v', fread($fp, 2))[1];
+ $packed = ord(fread($fp, 1));
+ $this->bitDepth = ($packed & 0x07) + 1;
+ $this->numColors = 1 << $this->bitDepth;
+ $hasGct = $this->bitDepth === 8;
+
+ $gif->width = $this->width;
+ $gif->height = $this->height;
+ $gif->bitDepth = $this->bitDepth;
+ $gif->numColors = $this->numColors;
+ if ($magic === 'GIF87a' || !$hasGct) return $gif; // それ以外情報がない
+
+ $this->bgColor = (int)bin2hex(fread($fp, 1));
+ $gif->bgColor = $this->bgColor;
+ bin2hex(fread($fp, 1)); // いつでも 0:0?
+
+ $gceLen = 0;
+ while (!feof($fp)) {
+ $skip = bin2hex(fread($fp, 3));
+ $color = new RGB((int)($skip[0].$skip[1]), (int)($skip[2].$skip[3]), (int)($skip[4].$skip[5]));
+ $this->globalColorTable[] = $color->rgb;
+ if ($skip[0].$skip[1] === '21' && $skip[2].$skip[3] === 'f9') {
+ $gceLen = (int)($skip[4].$skip[5]);
+ break;
+ }
+ }
+
+ // GCE開始
+ $this->hasAlpha = (ord(fread($fp, 1)) & 0x01) === 0x01;
+ $this->delayTime = (unpack('v', fread($fp, 2))[1]) / 100.0;
+ $this->numTransparentPixels = ord(fread($fp, 1));
+ $gif->hasAlpha = $this->hasAlpha;
+ $gif->delayTime = $this->delayTime;
+ $gif->numTransparentPixels = $this->numTransparentPixels;
+ fread($fp, 1); // GCE終了
+ fread($fp, 1); // 画像指定子開始
+ fread($fp, 4); // 画面のXとY、いつでも(0, 0)
+ fread($fp, 4); // 画像のサイズ、関数の開始で既に保存した
+ $pack = ord(fread($fp, 1));
+ $this->interlace = ($pack & 0x40); // 画像のサイズ、関数の開始で既に保存した
+
+ $this->lzwMinCodeSize = ord(fread($fp, 1)); // 画像データ開始
+ $gif->lzwMinCodeSize = $this->lzwMinCodeSize;
+ $imageData = '';
+ while (true) {
+ $blockSize = ord(fread($fp, 1));
+ if ($blockSize === 0) break;
+ $imageData .= fread($fp, 1);
+ }
+
+ $this->imageData = base64_encode($imageData);
+ $this->colorType = 3;
+ $this->compression = 'LZW';
+ $gif->colorType = $this->colorType;
+ $gif->compression = $this->compression;
+ $gif->globalColorTable = $this->globalColorTable;
+ $gif->imageData = $this->imageData;
+
+ fclose($fp);
+ return $gif;
+ }
+}
\ No newline at end of file
diff --git a/src/Site/Lib/Image/ImageInterface.php b/src/Site/Lib/Image/ImageInterface.php
new file mode 100644
index 0000000..a527646
--- /dev/null
+++ b/src/Site/Lib/Image/ImageInterface.php
@@ -0,0 +1,6 @@
+= 0xC0 && $marker <= 0xC3) { // 0xC0 = SOF0, 0xC1 = SOF1, 0xC2 = SOF2, 0xC3 = SOF3
+ fread($fp, 2);
+ $this->bpp = ord(fread($fp, 1));
+ // n = ビッグエンディアン(2ビット)
+ $this->height = unpack('n', fread($fp, 2))[1];
+ $this->width = unpack('n', fread($fp, 2))[1];
+ $jpg->width = $this->width;
+ $jpg->height = $this->height;
+ $jpg->bpp = $this->bpp;
+ } else if ($marker == 0xC4) { // DHT (Huffmanテーブル(複数可))
+ //
+ } else if ($marker == 0xDB) { // DQT (量子化テーブル(複数可))
+ //
+ } else if ($marker == 0xDD) { // DRI (リセット期間)
+ } else if ($marker == 0xDA) { // SOS(画像開始)
+ } else if ($marker >= 0xD0 && $marker <= 0xD7) { // RST
+ } else if ($marker >= 0xE1 && $marker <= 0xEF) { // APP
+ } else if ($marker == 0xFE) { // COM
+ } else if ($marker == 0xD9) { // EOI(画像終了)
+ break;
+ }
+ }
+
+ // kys($jpg);
+
+ $i++;
+ fclose($fp);
+ return $jpg;
+ }
+}
\ No newline at end of file
diff --git a/src/Site/Lib/Image/Png.php b/src/Site/Lib/Image/Png.php
new file mode 100644
index 0000000..c5e023d
--- /dev/null
+++ b/src/Site/Lib/Image/Png.php
@@ -0,0 +1,74 @@
+width = unpack('N', substr($data, 0, 4))[1];
+ $this->height = unpack('N', substr($data, 4, 4))[1];
+ $this->bitDepth = ord($data[8]);
+ $this->numColors = 1 << $this->bitDepth;
+ $this->colorType = ord($data[9]);
+ $this->compression = 'DEFLATE';
+ $this->filter = ord($data[11]);
+ $this->interlace = ord($data[12]); // 0 = なし, 1 = Adam7
+ $this->bpp = $this->bitDepth * $this->getSamplesPerPixel($this->colorType);
+ $this->hasAlpha = ($this->colorType === 4 || $this->colorType === 6);
+
+ $png->width = $this->width;
+ $png->height = $this->height;
+ $png->bitDepth = $this->bitDepth;
+ $png->numColors = $this->numColors;
+ $png->colorType = $this->colorType;
+ $png->compression = $this->compression;
+ $png->filter = $this->filter;
+ $png->interlace = $this->interlace;
+ $png->bpp = $this->bpp;
+ $png->hasAlpha = $this->hasAlpha;
+
+ fclose($fp);
+
+ return $png;
+ }
+
+ ///////////////
+
+ private function getSamplesPerPixel(int $colorType): int {
+ return match($colorType) {
+ 0 => 1, // グレースケール
+ 2 => 3, // RGB
+ 3 => 1, // 指標した色(ペレット)
+ 4 => 2, // グレースケール+アルファ
+ 6 => 4, // RGBA
+ default => 1,
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Site/Lib/Image/Targa.php b/src/Site/Lib/Image/Targa.php
new file mode 100644
index 0000000..7f3b1eb
--- /dev/null
+++ b/src/Site/Lib/Image/Targa.php
@@ -0,0 +1,115 @@
+ 'NO DATA',
+ 1 => 'UNCOMPRESSED COLOR-MAPPED IMAGE',
+ 2 => 'UNCOMPRESSED TRUE COLOR IMAGE',
+ 3 => 'UNCOMPRESSED GRAYSCALE IMAGE',
+ 9 => 'RUN-LENGTH ENCODED COLOR-MAPPED IMAGE',
+ 10 => 'RUN-LENGTH ENCODED TRUE-COLOR IMAGE',
+ 11 => 'RUN-LENGTH ENCODED GRAYSCALE IMAGE',
+ 32 => 'HUFFMAN-DELTA-RUN-LENGTH ENCODED COLOR-MAPPED IMAGE',
+ 33 => 'HUFFMAN-DELTA-RUN-LENGTH-4-PASS-QUADTREE-TYPE PROCESS ENCODED COLOR-MAPPED IMAGE',
+ ];
+
+ public function parse(string $file): \stdClass {
+ $tga = new \stdClass();
+ $fp = fopen($file, 'rb');
+
+ $this->id = ord(fread($fp, 1));
+ $this->colorMapType = ord(fread($fp, 1)); // 0 = なし, 1 = あり, 2~127 = Truevision向け, 128~255 = 開発者向け
+ $this->imageTypeByte = ord(fread($fp, 1));
+ $this->imageType = $this->imageTypes[$this->imageTypeByte];
+ $tga->id = $this->id;
+ $tga->colorMapType = $this->colorMapType;
+ $tga->imageTypeByte = $this->imageTypeByte;
+ $tga->imageType = $this->imageType;
+
+ $this->colorMapEntryIndex = unpack('v', fread($fp, 2))[1];
+ $this->colorMapLength = unpack('v', fread($fp, 2))[1];
+ $this->colorMapBits = ord(fread($fp, 1));
+ $tga->colorMapEntryIndex = $this->colorMapEntryIndex;
+ $tga->colorMapLength = $this->colorMapLength;
+ $tga->colorMapBits = $this->colorMapBits;
+
+ $this->x = unpack('v', fread($fp, 2))[1];
+ $this->y = unpack('v', fread($fp, 2))[1];
+ $this->width = unpack('v', fread($fp, 2))[1];
+ $this->height = unpack('v', fread($fp, 2))[1];
+ $this->bitDepth = ord(fread($fp, 1));
+ $tga->x = $this->x;
+ $tga->y = $this->y;
+ $tga->width = $this->width;
+ $tga->height = $this->height;
+ $tga->bitDepth = $this->bitDepth;
+
+ $desc = ord(fread($fp, 1));
+ $this->interlace = ($desc & 0xC0) >> 6;
+ $this->isTopToBottom = ($desc & 0x20) !== 0;
+ $this->isRightToLeft = ($desc & 0x10) !== 0;
+ $this->alphaBits = $desc & 0x0F;
+ $this->hasAlpha = ($this->alphaBits == 8) && in_array($this->imageTypeByte == [2, 3, 10, 11]);
+ $tga->interlace = $this->interlace;
+ $tga->isRightToLeft = $this->isRightToLeft;
+ $tga->isTopToBottom = $this->isTopToBottom;
+ $tga->alphaBits = $this->alphaBits;
+ $tga->hasAlpha = $this->hasAlpha;
+
+ $needsXFlip = $tga->isRightToLeft;
+ $needsYFlip = !$tga->isTopToBottom;
+ // 0010 0000
+
+ //---------
+ $imageIdBytes = $this->id;
+ $colorMapBytes = 0;
+ if ($this->colorMapType === 1 && $this->colorMapLength > 0) {
+ $bytesPerEntry = (int)($this->colorMapBits / 8);
+ $colorMapBytes = $this->colorMapLength * $bytesPerEntry;
+ }
+
+ $this->imageId = $imageIdBytes > 0 ? fread($fp, $imageIdBytes) : '';
+ $this->colorMapData = $colorMapBytes > 0 ? fread($fp, $colorMapBytes) : '';
+ $tga->imageId = $this->imageId;
+ $tga->colorMapData = $this->colorMapData;
+
+ if (in_array($this->imageTypeByte, [9, 10, 11])) { // RLE
+ // TODO
+ } else if (in_array($this->imageTypeByte, [1, 2, 3])) { // 非圧縮化
+ $this->bpp = (int)($this->bitDepth / 8);
+ $bytes = $this->width * $this->height * $this->bpp;
+ $this->imageData = fread($fp, $bytes);
+ $tga->bpp = $this->bpp;
+ $tga->imageData = $this->imageData;
+ }
+
+ fclose($fp);
+ return $tga;
+ }
+}
\ No newline at end of file
diff --git a/util.php b/util.php
index 88b2290..547fcaf 100644
--- a/util.php
+++ b/util.php
@@ -127,6 +127,7 @@ function uuid(): string {
}
function kys(mixed $arg): void {
+ if (!DEBUG_MODE) return;
if (gettype($arg) == 'boolean') $arg = $arg === true ? 'true' : 'false';
if (gettype($arg) == 'array' || gettype($arg) == 'object') {
foreach ($arg as $a) { if (gettype($a) == 'boolean') $a = $a === true ? 'true' : 'false'; }
@@ -140,6 +141,7 @@ function kys(mixed $arg): void {
}
function ffs(): void {
+ if (!DEBUG_MODE) return;
echo '';
echo '';
diff --git a/view/common/header.maron b/view/common/header.maron
index 5a51afa..d3e96b3 100644
--- a/view/common/header.maron
+++ b/view/common/header.maron
@@ -31,7 +31,7 @@
{@ if (isset($meta) && isset($meta->thumbnail) && !empty($meta->thumbnail)) @}
- {$ $imgspec = getImageInfo('/public/static/article/'.$meta->thumbnail); $}
+ {$ $imgspec = getImageInfo('/public/static/article/'.$meta->thumbnail)->fullInfo; $}