Page No.2

webp or AVIF -> jpg へのコンバート

逆の変換も、基本的にはたいして違わない。ただ、ちょっと webp においては一筋縄ではいかないところもあるけれど・・・。

<?php
    $filename = 'target_image.webp';
    $extension = pathinfo( $filename )['extension'];
    $newName = str_replace( $extension, 'jpg', $filename );
    $quality = 80;

    $img = imagecreatefromwebp( $filename );
    // $img = imageceratefromavif( $filename );

    $result = imagejpeg( $img, $newName, $quality );
?>
PHP
CopyExpand

imagecreatefromwebp は animated WebP を読んでくれない

表題のとおり、imagecreatefromwebp() は アニメーションする webp をその引数に指定すると 下記のような Fatal error を吐き出して止まってしまう。
Fatal error:  gd-webp cannot allocate temporary buffer
そもそも imagecreatefromwebp() は、アニメーションwebp は読み取れない、と php.net/manual function.imagecreatefromwebp のページにも書いてある。

自分がこれに気がついたのは、いろいろテストをしているときに、ネットで拾った webp 画像においてこのエラーに遭遇した時である。そして、このエラーを回避しようとしてあれこれと策を講じることになる。ちなみに、その時拾ったいくつかのアニメーション webp ファイルは、拡張子を gif に変更すると、そのまま gif ファイルとして有効だった。たまたまなのかもしれないけれど・・・。

どうすればこのエラーを回避できるかと。
とりあえず思い浮かんだのは、エラーといえば、try & catch だろうかと。いままでほとんど使ったことがない、try & catch ではある。で、調べていろいろ試してみる。が、まず、try & catch では基本的に Fatal error は拾えないということ。

で、set_error_handler() を使う方法というのもいくつかあったけれど、結局、これも Fatal error の時は、 呼び出されないということらしく、徒労に終わる。

それならば、その webp ファイルがアニメーションかということを判別できれば!ということを考えた。まぁ、結果を言うとその方法を取るしかないようであり、php.net manual imagecreatefromwebp のページにもちゃんとそれ用の webpinfo() という関数が掲載されている。

しかしである。これはありがたい!とばかりにこの関数を使ってみた。が、自分の持っている animated webp は、この関数では網にかからなかった。しかたなく、再びネットを探してみると、animeted に限って判別する件のページを見つけることができた。ここにあり ->「stackoverflow – How to identify whether webp image is static or animated?」

ここにある関数や先の webpinfo() 関数を見ていると、アニメーションファイルには、そのファイル内に anim もしくは anmf という識別子があるようだ。で、あればそんなにややこしいことをする必要はないようであり、掲載されていた関数をちょっとアレンジして下のようにして使わせてもらうことにした。

<?php
    function isWebpAnimated ( string $file ) :string|bool {

        if ( ! is_file( $file ) ) {
            return false;
        }

        $imgwebp = file_get_contents( $file );
        
        if ( false !== strpos( $imgwebp, 'ANIM' ) or false !== strpos( $imgwebp, 'ANMF' ) ) {
            $isAnimated = 'y';
        } else {
            $isAnimated = 'n';
        }
        return $isAnimated;
    }
?>
PHP
CopyExpand

一応、この関数は機能した。しかし、一応である。と、いうのは、あるファイルは網にかかるけれど、別のファイルはかからないという、不可解な現象となる。これはいったいなぜ?ってな具合。で、最初に使った webpinfo() という関数を改めて見てみると、strtoupper() を使っていることで、なるほど!となる。

animated WebP の判別関数

これは大文字小文字が混在しているということである。ならば、ということで速くなった正規表現を使うことにする。stripos() でもいいんだけれど、条件が複数あるなら正規表現でいいのでは無いかと思う。

