#!/usr/bin/perl
# -*- mode: perl; coding: utf-8-unix -*-
#
# 概要
#   EmacsでImageMagickの magick convertコマンドを使用するケースに代用する事で
#   magick convertコマンドそのままでいくつか起こる問題を解消したり機能拡張します。
#
# 使い方
#   0. 本スクリプトは画像ファイル表示可能なGUI起動のEmacs向けです。
#      -nwで起動したEmacsでは本スクリプトを使用する意味はありません。
#
#   1. JXRのデコーダー(JxrDecApp) および BPGのデコーダー(bpgdec) および
#      JXLのデコーダー(djxl)を予めインストールしておく必要があります。
#      それぞれ
#        - JXRは https://github.com/4creators/jxrlib
#        - BPGは https://bellard.org/bpg/
#        - JXLは https://github.com/libjxl/libjxl
#      からソースファイルを取得し、ビルドとインストールが必要です。
#      JXR,BPG,JXL形式の画像ファイルを扱わない場合は特に必要はありません。
#
#   2. 本スクリプトは ImageMagickの magick convertコマンドを使用します。
#      もしインストールされていない場合はインストールしてください。
#      デフォルトではパスの通った magick convertコマンドを使用します。
#
#   3. 本スクリプトをパスの通ったディレクトリ、もしくは任意の場所に
#      インストール(ファイルをコピー)してください。
#
#   4. .emacs に以下の設定を追加してください。
#      「3.」で パスの通ったディレクトリに本スクリプトをインストールした
#      場合の例示です。
#      ご自身の設定とぶつかる場合は都合に合わせて変更してください。
#      ;-------
#      ;;-----------------------------------------------------------------
#      ;; 組み込みImageMagickでの表示除外リスト
#      ;;-----------------------------------------------------------------
#      (when (image-type-available-p 'imagemagick)
#        (dolist (imgtype (list 'GIF 'GIF87 'JPEG 'JPG 'PNG 'PNG24 'PNG32 'PNG8
#                               'PNM 'PBM 'PGM 'PPM 'SVG 'SVGZ 'TIFF 'TIFF64
#                               'TIF 'XBM 'XPM))
#          (add-to-list 'imagemagick-types-inhibit imgtype))
#        (add-to-list 'imagemagick-types-inhibit 'JXR)
#        (add-to-list 'imagemagick-types-inhibit 'BPG)
#        (add-to-list 'imagemagick-types-inhibit 'JXL)
#        (imagemagick-register-types)
#        )
#
#      (add-to-list 'image-format-suffixes '(image/tga "tga"))
#      ;;-----------------------------------------------------------------
#      ;; image-dired setting
#      ;;-----------------------------------------------------------------
#      (dolist (imgext (list "xcf" "psd" "png" "jpeg" "jpg" "jfif" "gif" "tiff" "tif"
#                            "xbm" "xpm" "pbm" "pgm" "ppm" "svg" "bmp" "jp2" "heic"
#                            "jxr" "jxl" "bpg" "webp" "tga"))
#        (add-to-list 'image-file-name-extensions imgext))
#      (setq image-dired-cmd-create-thumbnail-program
#            "wrapped_convert")
#      (setq image-dired-cmd-create-thumbnail-options
#            '("-auto-orient" "%f[0]" "-size" "%wx%h" "-resize" "%wx%h>"
#              "-background" "white" "-gravity" "center" "-extent" "%wx%h"
#              "-strip" "jpeg:%t"))
#      (setq image-dired-cmd-create-temp-image-program
#            "wrapped_convert")
#      (setq image-dired-cmd-create-temp-image-options
#            '("-auto-orient" "%f[0]" "-size" "%wx%h" "-resize" "%wx%h>" "-strip"
#              "jpeg:%t"))
#      ;;-----------------------------------------------------------------
#      ;; image-mode setting
#      ;;-----------------------------------------------------------------
#      (setq image-type-file-name-regexps
#            '(("\\.png\\'" . png)
#              ("\\.gif\\'" . gif)
#              ("\\.jpe?g\\'" . jpeg)
#      ;;        ("\\.bmp\\'" . bmp)  ;workaround for failed to display BMP file.
#              ("\\.xpm\\'" . xpm)
#              ("\\.pbm\\'" . pbm)
#              ("\\.xbm\\'" . xbm)
#              ("\\.ps\\'" . postscript)
#              ("\\.tiff?\\'" . tiff)
#              ("\\.svgz?\\'" . svg)
#              ))
#      (setq image-converter--converters
#            '((graphicsmagick :command ("gm" "convert") :probe ("-list" "format"))
#              (ffmpeg :command "ffmpeg" :probe "-decoders")
#              (imagemagick :command "wrapped_convert" :probe ("-list" "format"))))
#      (add-to-list 'auto-mode-alist '("\\.jp2$"  . image-mode))
#      (add-to-list 'auto-mode-alist '("\\.heic$" . image-mode))
#      (add-to-list 'auto-mode-alist '("\\.jxr$"  . image-mode))
#      (add-to-list 'auto-mode-alist '("\\.bpg$"  . image-mode))
#      (add-to-list 'auto-mode-alist '("\\.jxl$"  . image-mode))
#      (setq image-converter 'imagemagick)
#      (setq image-use-external-converter t)
#      ;-------
#
# 解消が期待される問題
#   - 外部 magick convertコマンドを介して表示する際、JXR,BPG,JXLフォーマットの
#     画像が表示対象にならないのを解消します。
#     JXRのデコーダー(JxrDecApp) および BPGのデコーダー(bpgdec)がインストール
#     されていていれば magick convertコマンドとしてはデコード可能です。
#     しかし「magick convert -list format」に対応フォーマットとして表示されない為、
#     Emacsはデコードできないフォーマットと判断してしまいます。
#     本スクリプトではJXR,BPG,JXLのデコーダーの存在有無に 従って
#     magick convert -list format にJXR,BPG,JXLの情報を追加します。
#
#   - 画像よってデコードはできるが標準エラー出力(stderr)にメッセージが出ると
#     Emacsでの画像表示に失敗する事があるのを解消します。
#     本スクリプトで標準エラー出力を完全に捨てる事で対応しています。
#
#   - Emacsでのリモートファイル表現(/ssh:user@host:/pathes/filename.ext など)
#     を解釈するようになります。
#     sshを使用してファイル転送を行なう事で、image-dired でリモートの画像ファイルの
#     サムネイル表示や画像表示が可能になります。
#     予めパスワード無しでログインできるように公開鍵認証設定を行っておいて
#     ください。
#     プロトコル指定の文字列は ssh,scp,plink,pscp,sshx,scpxを解釈しますが、いずれの場合も
#     sshを使ってリモートファイルにアクセスしています。
#
#   - JXRおよびBPGファイルの標準入力からの読み込みを可能にします。
#     例えば「cat foo.jxr | magick convert jxr:- png:- > foo.png」はうまく動かない
#     のですが、本スクリプトで一時ファイルを介する事で可能にしています。
#
#   - JXLファイルの読み込みを可能にします。magick convertコマンド自体はJXLフォーマット
#     に未対応でも構いません。
#
# その他
#   素の magick convertコマンドの機能を損なわないようにラップしたかったのですが、
#   「+」で始まるオプション指定を解釈しないなど、Emacsから外部コマンドとして
#   使用する以外の目的では不十分かも知れません。
#   また、バグっていた場合は よしなに。
#

