Category Archives: 未分類

2020-11-28

Emacs Lispで画像を表示する方法(Emacs 27.1で確認)

参考

画像の表示方法については、Emacs LispマニュアルのImagesの所で説明されている。

手順

Emacs Lispで画像を表示するには次の二つのステップを踏む。

  1. Image Descriptorを作成する
  2. Image Descriptorをオーバーレイまたはテキストプロパティに設定する

まず第一にImage Descriptorを作成する。Image Descriptorとは画像をどのように表示するかを指定するリストで41.17.2 Image Descriptorsに書かれている形式で記述する。自分で組み立てても良いが41.17.8 Defining Imagesに書かれている create-image 等の関数を使うこともできる。SVG画像を作る場合は41.17.6 SVG Imagesに書かれている関数を使うこともできる。

次に作成したImage Descriptorをオーバーレイ(41.9 Overlays)またはテキストプロパティ(33.19 Text Properties)の、displayプロパティ(41.16 The display Property)に設定する。それによってはじめて実際に画像が表示される。自分でオーバーレイやテキストプロパティを設定しても良いが41.17.9 Showing Imagesに書かれている insert-image 等の関数を使うこともできる。この関数はバッファ内に適当な(" "等の)文字列を挿入してそのテキストプロパティのdisplayプロパティにImage Descriptorを設定する。

実例

それではやってみよう。

まず適当な画像ファイルを用意して、それを読み込むようなImage Descriptorを作成する。次の式を評価するとImage Descriptorが返ってくる。

(create-image (expand-file-name "~/tmp/karasawa.jpg"))

結果は次のようなリストになる。 :scale 1 の部分は高DPI環境では異なるかもしれない(高DPI環境でscaleを大きくする機能がcreate-imageに備わっている)。

(image :type jpeg :file "/home/hoge/tmp/karasawa.jpg" :scale 1)

この時点ではまだ画像は読み込まれない。

次のようにすると実際に画像が表示される。

(insert-image (create-image (expand-file-name "~/tmp/karasawa.jpg")))

(2021-10-11追記: create-imageに引き渡すファイル名は絶対パスに変換する必要がある。相対パスを指定するとdata-directoryやx-bitmap-file-pathの中から画像を探すことになり、設定によっては画像が読み込まれない)

画像ファイルを表示
図1: 画像ファイルを表示

関数insert-imageは適当な文字列(" ")をバッファに挿入し、その文字列がある範囲のdisplayテキストプロパティにImage Descriptorを設定する。

なので、次のようにしても同じ結果が得られる。