<?php
    function isWebpAnimated ( string $file ) :string|bool {

        if ( ! is_file( $file ) ) {
            return false;
        }

        $imgwebp = file_get_contents( $file );
        $pattern = '/anmf|anim/i';
        
        if ( preg_match( $pattern, $imgwebp ) ) {
            $isAnimated = 'y';
        } else {
            $isAnimated = 'n';                    }

        return $isAnimated;
    }
?>
PHP
CopyExpand

この正規表現を使った関数で、どちらも捉えることができた。テストしたのがわずかな数なので完璧とはいいがたいけれど。それにしても、だとしたらなぜ最初に使った webpinfo() 関数ではこぼれ落ちてしまったのだろうか。ちょっと気になるので、対象の文字列がファイルのどのあたりで現れるのか、strpos() にて出現場所を取得してみた(この時なぜか stripos() を使わなかったのである)。

anim、anmf の文字列をそれぞれ全部大文字、全部小文字の4種類で試してみたところ、正規表現で網にかかっていた一つのファイルがかからなくなってしまった。???である。で、疑問に思い、再び正規表現でマッチした文字列を取得してみると、これがなんと、anIM と、大小混ざった文字列となってた。なんでこんなことになっているんだろうか。微妙に不思議である。

webp or AVIf -> jpg converter page application

て、ことで、webp or AVIFjpg に変換するアプリケーションページのコード。AVIF の方も gif アニメーションにも対応しているとのことなんだけれど、そちらの具合はまだよくわからない。
デフォルトではこのファイルのあるフォルダ直下の targets フォルダにある webp & AVIF ファイルを jpg に変換します。一応、animated webp は除外するはず。できなければ、そのファイルの時点で Fatal error となって止まってしまう。

もし仮にこのコードを使用する場合、このコードを使用しての一切の責任は負いませんから完全自己責任にて使用して下さい。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>image file to jpeg from webp or AVIF converter</title>
<style>
    #mainlist span{display:block;}
</style>
</head>