use strict;
use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat pass_through);
use File::Temp qw/tempfile tempdir/;

my @cmdconvert=("magick", "convert");
my $jxrdec="JxrDecApp" ;
my $jxldec="djxl" ;
my $bpgdec="bpgdec" ;

my $version="wrapped_convert ver 2026/01/18a" ;

$,=' ' ;

my %convert_opt =(
    "-adjoin"                   => 0,
    "-affine"                   => 1,
    "-alpha"                    => 1,
    "-antialias"                => 0,
    "-authenticate"             => 1,
    "-attenuate"                => 1,
    "-background"               => 1,
    "-bias"                     => 1,
    "-black-point-compensation" => 0,
    "-blue-primary"             => 1,
    "-bordercolor"              => 1,
    "-caption"                  => 1,
    "-clip"                     => 0,
    "-clip-mask"                => 1,
    "-clip-path"                => 1,
    "-colorspace"               => 1,
    "-comment"                  => 1,
    "-compose"                  => 1,
    "-compress"                 => 1,
    "-define"                   => 1,
    "-delay"                    => 1,
    "-density"                  => 1,
    "-depth"                    => 1,
    "-direction"                => 1,
    "-display"                  => 1,
    "-dispose"                  => 1,
    "-dither"                   => 1,
    "-encoding"                 => 1,
    "-endian"                   => 1,
    "-family"                   => 1,
    "-features"                 => 1,
    "-fill"                     => 1,
    "-filter"                   => 1,
    "-font"                     => 1,
    "-format"                   => 1,
    "-fuzz"                     => 1,
    "-gravity"                  => 1,
    "-green-primary"            => 1,
    "-intensity"                => 1,
    "-intent"                   => 1,
    "-interlace"                => 1,
    "-interline-spacing"        => 1,
    "-interpolate"              => 1,
    "-interword-spacing"        => 1,
    "-kerning"                  => 1,
    "-label"                    => 1,
    "-limit"                    => 2,
    "-loop"                     => 1,
    "-matte"                    => 0,
    "-mattecolor"               => 1,
    "-moments"                  => 0,
    "-monitor"                  => 0,
    "-orient"                   => 1,
    "-page"                     => 1,
    "-ping"                     => 0,
    "-pointsize"                => 1,
    "-precision"                => 1,
    "-preview"                  => 1,
    "-quality"                  => 1,
    "-quiet"                    => 0,
    "-read-mask"                => 1,
    "-red-primary"              => 1,
    "-regard-warnings"          => 0,
    "-remap"                    => 1,
    "-repage"                   => 1,
    "-respect-parentheses"      => 0,
    "-sampling-factor"          => 1,
    "-scene"                    => 1,
    "-seed"                     => 1,
    "-size"                     => 1,
    "-stretch"                  => 1,
    "-stroke"                   => 1,
    "-strokewidth"              => 1,
    "-style"                    => 1,
    "-support"                  => 1,
    "-synchronize"              => 0,
    "-taint"                    => 0,
    "-texture"                  => 1,
    "-tile-offset"              => 1,
    "-treedepth"                => 1,
    "-transparent-color"        => 1,
    "-undercolor"               => 1,
    "-units"                    => 1,
    "-verbose"                  => 0,
    "-view"                     => 0,
    "-virtual-pixel"            => 1,
    "-weight"                   => 1,
    "-white-point"              => 1,
    "-write-mask"               => 1,
    "-adaptive-blur"            => 1,
    "-adaptive-resize"          => 1,
    "-adaptive-sharpen"         => 1,
    "-alpha"                    => 1,
    "-annotate"                 => 2,
    "-auto-gamma"               => 0,
    "-auto-level"               => 0,
    "-auto-orient"              => 0,
    "-auto-threshold"           => 1,
    "-bench"                    => 1,
    "-black-threshold"          => 1,
    "-blue-shift"               => 1,
    "-blur"                     => 1,
    "-border"                   => 1,
    "-bordercolor"              => 1,
    "-brightness-contrast"      => 0,
    "-canny"                    => 1,
    "-cdl"                      => 1,
    "-channel"                  => 1,
    "-charcoal"                 => 1,
    "-chop"                     => 1,
    "-clahe"                    => 1,
    "-clamp"                    => 0,
    "-colorize"                 => 1,
    "-color-matrix"             => 1,
    "-colors"                   => 1,
    "-connected-components"     => 1,
    "-contrast"                 => 0,
    "-contrast-stretch"         => 1,
    "-convolve"                 => 1,
    "-cycle"                    => 1,
    "-decipher"                 => 1,
    "-deskew"                   => 1,
    "-despeckle"                => 0,
    "-distort"                  => 2,
    "-draw"                     => 1,
    "-edge"                     => 1,
    "-encipher"                 => 1,
    "-emboss"                   => 1,
    "-enhance"                  => 0,
    "-equalize"                 => 0,
    "-evaluate"                 => 2,
    "-extent"                   => 1,
    "-extract"                  => 1,
    "-fft"                      => 0,
    "-flip"                     => 0,
    "-floodfill"                => 2,
    "-flop"                     => 0,
    "-frame"                    => 1,
    "-function"                 => 2,
    "-gamma"                    => 1,
    "-gaussian-blur"            => 1,
    "-geometry"                 => 1,
    "-grayscale"                => 1,
    "-hough-lines"              => 1,
    "-identify"                 => 0,
    "-ift"                      => 0,
    "-implode"                  => 1,
    "-kmeans"                   => 1,
    "-kuwahara"                 => 1,
    "-lat"                      => 1,
    "-level"                    => 1,
    "-level-colors"             => 1,
    "-linear-stretch"           => 1,
    "-liquid-rescale"           => 1,
    "-local-contrast"           => 1,
    "-mean-shift"               => 1,
    "-median"                   => 1,
    "-mode"                     => 1,
    "-modulate"                 => 1,
    "-monochrome"               => 1,
    "-morphology"               => 2,
    "-motion-blur"              => 1,
    "-negate"                   => 0,
    "-noise"                    => 1,
    "-normalize"                => 0,
    "-opaque"                   => 1,
    "-ordered-dither"           => 1,
    "-paint"                    => 1,
    "-perceptible"              => 1,
    "-polaroid"                 => 1,
    "-posterize"                => 1,
    "-profile"                  => 1,
    "-quantize"                 => 1,
    "-raise"                    => 1,
    "-random-threshold"         => 1,
    "-range-threshold"          => 1,
    "-region"                   => 1,
    "-render"                   => 0,
    "-resample"                 => 1,
    "-resize"                   => 1,
    "-roll"                     => 1,
    "-rotate"                   => 1,
    "-rotational-blur"          => 1,
    "-sample"                   => 1,
    "-scale"                    => 1,
    "-segment"                  => 1,
    "-selective-blur"           => 1,
    "-sepia-tone"               => 1,
    "-set"                      => 2,
    "-shade"                    => 1,
    "-shadow"                   => 1,
    "-sharpen"                  => 1,
    "-shave"                    => 1,
    "-shear"                    => 1,
    "-sigmoidal-contrast"       => 1,
    "-sketch"                   => 1,
    "-solarize"                 => 1,
    "-sparse-color"             => 2,
    "-splice"                   => 1,
    "-spread"                   => 1,
    "-statistic"                => 2,
    "-strip"                    => 0,
    "-swirl"                    => 1,
    "-threshold"                => 1,
    "-thumbnail"                => 1,
    "-tile"                     => 1,
    "-tint"                     => 1,
    "-transform"                => 0,
    "-transparent"              => 1,
    "-transpose"                => 0,
    "-transverse"               => 0,
    "-trim"                     => 0,
    "-type"                     => 1,
    "-unique-colors"            => 0,
    "-unsharp"                  => 1,
    "-vignette"                 => 1,
    "-wave"                     => 1,
    "-wavelet-denoise"          => 1,
    "-white-balance"            => 0,
    "-white-threshold"          => 1,
    "-channel-fx"               => 1,
    "-separate"                 => 0,
    "-append"                   => 0,
    "-clut"                     => 0,
    "-coalesce"                 => 0,
    "-combine"                  => 0,
    "-compare"                  => 0,
    "-complex"                  => 1,
    "-composite"                => 0,
    "-copy"                     => 2,
    "-crop"                     => 1,
    "-deconstruct"              => 0,
    "-evaluate-sequence"        => 1,
    "-flatten"                  => 0,
    "-fx"                       => 1,
    "-hald-clut"                => 0,
    "-layers"                   => 1,
    "-morph"                    => 1,
    "-mosaic"                   => 0,
    "-poly"                     => 1,
    "-print"                    => 1,
    "-process"                  => 1,
    "-smush"                    => 1,
    "-write"                    => 1,
    "-clone"                    => 1,
    "-delete"                   => 1,
    "-duplicate"                => 1,
    "-insert"                   => 1,
    "-reverse"                  => 0,
    "-swap"                     => 1,
    "-debug"                    => 1,
    "-distribute-cache"         => 1,
    "-help"                     => 0,
    "-list"                     => 1,
    "-log"                      => 1,
    "-version"                  => 0
    ) ;