(progn
  (insert " ")
  (put-text-property (1- (point)) (point) 'display (create-image (expand-file-name "~/tmp/karasawa.jpg"))))

適当な画像を用意するのが面倒ならばSVG画像を作る方法もある。次のコードを評価するとSVGデータを含んだImage Descriptorが返ってくる。

(require 'svg)
(let ((svg (svg-create 400 300)))
  (svg-rectangle svg 0 0 400 300 :fill "#69f")
  (svg-circle svg 200 150 100 :fill "#eee")
  (svg-image svg))
(image :type svg :data "<svg width=\"400\" height=\"300\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"> <rect width=\"400\" height=\"300\" x=\"0\" y=\"0\" fill=\"#69f\"></rect> <circle cx=\"200\" cy=\"150\" r=\"100\" fill=\"#eee\"></circle></svg>" :scale 1)

実際に表示させてみる。

(require 'svg)
(insert-image
 (let ((svg (svg-create 400 300)))
   (svg-rectangle svg 0 0 400 300 :fill "#69f")
   (svg-circle svg 200 150 100 :fill "#eee")
   (svg-image svg)))
SVG画像を表示した結果
図2: SVG画像を表示した結果

画像の変換

表示する際に簡単な画像処理も可能。

(insert-image (create-image (expand-file-name "~/tmp/karasawa.jpg") 'jpeg nil :width 300 :rotation 90 :relief -10))
縮小、回転、レリーフ
図3: 縮小、回転、レリーフ
(insert-image (create-image (expand-file-name "~/tmp/karasawa.jpg") 'jpeg nil :width 400 :conversion 'disabled))
disabled効果
図4: disabled効果

これらの変形・効果はSVG画像にも適用できる。おそらくSVGをレンダリングした後のビットマップに適用している。

この他にも41.17.2 Image Descriptorsにはいくつかのプロパティが説明されている。また、最終的な表示のされ方はsliceなど他のdisplayプロパティの影響も受ける。

GIFアニメーションの表示も可能。(see:41.17.10 Multi-Frame Images)

画像のクリック

画像がクリックされたときに何か処理を行うこともできる。

(require 'svg)

(let (;;適当な文字列("A")を追加しその範囲をstart,endとする。
      (start (point))
      (end (progn (insert "A") (point))))

  ;; displayプロパティにSVG画像を設定する。
  (put-text-property
   start end 'display
   (let ((svg (svg-create 200 40)))
     (svg-rectangle svg 0 0 200 40 :rx 10 :ry 10 :stroke "#888" :fill "#fff")
     (svg-text svg "Button" :x 100 :y 30 :font-size 35 :text-anchor "middle")
     (svg-image svg)))

  ;; keymapプロパティにマウスクリックの際に呼ばれる関数を設定する。
  (put-text-property
   start end 'keymap
   (let ((km (make-sparse-keymap)))
     (define-key km [mouse-1]
       (lambda (event)
         (interactive "e")
         ;;(message "(x . y)=%s" (posn-object-x-y (event-start event)))
         (message "clicked event=%s" event)))
     km))

  ;; pointerプロパティをhandにすることでマウスカーソルの形を変える。
  (put-text-property
   start end 'pointer 'hand))
画像を表示してクリックした結果
図5: 画像を表示してクリックした結果

マウスの左クリックは[mouse-1]をキーマップに設定することで検出できる。テキストプロパティのkeymapプロパティを設定することで、画像がある場所にだけ効果があるキーマップを設定できる。

クリックした位置などイベントの詳細情報はInteractive Codeの"e"か、 last-input-event で受け取れる。

イベントの詳細情報はリストの形で記録されている。(22.7.4 Click Events)

そこから画像内でのクリックされた位置を取り出すには次のようにする。(22.7.15 Accessing Mouse Events)

(let ((xy (posn-object-x-y (event-start event))))
  (format "x=%s y=%s" (car xy) (cdr xy)))

画像の一部をクリック

画像内の一部がクリックされたことを判定したい場合イベントの座標から割り出しても良いがImage Descriptor:mapプロパティを使うこともできる。

(require 'svg)

(let (;;適当な文字列("A")を追加しその範囲をstart,endとする。
      (start (point))
      (end (progn (insert "A") (point))))

  ;; displayプロパティにSVG画像を設定する。
  (put-text-property
   start end 'display
   (let ((svg (svg-create 400 300)))
     (svg-circle svg 100 150 30 :fill "red")
     (svg-circle svg 200 150 30 :fill "blue")
     (svg-circle svg 300 150 30 :fill "green")
     ;; SVG画像を作る。同時にImage Descriptorに:mapプロパティを付加する。
     (svg-image svg :map '(((circle . ((100 . 150) . 30)) red-area (pointer hand help-echo "red"))
                           ((circle . ((200 . 150) . 30)) blue-area (pointer hand help-echo "blue"))
                           ((circle . ((300 . 150) . 30)) green-area (pointer hand help-echo "green"))))))

  ;; keymapプロパティにマウスクリックの際に呼ばれる関数を設定する。
  (put-text-property
   start end 'keymap
   (let ((km (make-sparse-keymap)))
     (define-key km [red-area mouse-1] (lambda () (interactive) (message "red!")))
     (define-key km [blue-area mouse-1] (lambda () (interactive) (message "blue!")))
     (define-key km [green-area mouse-1] (lambda () (interactive) (message "green!")))
     km)))
mapプロパティを伴う画像をクリックした結果
図6: mapプロパティを伴う画像をクリックした結果

矩形、円、任意の多角形で当たり判定の領域を設定できる。

画像の自動スケーリング

関数 create-image には高解像度モニターに備えて画像を自動的に拡大する機能がある。

拡大率は変数 image-scaling-factor で設定できる。デフォルトは 'auto になっており、ウィンドウサイズとフォントサイズの比率によって計算する。

具体的な計算は関数 image-compute-scaling-factor で行われている。まず、ウィンドウのピクセル幅を横方向の文字数(桁数)で割った値を計算する(つまり、平均文字幅ピクセル数)。その値が10以上の時、10からどのくらい大きくなったかの比率が画像の拡大率となる。そうして計算された値がcreate-imageが返すImage Descriptorの:scaleプロパティに付加される。

create-imageのPROPS引数に :scale プロパティが指定されている場合はこの処理は行われない。

注意すべき点は、この拡大率はマウスイベントの座標には一切影響を及ぼさないということ。イベントから得られる座標はこの拡大率の影響を受けないフレーム上の純粋な座標となる。また、:mapプロパティもこの拡大率の影響を受けて調整されることは ない 。従ってこの自動スケーリングが機能してしまうと、座標や領域の位置が画像と食い違ってしまう。これが問題になる場合、 (image-compute-scaling-factor image-scaling-factor) の計算値を使ってマウス座標や:mapプロパティを変換する必要がある。

2020-10-27

2020年秋の新番組

ウィルス騒ぎの影響から少し戻ってきた感じがありますね。

09/25(金) 17:20~ NHK Eテレ かいじゅうステップ ワンダバダ 第2シリーズ
09/25(金) 19:00~ TOKYO MX 魔神英雄伝ワタル 七魂の龍神丸 1~4話イッキ見SP
10/–(-) ~ YouTube おねがいっパトロンさま!
10/01(木) 23:30~ TOKYO MX ひぐらしのなく頃に 新アニメ
10/01(木) 25:28~ TBS アサルトリリィ Bouquet
10/02(金) 21:54~ TOKYO MX 兄に付ける薬はない! 第4期
10/02(金) 22:00~ TOKYO MX 100万の命の上に俺は立っている
10/02(金) 22:30~ TOKYO MX 魔女の旅々
× 10/02(金) 24:00~ TOKYO MX ヒプノシスマイク -Division Rap Battle- Rhyme Anima
10/02(金) 24:30~ TOKYO MX ダンジョンに出会いを求めるのは間違っているだろうかIII
10/02(金) 25:00~ TOKYO MX レヱル・ロマネスク
10/02(金) 25:05~ TOKYO MX トニカクカワイイ
10/02(金) 25:23~ テレビ東京 キングスレイド-意志を継ぐものたち-
10/02(金) 25:25~ TBS系 呪術廻戦
10/02(金) 26:25~ TBS ハイキュー!! TO THE TOP(第4期)
10/02(金) ~ Amazon PV BURN THE WITCH
× 10/03(土) 05:30~ TBS 犬と猫どっちも飼ってると毎日たのしい
10/03(土) 09:30~ テレビ東京系 ドラゴンクエスト ダイの大冒険
10/03(土) 17:30~ 日本テレビ系 半妖の夜叉姫
10/03(土) 22:30~ TOKYO MX ラブライブ!虹ヶ咲学園スクールアイドル同好会
× 10/03(土) 23:30~ TOKYO MX 戦翼のシグルドリーヴァ
10/03(土) 24:30~ TOKYO MX 魔法科高校の劣等生 -来訪者編-(第2期)
10/03(土) 25:30~ TOKYO MX ギャルと恐竜
10/03(土) 26:00~ テレビ朝日系 いわかける!- Sport Climbing Girls -
10/04(日) 21:55~ BS12 かえるのピクルス
10/04(日) 22:00~ TOKYO MX 無能なナナ
10/04(日) 23:00~ TOKYO MX アイドリッシュセブン Second BEAT!
10/04(日) 23:30~ TOKYO MX 神達に拾われた男
10/04(日) 24:30~ TOKYO MX まえせつ!
× 10/04(日) 25:00~ TOKYO MX 大人にゃ恋の仕方がわからねぇ!
× 10/04(日) 25:05~ TOKYO MX エタニティ ~深夜の濡恋ちゃんねる~
× 10/04(日) 25:20~ TOKYO MX 秘密結社 鷹の爪 ~ゴールデン・スペル~
10/05(月) 23:00~ TOKYO MX ゴールデンカムイ 第三期
10/05(月) 25:05~ TOKYO MX One Room サードシーズン
10/05(月) 26:00~ テレビ東京 魔王城でおやすみ
10/06(火) 24:30~ TOKYO MX 池袋ウエストゲートパーク
10/07(水) 22:30~ TOKYO MX ツキウタ。 THE ANIMATION2
10/07(水) 23:30~ TOKYO MX くま クマ 熊 ベアー
10/07(水) 23:54~ BS11 せいぜいがんばれ!魔法少女くるみ 第3期
10/07(水) 24:00~ TOKYO MX NOBLESSE -ノブレス-
10/07(水) 25:05~ TOKYO MX 第501統合戦闘航空団 ストライクウィッチーズ ROAD to BERLIN
× 10/07(水) 25:35~ TOKYO MX キミと僕の最後の戦場、あるいは世界が始まる聖戦
10/08(木) 21:54~ TOKYO MX ぐらぶるっ!
10/08(木) 24:30~ TOKYO MX アクダマドライブ
10/08(木) 25:58~ TBS 安達としまむら
10/09(金) 07:30~ テレビ東京系 カピバラさん
× 10/09(金) 19:30~ TOKYO MX 最響カミズモード!
10/10(土) 22:00~ TOKYO MX ご注文はうさぎですか? BLOOM (第3期)
10/10(土) 24:00~ TOKYO MX 神様になった日
10/10(土) 25:30~ テレビ朝日系 体操ザムライ
10/11(日) 22:30~ TOKYO MX 憂国のモリアーティ
10/11(日) 24:30~ TOKYO MX まえせつ!
× 10/12(月) 22:30~ TOKYO MX おちこぼれフルーツタルト
10/12(月) 24:00~ TOKYO MX A3!(エースリー) -AUTUMN & WINTER-
10/12(月) 25:30~ テレビ東京 おそ松さん 第3期
10/12(月) 26:30~ テレビ東京 それだけがネック
× 10/13(火) 23:00~ TOKYO MX 禍つヴァールハイト
10/25(日) 20:30~ AT-X 刀使ノ巫女 刻みし一閃の燈火(前後編)
2020-09-06

2020年夏の新番組

もう9月になってしまったけれど。

07/01(月) 17:59~ TOKYO MX 耐え子の日常 新シリーズ
07/02(木) 23:30~ TOKYO MX HERO MASK
07/03(金) 22:00~ TOKYO MX 天晴爛漫
07/03(金) 24:00~ TOKYO MX ド級編隊エグゼロス
07/03(金) 24:30~ TOKYO MX 食戟のソーマ 豪の皿
× 07/03(金) 25:53~ テレビ東京 アラド:逆転の輪
07/03(金) 25:55~ TBS系 炎炎ノ消防隊 弐ノ章
○- 07/04(土) 22:00~ TOKYO MX Lapis Re:LiGHTs~この世界のアイドルは魔法が使える~
○- 07/04(土) 23:30~ TOKYO MX 魔王学院の不適合者
07/05(日) 25:00~ TOKYO MX 巨人族の花嫁
07/05(日) 25:35~ テレビ東京 八月のシンデレラナイン Re:fine
07/06(月) 21:55~ フジテレビ 超普通都市カシワ伝説R
07/06(月) 24:00~ TOKYO MX THE GOD OF HIGH SCHOOL -ゴッド・オブ・ハイスクール-
07/07(火) 19:00~ Animax ムヒョとロージーの魔法律相談事務所 第2期
× 07/07(火) 21:54~ TOKYO MX うまよん
07/07(火) 24:28~ BS日テレ 戦乙女の食卓
07/07(火) 24:30~ BSフジ 異常生物見聞録
07/07(火) 24:30~ TOKYO MX 放課後ていぼう日誌
× 07/08(水) 23:30~ TOKYO MX Re:ゼロから始める異世界生活 第2期
07/08(水) 24:55~ フジテレビ GREAT PRETENDER
07/08(水) 25:05~ TOKYO MX デカダンス
07/09(木) 25:28~ TBS ノー・ガンズ・ライフ 第2期
07/09(木) 25:58~ TBS やはり俺の青春ラブコメはまちがっている。完
07/09(木) ~ Netflix 日本沈没2020
07/10(金) 22:30~ TOKYO MX 宇崎ちゃんは遊びたい!
07/10(金) 25:25~ TBS系 彼女、お借りします
07/10(金) 25:35~ TOKYO MX ピーター・グリルと賢者の時間
× 07/10(金) 25:50~ TBS系 GETUP! GETLIVE! ショートアニメ
07/11(土) 24:00~ TOKYO MX ソードアート・オンライン-アリシゼーション- War of Underworld -THE LAST SEASON-
07/12(日) 22:30~ TOKYO MX A.I.C.O. -Incarnation-
07/12(日) 23:00~ TOKYO MX モンスター娘のお医者さん
07/12(日) 27:05 ~ テレビ東京 忍者コレクション
07/15(水) 24:00~ TOKYO MX 恋とプロデューサー~EVOL×LOVE~
07/15(水) 22:00~ TOKYO MX GIBIATE the Animation
07/16(木) 24:55~ フジテレビ 富豪刑事 Balance:UNLIMITED

いくつか良さそうなのもあったのだけどその後気に入らなくてガンガン切っていったら現在10本くらい。宇崎ちゃんとかはもう少し我慢して見れば良かったかなぁ。

今のところ一番楽しみにしているのは放課後ていぼう日誌。導入は違和感あったけれど3話~4話くらいから面白さが分かってきた。