<body>
<div id="mainframe">
	<div id="header"></div>
	<div id="maincontents">
		<div id="innerframe">
            <?php
                function isWebpAnimated ( string $file ) :string|bool {

                    if ( ! is_file( $file ) ) {
                        return false;
                    }

                    $imgwebp = file_get_contents( $file );
                    $pattern = '/anmf|anim/i';
                    
                    if ( preg_match( $pattern, $imgwebp ) ) {
                        $isAnimated = 'y';
                    } else {
                        $isAnimated = 'n';                    }

                    return $isAnimated;
                }

                if ( isset( $_GET['target'] ) ) {
                    $target = $_GET['target'];
                    $quality = ( int ) $_GET['quality'];

                    if ( empty ( $target ) ) {
                        $target = './targets/';
                    }

                    if ( file_exists( $target ) ) {
                        $files = scandir( $target );
        
                        if ( $files ) {
                            $count = 0;

                            echo '<ol id="mainlist">';
                            foreach ( $files as $val ) {

                                $extension = pathinfo( $val )['extension'];

                                if ( 'webp' === $extension or 'avif' === $extension ) {

                                    $liinner = array();
                                    $tarfile = $target . $val;

                                    $fileinfo = getimagesize( $tarfile );

                                    $newName = str_replace( $extension, 'jpg', $tarfile );

                                    if ( 'webp' === $extension ) {

                                        if ( 'y' === isWebpAnimated( $tarfile ) ) {

                                            echo '<p class="disablecreate" style="color:red;" data-img="' . $tarfile . '">Error disable create image from animation webp : ' . $tarfile . '</p>';
                                            continue;
                                        }
                                        $img = imagecreatefromwebp( $tarfile );
    
                                    } elseif ( 'avif' === $extension ) {
                                        $img = imagecreatefromavif( $tarfile );
                                    } else {
                                         $img = false;
                                    }
                                    
                                    if ( ! $img ) {
                                        continue;
                                    }
                                    $result = imagejpeg( $img, $newName, $quality );
                                        
                                    $liinner[] = '<span>original ' . $extension . ' : ' . $val . ' : ' . $fileinfo[3] . ' : ' . floor( filesize( $tarfile ) / 100 ) / 10 . 'kb </span>';
                                    $liinner[] = '<span>original ' . $extension . '</span><span><img src="' . $tarfile . '"></span>';
                                    if ( $result ) {
                                        
                                        $fileinfow = getimagesize( $newName );

                                        $liinner[] = '<span>jpeg : ' . $newName . ' : ' . $fileinfow[3] . ' : ' . floor( filesize( $newName ) / 100 ) / 10 . 'kb jpeg quality : ' . $quality . '</span>';
                                        $liinner[] = '<span><img src="' . $newName . '"></span>';
                                    } else {
                                        $liinner[] = '<span>Failed to convert to webp.</span>';
                                    }
                 
                                    echo '<li>' . implode( '', $liinner ) . '</li>';

                                    ++$count;
                                }
                                if ( $count > 200 ) {
                                    $liinner[] = '<li>Stopped, images count over 200!';
                                    echo '<li>' . implode( '', $liinner ) . '</li>';
                                    break;
                                }
                            }
                            echo '</ol>';
                        }
                    }
                } else {
            ?>
                    <form action="" method="get">
                        <h2>into image-files folder path. if many, comma separated.</h2>

                        <div style="display:grid;position:relative;grid-template-columns:1fr 4fr;">
                            <span>target directly :</span><span><input type="text" value="" name="target" size="75"> : default is "/targets/" </span>
                            <span>Quality : </span><span><input type="number" name="quality" min="50" max="100" value="80"></span>
                            <!-- <span>Operation : <select name="ope"><option selected value="0">to webp</option><option value="1">through webp to BASE64</option></select></span> -->
                            <span><input type="submit" name="submit" value="execute"></span><span></span>
                        </div>
                    </form>


            <?php
                }
            ?>
		</div>
	</div>
    <div id="reload" style="position:fixed;width:50px;height:50px;top:50%;right:10%;padding:10px;background:white;border:solid 1px lime;border-radius:5px;"><p>Reload</p></div>
</div>
</body>

<script type="text/javascript">
    window.onload = function() {
        const doc = document,
            dis = doc.getElementsByClassName( 'disablecreate' ),
            dis_len = dis.length,
            dis_target = [];

        for ( let i = 0; i < dis_len; ++i ) {
            const dataimg = dis[ i ].getAttribute( 'data-img' );

            dis_target.push( dataimg );
        }

        let ansstr='no';
        if (navigator.cookieEnabled) {
            ansstr='yes';
        }

        doc.getElementById( 'reload' ).onclick = () => {
            const loc = document.location,
                locroot = loc.protocol + '//' + loc.hostname + '/' + loc.pathname;

            location.href = locroot;
        }

        if ( dis_target.length ) {
            const infodiv = doc.createElement( 'div' ),
                parent = doc.getElementById( 'mainframe' );

            infodiv.style.cssText = 'position:fixed;width:600px;height:auto;top:30%;left:50%;margin-left:-300px;padding:20px;background:white;border:double 5px blue;border-radius:10px;opacity:0;visibility:hidden;transition:all 1.5s;'
            infodiv.innerHTML = '<h2 style="color:red;">Disable create from webp because animated</h2><ol style="margin:10px auto;text-align:left;"><li>' + dis_target.join( '</li><li>' ) + '</li></ol>';
            parent.appendChild( infodiv );

            infodiv.style.visibility = 'visible';
            infodiv.style.opacity = 1;

            infodiv.onclick = () => {
                infodiv.style.opacity = 0;
                infodiv.style.visibility = 'hidden';
            }
        }
    }
</script>
</html>
PHP
CopyExpand

Sanbanse Funabashi

Top

スクロールさせるか画像をクリックすると元に戻ります。