my @files ;
for( my $i=0 ; $i<=$#ARGV ; $i++ ){
    if( exists($convert_opt{$ARGV[$i]}) ){
        $i += $convert_opt{$ARGV[$i]} ;
    }else{
        push(@files, $ARGV[$i]) ;
    }
}

my $args = join(" ",@ARGV);

if( $args =~ /-list format/ ){
    open(INFILE, join(" ", (@cmdconvert, @ARGV, "|")));
    while(<INFILE>){
        print $_ ;
        if( /YUV\* r/ ){
            my $av_bpgdec=(`$bpgdec` ne "") ;
            if( $av_bpgdec ){
                printf("      BPG  %s--   BPG File Format Syntax\n",
                       $av_bpgdec ? "r" : "-") ;
            }

            my $av_jxrdec=(`$jxrdec` ne "") ;
            if( $av_jxrdec ){
                printf("      JXR  %s--   JXR File Format Syntax\n",
                       $av_jxrdec ? "r" : "-") ;
            }

            my $av_jxldec=(`$jxldec 2>&1` ne "") ;
            if( $av_jxldec ){
                printf("      JXL  %s--   JXL File Format Syntax\n",
                       $av_jxldec ? "r" : "-") ;
            }
        }
    }
    close(INFILE) ;

}elsif( $files[0] =~ /^(bpg|jxr):-(\[-?\d+\])?$/i ){
    my $fmt=$1 ;
    my ($tempfh, $tempfile) = tempfile(SUFFIX => ".$fmt");

    open(INFILE, "-") ;
    my $val ;
    while(read(INFILE, $val, 1)) {
        print $tempfh $val ;
    }
    close(INFILE) ;

    close(STDERR);
    my @modargs = &modarray($files[0], $tempfile, \@ARGV);
    open(INFILE, join(" ", (@cmdconvert, @modargs, "|"))) ;
    while(read(INFILE, $val, 1)) {
        print $val ;
    }
    close(INFILE) ;

    unlink($tempfile) ;

}elsif( $files[0] =~ /^(jxl):-(\[-?\d+\])?$/i ){
    my $fmt=$1 ;
    my ($itempfh, $itempfile) = tempfile(SUFFIX => ".$fmt");
    my ($otempfh, $otempfile) = tempfile(SUFFIX => ".ppm");

    open(INFILE, "-") ;
    my $val ;
    while(read(INFILE, $val, 1)) {
        print $itempfh $val ;
    }
    close(INFILE) ;
    `$jxldec $itempfile $otempfile 2>&1` ;

    close(STDERR);
    my @modargs = &modarray($files[0], $otempfile, \@ARGV);
    open(INFILE, join(" ", (@cmdconvert, @modargs, "|"))) ;
    while(read(INFILE, $val, 1)) {
        print $val ;
    }
    close(INFILE) ;

    unlink($itempfile) ;
    unlink($otempfile) ;

}else{
    close(STDERR);
    my $otempfh;
    my $otempfile;
    if( $files[0] =~ /^\/(ssh|scp|plink|pscp|sshx|scpx):(\S+):([\s\S]+)$/ ){
        my $user_host=$2;
        my $file=$3;
        my $inarg="-" ;
        my $frame="" ;
        if( $file =~ /(\[-?\d+\])$/ ){
            $frame=$1;
        }
        $file =~ s/(\[-?\d+\])$// ;
        $file =~ s/\\/\\\\/g;
        $file =~ s/ /\\ /g;
        $file =~ s/\(/\\\(/g;
        $file =~ s/\)/\\\)/g;
        $file =~ s/\[/\\\[/g;
        $file =~ s/\]/\\\]/g;
        $file =~ s/\{/\\\{/g;
        $file =~ s/\}/\\\}/g;
        $file =~ s/\+/\\\+/g;
        $file =~ s/\*/\\\*/g;
        $file =~ s/\?/\\\?/g;
        $file =~ s/\$/\\\$/g;

        if( $file =~ /\.(\w+?)$/ ){
            if( $1 =~ /jfif/i ){
                $inarg="jpg:-${frame}" ;
            }else{
                $inarg="$1:-${frame}" ;
            }
        }
        my @modargs = &modarray($files[0], $inarg, \@ARGV);
        open(INFILE, join(" ", ("ssh", "\'$user_host\'", "\'cat $file\'",
                                "|", $0, @modargs, "|")));
    }else{
        my $file = $files[0] ;
        my @modargs;
        if( $file =~ /\.jxl(\[-?\d+\])?$/i ){
            ($otempfh, $otempfile) = tempfile(SUFFIX => ".ppm");
            @modargs = &modarray($files[0], $otempfile, \@ARGV);
            $file =~ s/\[-?\d+\]$// ;
            `$jxldec $file $otempfile` ;
        }else{
            @modargs = &modarray("", "", \@ARGV);
        }
        open(INFILE, join(" ", (@cmdconvert, @modargs, "|"))) ;
    }
    my $val ;
    while(read(INFILE, $val, 1)) {
        print $val ;
    }
    close(INFILE) ;
    if( defined($otempfile) ){
        unlink($otempfile) ;
    }
}

exit 0 ;

sub modarray{
    my ($src, $dst, $ary)=@_;
    my @modary;

    foreach my $e (@$ary){
        if($e eq $src){
            push(@modary, "\'$dst\'");
        }else{
            push(@modary, "\'$e\'");
        }
    }
    return @modary;
}
