Quantcast
Channel: OPTPiX Labs Blog
Viewing all 263 articles
Browse latest View live

DXTC(S3TC)圧縮のアルゴリズムとは?~前編~

$
0
0

初めまして、R&D部 上田と申します。
「画像変換の知恵袋」には初登場です。今回から、よく使われるいくつかの画像圧縮アルゴリズムについて解説していきたいと思います。
第一回目はDXTC(S3TC)圧縮について、です。

DXTCって何?

DXTCとは画像圧縮アルゴリズムの一種で、DirectX Texture Compressionの略称です。
DXTCを使うと画像容量を小さくすることができます。ただし、非可逆圧縮なので画像が劣化します。
また、S3 Graphics社の開発した技術なので、S3TC(=S3 Texture Compression)とも呼ばれています。

DXTCってどんなところで使われているの?

NINTENDO GAMECUBE®、Wii®、Wii U™、PSP®、PS3®、Xbox®、Xbox360®などの家庭用ゲーム機や、Windows PCのほとんどのグラフィックスカード、さらにNvidia Tegra搭載のスマートフォン・タブレットなどで、3Dグラフィックスを描画する際の圧縮フォーマットとして使われています。

なんでDXTCを使うの?(JPEGじゃダメなの?)

JPEGやPNGに比べるとDXTCの圧縮率は低いです。その代わり、DXTCは画像を圧縮したままGPUのVRAMに置いて使うことができる、という大きな利点があります。

これに対してJPEGなどでは、画像を無圧縮状態に展開しなければGPUで扱えないため、GPUを使って3Dグラフィックスのテクスチャを表示する場合、DXTCに比べてVRAM容量やメモリバンド幅(転送速度)を多く使ってしまいます。

DXTCの圧縮品質はどんな感じ?

DXTCを使って画像を圧縮してみましょう。

サンプルとして、24ビットRGBカラーで画像サイズが640×480の画像を用意しました。
それぞれの画像容量は、無圧縮で 921,600 バイト(=640×480×3)です。

flower_quarter cat_quarter renga_quarter
無圧縮の元画像(各 921,600 バイト)
(クリックで等倍表示)

DXTCで圧縮します。

flower_dxt1_quarter cat_dxt1_quarter renga_dxt1_quarter
DXTC圧縮後の画像(各 153,600 バイト)
(クリックで等倍表示)
 

圧縮するとそれぞれの画像容量は 153,600 バイト となり、圧縮前の6分の1になります。
DXTCは圧縮すると画像が劣化すると書きましたが、圧縮前後の画像を比較していただくとわかるように、劣化はかなり注意して見ないとわかりません。
注)ブログ内の画像は、ブラウザでそのまま表示できるように全てPNG形式に変換しているため、上記画像容量とは異なります。以下の画像についても同様です。

DXTCには苦手な画像がある?

今度は次のような画像をDXTCで圧縮してみましょう。上の3つの画像とは違ってアニメ調の画像です。

magicgirl_org

無圧縮の魔法少女イラスト<br />(クリックで3倍表示)

magicgirl_dxt1_org

DXTC圧縮後の魔法少女イラスト<br />(クリックで3倍表示)

 
 

パッと見た感じは綺麗に圧縮できている気がするんですが……、

キャラクターの輪郭あたりをよく見てみると(この辺 magicgirl_dxt1_upscale_half とか)、色がにじんでしまっているのがわかると思います。

実はDXTCにはアニメ調の画像を圧縮すると画像が劣化しやすい、という特徴があります。

写真のような画像よりもアニメ調の画像が劣化しやすいのは、DXTCの仕組みに原因があります。

DXTCってどんな仕組み?

それではDXTC圧縮の仕組みを説明しましょう。
圧縮前の画像として上で表示した画像(花)を使います。

flower_quarter

(クリックで等倍表示)
 

24ビット RGBカラーで画像サイズが640×480、画像容量は 921,600 バイト の画像です。

1.4×4ピクセルのブロックに分割する

DXTCでは、4×4ピクセルを1つのブロックとして、ブロック毎にそれぞれ独立して圧縮処理を行います。

upscale_half
画像を拡大して1ブロック(4×4ピクセル)取り出す

2.代表色を2色選び、それらの中間色を2色つくる

さて、この1ブロックのそれぞれのピクセルには、それぞれ別の色が入っています。こうやって拡大すると色の違いが目に見える形になるのですが、拡大前の画像を見てこのピクセル毎の色の違いが分かるような人はほとんどいないでしょう。

DXTC圧縮ではこの1ブロック(16ピクセル)を4色で表現します。
まずブロックの代表色となる2色を16ビットのRGBカラー(RGB565)から選び、この2色から中間色となる2色を作ります。

select_color_half
代表色と中間色

中間色を生成するのは情報量を減らすためで、2色分の情報量(16ビット × 2色 = 32ビット)で4色を表現することができます。

このとき、代表色C0>C1となるように代表色を選んでいます。
(C0≦C1とした場合については後で説明します)

この4色をどのように選ぶか、というアルゴリズムについてはDXTCでは規定されておらず、圧縮ツールによって色々な手法が採られています。OPTPiX imésta では、独自のアルゴリズムを用い、より高画質な圧縮を行うことができます。

3.代表色と中間色を使って、ブロック内の色を置き換える

こうしてできた4色を使ってブロックの色を置き換えていきます。

color_replace_half
代表色と中間色を使って色を置き換える

各ピクセルの色が変化しているのが分かるでしょうか?
ブロック内の全てのピクセルがC0~C3のどれかの色に置き換わっています。

4.ブロック内のピクセルにインデックスを付ける

そして最後に4色のうちどの色を使ったのかを各ピクセル毎にインデックス情報として記憶していきます。
4色が区別できればいいので、2ビット(00,01,10,11)で表現できます。

index_half
各色に対してインデックスを付ける

5.完成!

以上1~4の処理を行うことで、各ブロック毎に代表色2色(16ビット RGBカラー)と各ピクセルに付けたインデックスを記憶しておけば、そのブロックを再現できるということになります。

conclusion_half
圧縮終了!

これにより、圧縮前の画像では1ブロックを表現するために、
(16ピクセル)×(24ビット RGBカラー) = 384 ビット = 48 バイト
が必要だったのに対し、

圧縮後の画像では1ブロックを表現するために、
(16ピクセル)×(2ビット のインデックス)+
     (16ビット RGBカラー)×(2色) = 64 ビット = 8 バイト
だけで大丈夫になりました。

全体の画像容量も 921,600 バイト → 153,600 バイト(6分の1) となります。

結局、なんでアニメ調の画像が苦手なの?

ということになりますね。
例えば、次のような1ブロックにDXTC圧縮をするとしましょう。

block_quarter
DXTC「苦手なヤツです」

このとき、どのような代表色2色を選べばいいのでしょう?
とりあえず、ブロック内に黒色・水色・茶色の3色があるのでそれらから代表色2色を選んで中間色を作ってみます。

color_pair_half
代表色を選んで中間色を作ってみる

どの2色を選んだとしても、その2色の中間色ではもう1つの色を作りだせないですね。
実は、この1ブロックの画像をDXTC圧縮すると、こんな画像ができます。

block_dxt1_quarter
DXTC「苦手だって言ったのに…」

このように、DXTCでは代表色とその中間色を使ってブロック内の色を置換するので、ブロック内の色によっては置換がうまくできない場合があります。
そして、写真のような画像よりもアニメ調の画像の方がこのようなブロックが多いのです(特に輪郭線を黒で描いている部分など)。

結果としてDXTCはアニメ調の画像が苦手ということになります。

C0≦C1だった場合について

DXTCの仕組み 手順2で「C0≦C1」とした場合についてですが、このように代表色を選ぶと中間色の計算方法が変わります。

dxt1_alpha_half
C0とC1の大小による中間色の変化

中間色が1色になるかわりに、透過色(α=0)が使えるようになります。

さらに続く…

今回の解説は、実はDXTCの中のDXT1と呼ばれる規格で、DXTCにはDXT1の他に DXT2~DXT5までの合計5つの規格があります。

次回はDXT2~DXT5の解説記事を書いていきたいと思います。


田中圭一のゲームっぽい日常 最近のアニメやラノベの傾向

$
0
0
田中圭一のゲームっぽい日常 最近のアニメやラノベの傾向

ここ1年ほどでヒットしたアニメやラノベはたくさんありますが、ヒット作になり得た「要素」はといえば、それほどバリエーションは多くありません。

「魔法少女、または魔界のヒロイン」「私たち、アイドルになりたい!またはバンドをやりたい!」「現世ではパッとしないボクでも、異世界・バーチャル世界では英雄」「普段はパッとしないけど、じつはこのボクにだけ、秘められた力が・・」「妹がオレにぞっこん」・・・と、こんな感じです。

そうなると、ヒット間違いなしのラノベかアニメのタイトルは「4人の妹が、じつは魔界から来た姫たちで、4人ともオレにラブラブで、4人はバンドを組んで現世では人気アイドルで、そんな時オレは、秘められた力が覚醒してしまい、魔界で大活躍」・・ということになるかと思います。

究極の全部乗せ。どうでしょう?

 

※ラノベ……「ライトノベル」の略で、アニメ調・マンガ調のイラストが表紙になっている若年層向けの読みやすい小説が一般的にこう呼ばれています。

GlobalGameJamの次は、「Fuse04」!

$
0
0

 ゲーム業界、というよりはゲームを取り巻く環境がどんどん変わっていくなぁ、と最近常々。ソリューション営業部改め「セールス・コミュニケーション部」の浅井でございます。ああ、もぅ。

 ああ、もぅ。と嘆息するのは先日協賛表明した「GlobalGameJam」ことGGJが無事に終了してから早1ヶ月が経過している・・・という事実。弊社の「SpriteStudio」も無事にいくつかのチームでご利用いただけまして、『かんたんにアニメーションが組めた!』と大変ご好評いただくことが出来ました。

DancingFairy_title

『Dancing Fairy in The Bald Head』@北陸先端科学技術大学院大学(JAIST)

 イロイロなチームでSpriteStudioをご利用いただいたのですが、一際嬉しかったのは、SpriteStudioにサンプルで入っている我らが「こみぽちゃん」をカスタマイズして使っていただいたこちらの作品。チームメンバーの方いわく、大変好評だったとか!

 ・・・ご好評・・・いただいたものの返す返すも無念だったのは左様、ご提供出来たのが生憎現行製品のSpriteStudioだったこと。そーなんです、先日プレスリリースいたしました新しいSpriteStudioこと『OPTPiX SpriteStudio』は現在、発売前のβ版をやっとこ提供開始、というステータス……。 なので残念、GlobalGameJamには間に合わず!

Fuse04_logo

 なのですが、3月にIGDA東北支部主催で開催されるFuse04」(なんと6時間でゲームを完成させるという大変過酷なイベント)、こちらに『OPTPiX SpriteStudio』のβ版をご提供させていただくことに!

 この『OPTPiX SpriteStudio』の素敵さ具合については製品ページをご覧頂くとして、どんな手法に使われるか皆目検討がつかない、かつGlobalGameJamよりも更に短い時間でゲームを作り上げるこのイベントで、果たしてできたてホヤホヤの『OPTPiX SpriteStudio』がお役に立つことができるのか!? 果たして!? と、ちょっと心配。

 ともあれFuse04参加者の皆様におかれましては、是非ご活用くださいませ。

 » Fuse04イベント参加お申込みページ

 ※『OPTPiX SpriteStudio』は各種ゲームイベント向けに積極的に協賛していきたいと考えております。イベントをご計画の折にはお問い合わせください。

DXTC(S3TC)圧縮のアルゴリズムとは?~後編~

$
0
0

R&D部 上田です。
DXTC(S3TC)圧縮のアルゴリズム解説、後編です。前回のエントリーでは「DXTCとは何か?」「どんなところで使われているのか?」といった基本的な解説と、DXT1のアルゴリズムをご説明しました。今回はDXT2~DXT5のアルゴリズムについて解説していきます。

DXT2~5って何?

DXTC(S3TC)圧縮には、DXT1~5の5種類のアルゴリズムが存在します。
前回解説したのはDXT1のアルゴリズムです。

何が違うの?

DXT1はアルファ(α,alpha)チャネル付き画像(32bit ARGBカラー)に対応していませんでした(完全透明色=抜き色のみ対応)。これに対して、DXT2~5はアルファチャネル付き画像に対応しています。

その代わり、DXT1が24bit RGBカラー画像を圧縮すると圧縮率が1/6 (4×4ブロック=16ピクセルあたり8バイト)となるのに対して、
DXT2~DXT5は32bit ARGBカラー画像を圧縮すると圧縮率が1/4 (4×4ブロック=16ピクセルあたり16バイト)になります。

DXT2~5のアルゴリズム

DXT2~5で共通する基本原理

まず、4×4ピクセルを1ブロックとして扱うことは、DXT1~5の全てで同じです。

さて、改めて説明するまでもないことですが、32bit ARGBカラー画像というのは、24bit RGBカラー画像に8bitのアルファ成分を加えた画像です。

ARGBimage_half
32bit ARGBカラー画像はRGB成分とアルファ成分でできている

まず、24bit RGBカラー画像部分については、DXT1と同じ圧縮するアルゴリズムを使用します。

DXT1_half
DXT1で24bit RGBカラー画像を圧縮できる

32bit ARGBカラー画像の圧縮では、RGB成分はDXT1と同じ手法で圧縮し、アルファ成分のみを1ブロック64bit として圧縮します。

DXT2to5_half
DXT2~5では、RGB成分はDXT1で圧縮し、アルファ成分は別に圧縮する

このアルファ成分の圧縮アルゴリズムが4種類あり、それらがDXT2~5となります。

DXT3のアルゴリズム

まずDXT2は飛ばして、先にDXT3のアルゴリズムから解説します(その方が都合が良いので…)。

一つ前の図で、DXT2~5ではアルファ成分を1ブロックあたり64bitに圧縮する、と書きました。DXT3ではこの64bitを単純にアルファ成分の表現に使います。まず圧縮前と圧縮後について、1ピクセルあたりのビット数を比べると以下のようになります。

DXT3_alpha_half
DXT3での1ピクセルあたりの階調数は1/16になる

圧縮前後で1ピクセルあたりのビット数が半分になるのに対し、そのビット数で表現できる階調数は1/16になります。表現できる階調数が256階調から16階調になる、ということをグラフにしてみると以下のようになります。

DXT3_graph_half
256階調(圧縮前)と16階調(圧縮後)の比較

グラフにすると表現力が大幅に落ちているのが良く分かりますね。

DXT3の実際の処理としては、圧縮前のアルファ値が0~8なら圧縮後は0、9~25なら17、26~42なら34、…というようにアルファ値を置き換えていきます。

DXT3_example_half
DXT3でアルファ成分を圧縮するとこうなる

上の図を見るとわかるように、DXT3のアルゴリズムではアルファ値の細かい変化は圧縮後に反映されず、アルファ値が滑らかに変化していくような画像をDXT3で圧縮すると、劣化が激しくなってしまいます。

DXT5のアルゴリズム

今度はDXT4を飛ばしてDXT5のアルゴリズムを解説します。

DXT5ではDXT3の欠点を克服するようにアルゴリズムが修正されています。まず、DXT1と同じように代表的な8bitアルファ値を二つ決め、それらから補間したアルファ値を作り、合計8個のアルファ値を表現します。

DXT5_alpha_half
DXTC5ではアルファ値を2個取り出し、それらから補間アルファ値を作る

また、ここでもDXT1と同様に「代表アルファ値二つの大小関係」によってアルファ値の表現方法が異なる点に注目してください。
具体的には、上の図で示したように、アルファ値8個の内容は

  • 代表アルファ値二つの間を8階調で表現する
  • 代表アルファ値二つの間を6階調で表現し、更に「完全透明(0)」「完全不透明(255)」の二つの値を持つ

のいずれかになります。
後者は、半透明を使った文字やアイコンで、周囲を完全透明にしているような場合に有用です。

そしてこれら8個のアルファ値について、ブロック内の各ピクセルにインデックスを付けます。
今回は8個の値があるので、各ピクセル3bit使います。こうすることで、64bitに圧縮できます。

DXT5_bit_half
DXT5圧縮後のアルファ成分は、2個のアルファ値と3bitのインデックスになる

このように補間したアルファ値を用いることで、DXT3よりも圧縮後のアルファ値の表現力を向上することが可能になります。

DXT5_graph_half
DXT5では256階調を任意の範囲で8階調にする

ただし、上図で示したようにアルファ値の範囲が広くなると階調表現力が下がってしまうので、使用する場面を選ぶ必要があります。例えば、アルファ値の範囲が0~255という一番極端な場合では、DXT3よりもDXT5のほうが階調表現が劣ることになります。

DXT2とDXT4のアルゴリズム

飛ばしていたDXT2とDXT4についてですが、基本的なアルゴリズムはDXT2がDXT3と、DXT4がDXT5と同じです。違う点は、DXT2とDXT4ではRGB値にアルファ値が予め乗算されて格納されていることです。これを乗算済みアルファと言います。乗算済みアルファについては、3D描画の領域の話題になりますので、興味のある方は「乗算済みアルファ」で検索してみてください。

DXTC 1~5 特徴のまとめ

DXTC 1~5の特徴
名前 RGBチャネル アルファチャネル 圧縮率 備考
DXT1 代表2値+補間2値 なし 1/6 元画像αなし
DXT1 代表2値+補間1値 1bit 1/8 元画像αあり
DXT2 代表2値+補間2値 4bit 1/4 乗算済α
DXT3 代表2値+補間2値 4bit 1/4  
DXT4 代表2値+補間2値 代表2値+補間6値  または
0+代表2値+補間4値+255
1/4 乗算済α
DXT5 代表2値+補間2値 代表2値+補間6値  または
0+代表2値+補間4値+255
1/4  

 

「PNG24高圧縮化加工」でFacebookのカバー写真を綺麗なままコンパクトに!

$
0
0

Web担当の嶋です。
昨年末に「Facebookにキレイなカバー写真を投稿するポイント」というエントリーを公開しました。その時の結論としては「256色のPNGにしてアップすると、ロゴもくっきりするしファイルサイズも小さくなる」というものでした。

しかし、アニメ調などベタ塗りの箇所が比較的多いカバー写真であればいいのですが、写真やグラデーションを多用した画像を減色すると、物によっては階調不足が気になる場合もあります。

今回はOPTPiX imesta 7 for Mobile & Socialに試験的に搭載されている機能「PNG24高圧縮化加工」を使って、24bitの画像をFacebookのカバー写真にする、というのを試してみたいと思います。

256色でも充分きれい、でも画質にこだわるならやはり24bitでアップしたい

早速ですがFacebookページのサンプルとして結婚式場をイメージし、写真を中心に構成したサンプル画像を作ってみました。まずはオリジナルのPNGをOPTPiX imesta 7 for Mobile & Socialを使って256色に減色してみましょう。

PNG(24bit) PNG256色
Facebookカバー写真(PNG24bit)  483KB Facebookカバー写真(PNG256色) 305KB

256色に減色した画像でも充分なクオリティが得られていると言っていいと思います。しかし、細かいところを見てみると、違いが見える箇所があります。画像の一部を切り出して比較してみましたのでご覧ください。(画像をクリックすると3倍に引き伸ばされた画像が表示されます)

256色と24bitの比較その1 256色と24bitの比較その2

色数が少なくなる分、24bitに比べてグラデーションの階調が段階的になっています。拡大しないと分からないレベルですから、一般的なサイトであれば問題はないかと思いますが、ラグジュアリー商品を扱う企業のサイトやモデルさんの個人ページなど、画質がその商品やサービスのクオリティへのイメージに与える影響が大きい場合は、やはり「キレイな看板」にこだわりたいものです。

 

フルカラーならJPEG、でもFacebookにアップロードすると再圧縮されてしまう

ここで前回の「Facebookにキレイなカバー写真を投稿するポイント」というエントリーの復習を。Facebookでは、JPEG画像をアップした際にサーバーで再度圧縮処理が行われるため、文字やロゴの周りにモスキートノイズが発生しがちです。ノイズが発生した画像は、前のエントリーに拡大画像などを掲載していますのでそちらをご覧ください。

 

PNGのファイルサイズを24bitのまま小さくする「PNG24高圧縮化加工」

人間の目の特徴として、変化を識別しやすい色と、しにくい色が存在しています。この特性を利用し、フルカラー画像の「見た目」がほとんど変わらないように、画像(ピクセル)の色や並びを加工し、PNG圧縮の圧縮効率を上げる技術が、「PNG24高圧縮化加工」です。現在OPTPiX imesta 7 for Mobile & Socialに「OPTPiX Labs」として試験的に搭載されています。

PNG24高圧縮化加工 ダイアログ

操作としては至ってシンプル。加工したい画像を開いた状態でツールバーから [イメージ] → [PNG24高圧縮化加工] を選択し、フィルタの強さを選ぶだけです。フィルタを強くかければファイルサイズをよりコンパクトにできますが、画質に影響する場合があります。また、元画像によっては圧縮の効果があまり出ない場合もあります。

 

24bitのPNGの方が256色のPNGよりもファイルサイズが小さくなった!

このPNG24高圧縮化加工で処理した画像をFacebookのカバー写真に利用してみました。フィルタはデフォルトの「中」でかけています。

元画像(PNG24bit) アップロード後
Facebookカバー写真(PNG24bit)  483KB Facebookカバー写真(PNG24bit・アップロード後) 456KB
(前回と異なり、サーバーでの変換時に若干容量が減少)
元画像(PNG256色) アップロード後
Facebookカバー写真(PNG256色) 140KB Facebookカバー写真(PNG256色・アップロード後) 379KB
(アップロード時に24bitに変換されてしまうため容量が増加)
元画像(PNG24高圧縮化加工) アップロード後
Facebookカバー写真(PNG24bit高圧縮化加工)  305KB Facebookカバー写真(PNG24高圧縮化加工・アップロード後) 343KB
(元画像より容量が増えたものの、256色をアップロードした時よりも小さく)

なんとPNG24高圧縮化加工したPNGの方が256色のPNGをアップするよりもファイルサイズが小さくなりました。

コミPo! のFacebookページのカバー写真と、ウェブテクノロジのFacebookページのカバー写真でも試してみることにします。ちなみにウェブテクノロジのFacebookページのカバー写真、OPTPiX SpriteStudioのロゴが変わったのに気づかれましたでしょうか?

ウェブテクノロジFacebookページのカバー写真でのテスト

元画像(PNG24bit) アップロード後
Facebookカバー写真(PNG24bit)  175KB Facebookカバー写真(PNG24bit・アップロード後) 204KB
元画像(PNG256色) アップロード後
Facebookカバー写真(PNG256色) 111KB Facebookカバー写真(PNG256色・アップロード後) 248KB
元画像(PNG24高圧縮化加工) アップロード後
Facebookカバー写真(PNG24bit高圧縮化加工)  149KB Facebookカバー写真(PNG24高圧縮化加工・アップロード後) 181KB

こちらもPNG24bit高圧縮化加工を施した画像のほうが256色のPNGをアップするよりも小さくなりました。ロゴ中心でグラデーションが少ないからか、PNG24bitの画像をそのままアップしたほうが、256色のPNGをアップするよりもファイルサイズが小さくなっています。

 

コミPo! 公式Facebookページのカバー写真

元画像(PNG24bit) アップロード後
Facebookカバー写真(PNG24bit)  394KB Facebookカバー写真(PNG24bit・アップロード後) 415KB
元画像(PNG256色) アップロード後
Facebookカバー写真(PNG256色) 114KB Facebookカバー写真(PNG256色・アップロード後) 312KB
元画像(PNG24高圧縮化加工) アップロード後
Facebookカバー写真(PNG24bit高圧縮化加工)  261KB Facebookカバー写真(PNG24高圧縮化加工・アップロード後) 328KB

こちらは逆にPNG24高圧縮化加工を施したPNGをアップしたほうが256色PNGをアップするよりもファイルサイズが大きくなりました。ただし、その差はわずか16KB。圧縮時にフィルタを「強」にした所、ファイルサイズは316KB、その差4KBまで縮小しました。フィルタ「強」で作成した画像はこちらからご覧ください。

 

用途に合わせて「減色」と「PNG24高圧縮化加工」を使い分ける

画像によってはPNG24高圧縮化加工よりも256色に減色したほうがファイルサイズが小さくなる、という結果もありました。しかし、その差はほんの僅か。数キロバイトの差で色数が65,536倍になりますので、「ファイルサイズが小さい方を使う」ことを前提にしつつも、画像を見比べて「256色だとキツイ」と思ったらPNG24高圧縮化加工を使う、という風に使い分けるのがいいかと思います。

ちなみに、今回はFacebookへのアップロード時に24bitへの再変換が掛かってしまうため、256色でもPNG24高圧縮化加工した画像でもそれほど極端な容量差は出ませんでしたが、スマートフォンアプリやウェブサイト、ソーシャルゲームなどに使うときは256色PNGの方が圧倒的に容量は小さくなります。

画像素材の種類や、何に使うかに合わせて「減色」「PNG24高圧縮化」を選択して使うことができる、それがOPTPiX imesta 7 for Mobile & Socialです。フルトライアルもご用意していますので、是非このPNG24高圧縮化をお試しください。

※注:上記の内容は2013年3月現在のFacebookの仕様に基づいたもので、仕様が変わる可能性がありますのであらかじめご了承ください。

田中圭一のゲームっぽい日常 謎のパスタブーム

$
0
0

田中圭一のゲームっぽい日常 謎のパスタブーム1ヶ月ほど前、理由もなく突然「無性にパスタが食べたい・・」と思い、会社の近所にあるイタリアンレストランに飛び込みました。とにかく、猛烈に食べたいという衝動が抑えられませんでした。

そして、パスタを口に入れた瞬間「凄ぇ美味ぇ!世の中にこんな美味いものがあったなんて!」と、どういうわけか感動しました。やはり、食べたいときが美味い時なんでしょうけれど、なぜ衝動的に食べたくなったのか理由がさっぱりわかりません。

とにかく、それ以来、可能な限り食事はパスタです。ほぼ毎日。ひどいときは昼食と夕食ともパスタ。クリームソース、トマトソース、ペペロンチーノ、和風ソースまでなんでもOKでした。どれも最高に美味しく、それは今も続いています。

本当に理由はわかりません。私の体または味覚に、なんらかの変化が起こったのでしょうか?また、このパスタブームは、いつまで続くのでしょうか?気味が悪いような感じもしますが、とにかく毎日パスタが美味しくて美味しくて幸せです。体重も着実に増えてきています。(^^;)

【イベント出展】Unite Japan

携帯ゲームファン待望!SpriteStudioPlayerがWonderSwanに対応しました!

$
0
0

携帯ゲームを愛するゲーム開発者に大ニュースです。コンシューマゲーム、スマートフォンアプリなど様々なゲーム制作現場で活用されている2Dアニメーション制作ツール「SpriteStudioシリーズ」のデータを、数々の名作を生み出し、携帯ゲーム機の歴史にその名を刻むワンダースワンの開発環境である「ワンダーウィッチ」に取り込むためのモジュール、その名も「SpriteStudioPlayer for WonderSwan」がリリースされました。

OPTPiX SpriteStudioPlayer for WonderSwan

リリースにあたって、本日4月1日付でニュースリリースを発表しております。下記ページからご覧ください。

» 携帯ゲーム機ワンダースワンの開発キット「ワンダーウィッチ」に対応 OPTPiX SpriteStudioPlayer for WonderSwanを発表


ファイル容量はそのままに美しい画像を実現した新技術「Clear PVRTC」を搭載、OPTPiX imesta for Mobiel & Socialの新バージョンが登場

$
0
0

本日4月2日、携帯サイト・ソーシャルコンテンツ・ソーシャルアプリ向けの画像最適化ツール「OPTPiX imesta for Mobile & Social」をアップデートいたしました。今回のアップデートで「Clear PVRTC」をOPTPiX Labsの技術として搭載いたしました。この技術は、ファイル容量をそのままに、標準ツールでのPVRTC変換よりも高画質な画像変換を実現しています。

詳細は本日発表のプレスリリースページをご覧ください。

「Clear PVRTC」の効果比較サンプル

Unityユーザーのための公式大型イベント、「Unite Japan」に参加いたします! もちろん、セッションも!

$
0
0

4月15日(月)・16日(火)にベルサール汐留で開催されるUnityユーザーの一大イベント「Unite Japan」に弊社が参加することになりました。Unityでのゲーム制作に関わる人たちが全国各地から集まるこのイベントですが、開発者向けセッションでの講演という大役まで仰せつかりました。タイトルは、『サムライソウル』にみる UNITY と SpriteStudio の演出と表現です。

UNITY - Unite Japan

セッションでは今春リリースとなる学園伝奇ジュヴナイルシリーズ最新作「サムライソウル」に弊社の”OPTPiX SpriteStudio”、そして”SpriteStudioPlayer for Unity”がどのように使われたのかを様々な裏話も交えてお送りする予定です。

Unite Japanの入場はチケット制となっています。入場には2日間通しチケットの購入が必要となります。購入ページヘのリンクはこのエントリーの最後に。

ちなみにサムライソウルのプロモーションムービーはこちら。同タイトルの中でどんな所に、どんな具合に”SpriteStudio”が活用されているのか……!当日のセッションをどうぞお楽しみに!

 

Unite Japan 概要

イベント名 Unite Japan
日時 2013年4月15日(月)・16日(火) 両日とも午前10時開場
弊社セッションは4月15日(月) 15:00~15:45 です。
場所 ベルサール汐留
» 地図はこちら
公式サイト http://japan.unity3d.com/unite/unite2013/
備考 入場にはチケットが必要です。(10,500円、学生半額)
2日間通し券の購入はこちらから。

田中圭一のゲームっぽい日常 マンガで味わう臨場感

$
0
0
田中圭一のゲームっぽい日常 マンガで味わう臨場感

マンガの魅力ってなんだと思いますか?
「キャラクターから目が離せない」とか「ストーリー展開が面白い」とか「絵柄や世界観が好みに合う」とか、人によってグっとくるポイントは様々だと思います。

そんな中、最近私が注目しているのは「まるで、その場にいるかのような臨場感を感じることのできるマンガ。」です。

そもそもマンガなんて、ペンで描かれたモノクロの世界。なのに、作品によっては「描かれた世界の中に自分も立って、キャラクターたちを見つめている」ような錯覚を覚えるマンガがあります。

あずまきよひこさんの「よつばと!」と、森薫さんの「乙嫁語り」が、それにあたります。読んでいると、ペンで描かれた絵なのに、あたかもその場所に自分がいるような錯覚を覚えます。

どちらの作品も、さして大きなドラマもなく、キャラクターたちが坦々と日常を過ごし、その中で起こる小さなエピソードだけを描いています。

逆に、そのせいか、読者はそのキャラクターたちと同じ時間を過ごしているかのような錯覚を覚えるのです。映画館に入って映画が始まった瞬間から、観客が日常から別世界へ誘われるように、これらのマンガを読むと、読者は、別世界へ行けるのです。

これって、凄いことだと思います。また、同業者として一作でもいいから、こういうマンガを描いてみたいと思います・・・けど、無理でしょうね。私には。(^^;)

Unity、HTML5などに対応した2Dアニメーション作成ツール「OPTPiX SpriteStudio」本日発売!

$
0
0

HTML5のデータ形式やUnity、Cocos2d-x、CoronaSDKなどの各種ゲームエンジンにも対応、コンシューマゲーム、スマートフォンアプリなどの様々なゲーム開発にご活用いただいております「SpriteStudioシリーズ」の最新版、「OPTPiX SpriteStudio」を本日発売いたしました。

今回新しく発売する「OPTPiX SpriteStudio」は、旧バージョンの基本機能を引き継いだ上で、処理速度と使い勝手の向上を実現し、大幅にリニューアルしました。また、従来のWindows プラットフォームに加えMac 環境にも対応し、英語版のリリースも予定しています。(Mac版、英語版は近日リリース予定です)

詳しくは本日発表のプレスリリースをご覧ください。

【イベント】FUSE 05

PVRTCの画質をあきらめない! 『Clear PVRTC』、登場!

$
0
0

ゲームプラットフォームとして今や一大勢力となった「iPhone」「iPad」。かつてのコンシューマゲーム機レベルの『しっかりと身の詰まった』面白いゲームやコンテンツが目白押し!

ですが、どうしても通信回線を使ってダウンロードという工程が挟まる、またストアアプリとしての厳しい容量制限があることなどから、『しっかりと詰めた身』を泣く泣く削る必要があります。その『削られる身』の代表格が左様、画像リソース。悩ましいところです。

この画像リソース。WebViewスタイルであれば例えばフルカラーの画像を減色して「PNG」にするなり、ノイズを我慢して「JPG」に圧縮するなりでどうにかこうにかすることが出来るのですが、時代はネイティブアプリ。となるとその画像圧縮スタイルとして「PVRTC」を選択することになります。 ところがこの「PVRTC」、高い圧縮率と高いGPUパフォーマンスと引き換えに、画質の方は少々・・・。

 

標準ツールで変換したPVRTC画像
キタナイ!

 

世界を席巻するヒットタイトルであっても、この『PVRTCの残念洗礼』は不可避・・・だったのですが、もう諦めなくてもいいんです! 圧縮率も! GPUパフォーマンスも! そして画質も!

 

Clear PVRTCで変換した画像
キレイ!

 

ご覧いただいているこの画像は、『OPTPiX imesta 7 for Mobile & Social』が新たに搭載する新機能『Clear PVRTC』従来の「PVRTC」と互換性を維持しつつ、圧縮後も高画質を維持することに成功しました。

本機能が『OPTPiX imesta 7 for Mobile & Social』に搭載されたのは、4/2のアップデートから。普段から「PVRTCの圧縮率は魅力的だけど、画質がアレなのでフルカラーPNGにしておこう」や、「一瞬しか表示しないエフェクトだから画質の悪いPVRTCでいいよねー」と諦めていた向きには是非、お試しいただきたい新機能です。

スプライトシート軽量化テクニック(JPEG分割 vs 減色)

$
0
0

開発部の山崎です。今回は、スプライトシートのファイル容量を軽減するためのテクニックをご紹介いたします。

スプライトシートとは

Adobe Flash CS6には「スプライトシート」を出力する機能があります。スプライトシートを使うと、Flash Playerを使わずに、ブラウザ上でパラパラマンガ方式のアニメーションを表示することができます。現在主流のブラウザ(IE9を除く)であれば、CSSやJavaScriptを使って容易にアニメーションを表示することができます。最近は、Flashに対応していないスマートフォンでWebを閲覧する機会が増えていることから、注目されている機能です。
本記事では、このスプライトシートのファイル容量を軽減するための方法について考察してみます。

ちなみに、弊社製品の「OPTPiX SpriteStudio」と「OPTPiX imésta」を組み合わせて、スプライトシートを作成することも可能です。

Adobe Flash CS6が出力するスプライトシートの問題点

Adobe Flash CS6では「スプライトシート」を出力する際、PNG8またはPNG32の2種類の画像フォーマットが選択できます。しかしながら、PNG8はファイルサイズは小さいのですが画質がいまひとつで、逆にPNG32は画質は良いのですがファイルサイズが大きいと、多くのクリエイターが頭を抱えているようです。

アニメーションを表示する際は、出力するコマ数も問題になります。コマ数が少ないとカクカクとしたぎこちない表示になってしまうので、なめらかなアニメーションを表示するにはコマ数を増やす必要があります。必然的に、画像ファイルサイズがクローズアップされるわけです。

比較サンプルとして、PNG32とPNG8のアルファ付画像を以下に示します。

Flashから出力したスプライトシートの例(PNG32bit)
Adobe Flash CS6で出力したPNG32画像
149,599バイト
ファイルサイズが大きい
Flashから出力したスプライトシートの例(PNG8bit) 
Adobe Flash CS6で出力したPNG8画像
39,948バイト(PNG32比26.7%)
画質が悪い
▲ Flashから出力したスプライトシートの例
(PNG32はPNG8に比べて画質は良いがファイルサイズは3.7倍ほど大きい)
この記事では、画質を比較しやすくするために、実際のスプライトシートのようにパラパラ画像ではなく、1枚画像で例示しています。また、前述のようにIE9ではスプライトシートを表示できないため、スプライトシートを本ページ上でそのまま表示せず、Chromeで表示した画像をキャプチャしたものを使用しています。元のスプライトシート画像ファイルを参照したい場合は、本記事の最後をご覧ください。

JPEGに分割し軽量化するテクニックが流行中

スプライトシートのファイルサイズが大きくなってしまう問題では多くのクリエイターが困っているようで、最近になって、出力されたPNG32ファイルを加工することで画質を維持したままトータルのファイルサイズを減らすテクニックを見かけるようになりました。

そのテクニックとは、「PNG32の画像を『JPEGファイル』と『アルファチャンネルのみのPNG32ファイル』の2つに分割することでトータルのファイルサイズを減らす」というものです。表示の際は、分割した2つの画像を合成することでスプライトシートに戻すのです。

スプライトシートが写真のような画像主体であれば、PNG32よりもJPEGのほうが高い圧縮率が期待できます。ただし、JPEGにはアルファチャンネルを含めることができないため、JPEGだけでは背景の透けない画像になってしまいます。そこで苦肉の策(?)として、アルファチャンネルのみをPNGに残して情報を補完し、合成表示するというアイディアが生まれたのです。以降、アルファチャンネルのみ格納したPNGファイルのことを、本記事ではPNG*(A)と表記することにします。この表記は、本記事独自のものです。

PNG32のスプライトシートを、JPEGとPNG(A)に分割するにはAdobe Flash CS6の機能だけではできないので、他の画像加工ツールが必要になります。弊社の「OPTPiX imésta 7 for Mobile & Social」を使えばJPEGへの変換も、PNG32(A)の出力も容易に行えます。
この分割テクニックは、アルファの情報をPNG32(A)で作成することを前提として広く紹介されていますが、インデックスカラーのPNG8(A)ファイルとして出力すれば、アルファチャンネルが失われることなくさらに小さく軽量化することが可能になります。

いくつかのサンプル画像を実際に作成して試してみたところ、PNG32で作成するよりもPNG8で作成したほうが10%~30%ほど小さく軽量化されることが確認できました(PNGファイル部分だけの比較)。従って、以下にご紹介するサンプル画像のPNG(A)画像は、すべてPNG8で作成しています。

さらに細かい話になりますが、アルファチャンネルのみをグレースケールのPNGにするという手段もあります。この場合、パレット情報の部分が無くなるため、さらにファイルを小さくできます。しかし、グレースケールにしてしまうと単純な合成表示ができなくなり、合成する前にアルファチャンネルへの変換処理が必要になります。すなわち合成表示のJavaScriptのコードに大きく手を入れる必要があります。作成の手間と合成の手間を考えた結果として、ここでは敢えてPNG8を選択しました。

Adobe Flash CS6で出力したPNG8画像
Adobe Flash CS6で出力したPNG8画像
39,948バイト(PNG32比26.7%)
 
Adobe Flash CS6で出力した JPEG+アルファチャンネル付きPNG8画像
PNG32をJPEG+PNG8(A)画像に変換して
表示した画像
38,583バイト(PNG32比25.8%)
 

▲ JPEGとPNG8(A)に分割出力したあと合成表示した画像の例
特に影の部分がきれいに透けています

しかしながら、このJPEG+PNG8(A)分割テクニックにも、以下のようにいくつか弱点があります。これらの弱点とメリットを天秤に掛けて、分割テクニックの採否を検討してください。

  • 分割した画像を合成して表示する際、ブラウザのcanvasタグの機能を使って描画しているため、ブラウザの種類によっては表示されない(*1)
  • JavaScriptで2つの画像を合成するため、表示されるまでに若干のタイムラグがある。画像が大きくなったり表示枚数が増えることでさらに影響が大きくなる
  • JPEGとPNG(A)ファイルに分割する作業が面倒
  • 2種類の画像を管理するのが面倒

*1 IE9はcanvasタグの互換性の問題で正しく表示できません。スマートフォン(AndroidやiOS)のブラウザでは合成表示が可能ですが、フィーチャーフォン(ガラケー)では合成表示できません。

アルファチャンネル込みで減色した画像との比較

PNGのファイルサイズを小さく軽量化する手段の1つに減色というテクニックがあります。ただし、Photoshopの減色機能ではアルファチャンネル付インデックスカラーへの減色ができませんので、スプライトシートを軽量化するのであれば「OPTPiX imésta 7 for Mobile & Social」のようにアルファチャンネル込みで減色できるツールを使う必要があります。

ここではiméstaの減色機能を使い、PNG32のスプライトシートをPNG8に変換し、JPEG+PNG8(A)へ分割後合成表示した場合との画質を比較 してみることにします。

比較を公正に行う目的で、いくつかの画像を用意しました。

  • 写真を切り抜いたタイプ
  • カードゲームに使われるようなイラスト
  • グラデーションのある文字や半透明効果の入ったボタン

詳しくは、エントリー最後にあるzipファイルをご覧ください。結果を画質のみで比較できるように、圧縮後のファイルサイズが揃うようにJPEGの圧縮率を変えています。

次に、特徴的な結果となった画像を3種類ほどピックアップして紹介します。なお、拡大画像はいずれも白枠内の部分を3倍に拡大したものです。

写真を切り抜いたタイプ

写真を切り抜いた画像などは、JPEGに分割するときれいに表示されます。iméstaによる減色も負けていませんが、このようなスプライトシートを作成する場合は、(画像によりますが)JPEGに分割するほうが画質的に良い結果が得られる場合があります。

Adobe Flash CS6で出力した JPEG+アルファチャンネル付きPNG8画像
PNG32をJPEG+PNG8(A)画像に変換して
表示した画像

38,583バイト(PNG32比25.8%)
OPTPiX iméstaで減色処理したPNG8画像
OPTPiX iméstaで減色処理したPNG8画像
39,933バイト(PNG32比26.7%)
 
 

カードゲームに使われるようなイラスト

イラストなどの画像になると、JPEG分割ではモスキートノイズが目立ちクッキリと表示されず、PNG8に比べてモヤっとしてきます。

イラストA

Adobe Flash CS6で出力した JPEG+アルファチャンネル付きPNG8画像
PNG32をJPEG+PNG8(A)画像に変換して表示した画像
16,657バイト(PNG32比31.8%)
OPTPiX iméstaで減色処理したPNG8画像
OPTPiX iméstaで減色処理したPNG8画像
16,858バイト(PNG32比32.2%)

イラストB

Adobe Flash CS6で出力した JPEG+アルファチャンネル付きPNG8画像
PNG32をJPEG+PNG8(A)画像に変換して表示した画像
40,464バイト(PNG32比30.2%)
OPTPiX iméstaで減色処理したPNG8画像
OPTPiX iméstaで減色処理したPNG8画像
40,754バイト(PNG32比30.5%)
 

グラデーションのある文字や半透明の入ったボタン

文字やボタンでは、JPEGに分割してしまうとかなり激しく崩れてしまいます。色数が少なめでクッキリと表示したい画像はJPEGには向いていません。

グラデーションのある文字

Adobe Flash CS6で出力した JPEG+アルファチャンネル付きPNG8画像
PNG32をJPEG+PNG8(A)画像に変換して表示した画像
25,371バイト(PNG32比58.9%)
OPTPiX iméstaで減色処理したPNG8画像
OPTPiX iméstaで減色処理したPNG8画像
22,330バイト(PNG32比51.8%)

半透明の入ったボタン

Adobe Flash CS6で出力した JPEG+アルファチャンネル付きPNG8画像
PNG32をJPEG+PNG8(A)画像に変換して表示した画像
24,732バイト(PNG32比50.3%)
OPTPiX iméstaで減色処理したPNG8画像
OPTPiX iméstaで減色処理したPNG8画像
25,130バイト(PNG32比51.1%)
 

結論 スプライトシートにはインデックスカラーが最適

スプライトシートのファイルサイズを最適化するには、JPEGとPNGの2枚の画像を管理したり、JavaScriptで合成しなければならない手間やマルチプラットフォームへの対応などを考えると、写真系の画像であっても一括してiméstaを使ってPNG8への変換がオススメできると言えそうです。

比較用参考画像

最後に、比較に使用したすべてのサンプル画像をzipファイルにまとめました。
更に詳しい情報を見てみたい、という方はダウンロードしてご覧ください。

» スプライトシートのサンプルファイルをダウンロードする(spritesheet_sample.zip : 824KB)


Unityユーザーのための公式大型イベント、「Unite Japan」に参加してきました!

$
0
0

去る、4月15日(月)・16日(火)にベルサール汐留にて開催されましたUnityユーザーの一大イベント「Unite Japan」に弊社も大参加してきました! というお話をば事後報告。ソリューション営業部あらためセールス&コミュニケーション部の浅井でございます。

さて本イベントを通じて体感できたのは、Unityユーザーの熱気。チケットも即完売、各セッションも満員御礼で立ち見、と大変なイベントでした。

弊社ブースには「SpriteStudioPlayer for Unity」の最新事例『サムライソウル』を、新製品「OPTPiX SpriteStudio」と共に。また、これまたUnityと大変相性の良い画像最適化ツール「OPTPiX imesta 7 for Mobile & Social」の新機能「Clear PVRTC」を展示。Unityアセットに2Dツールは数あれど、国産製品は稀。折しもこのイベントの当日0時にリリースされた『サムライソウル』への注目とあいまって、両製品とも大変ご注目いただきました。

ブースの様子

セッションの模様

セッションは満席が続出

弊社セッション、『サムライソウルにみる SpriteStudio と Unity の演出と表現』もおかげさまで満員御礼! 本セッションでは、ナウプロダクション様が『サムライソウル』を開発するにあたり、UnityとSpriteStudioの連携の仕方SpriteStudio自体の組み込みの容易さに加え、『サムライソウル』という学園伝奇ジュヴナイルシリーズ最新作の魅力をご紹介いただきました。個人的にワンダースワン版から同シリーズのファンでしたので、こういう形でご一緒できるのは大変恐悦至極!!

・・・だったのですが、ファミ通Appsに紹介されたセッションレポート掲載の自分の写真がですね、「ぱにぽに」のこのコマににていると大層、評判。

「ぱにぽに」第8巻 102話より引用
※ガンガンファンタジーコミックス「ぱにぽに(氷川へきる著)」第8巻より引用
 

いやみなさんのお言葉に真摯に耳を傾けているつもりなんですが・・・。

ともあれ、Uniteにお越しいただいた方も、また生憎お越しになれなかった方も、「OPTPiX SpriteStudio」と「OPTPiX imesta 7 for Mobile & Social」を、そしてもちろん『サムライソウル』を何卒よろしくお願いいたします。

『サムライソウル』公式サイト

田中圭一のゲームっぽい日常 東京で大阪のタコヤキに出会う

$
0
0
田中圭一のゲームっぽい日常「東京で大阪のタコヤキに出会う」

東京に出てきた大阪人は、よくこんなことを言います。「大阪のタコヤキは、たとえ駅前のしょぼい屋台でもレベルが高く、一方東京のタコヤキは、テナント店でもレベルが低い。」

大阪出身の私は、本当にそう思います。ただし、これは好みの問題なのかもしれません。東京のうどん出汁と大阪のうどん出汁の違いみたいなもので、それぞれの地域によって好みが分かれるだけなのかも…。

それはそれとして、大阪人である私は、タコヤキに関する限り東京で大阪の味に出会うことはとても少ないのです。本格的な関西うどんなら、東京でも食べることができます。なのに、タコヤキについては大阪風と謳っている店でも、本当の大阪風ではないのです。

この決定的な違いは、小麦粉と水の配合比率ではないかと思います。

東京のタコヤキは、身が締まっているというか、ホットケーキっぽい堅さであるのに対して、大阪のタコヤキは、形が保てるギリギリの柔らかさ、わらび餅くらいのプニョプニョ感が特徴です。大阪人である私は、子供の頃からフワフワでプニョプニョなのがタコヤキだと思っていたので、どうしても東京のプルンとしたタコヤキには違和感があるのです。

最近、最寄り駅の近くに「大阪で修行したタコヤキ屋」という看板を掲げているお店がオープンしました。期待せずにフラっと立ち寄って食べたところ、まさに大阪のタコヤキでした!柔らかさもさることながら、だし汁で溶いた小麦粉の味わい深さや、コンニャクの切れ端がタコと一緒に入っていて食感に彩りを加えていたりと、これぞ大阪のタコヤキや、って感じで感動しました。しばらく通い詰める予定です!

PVRTC圧縮のアルゴリズムとは?

$
0
0

最近、花粉症が収まってきてちょっとずつ調子が上向きな R&D部 上田です。

1か月お休みを挟んで、第三回目になる今回はPVRTC形式のアルゴリズムについてのお話です。

PVRTCって何?

まず最初にPVRTCの基本情報をざっくり書いておきます。

  • 正式名称:  PowerVR Texture Compression
  • 開発元:   Imagination Technologies
  • 圧縮モード: 4bppモード、2bppモード
  • 圧縮方式:  非可逆圧縮(圧縮後の画像は劣化します)
  • 特記事項:  画像サイズの縦横がそれぞれ2の累乗の画像のみに対応

圧縮モードにある「bpp」という単位は「bits per pixel」の略で、1ピクセルの色を表現するのに必要とするビット数を表しています。4bppというのは1ピクセルにつき4ビットの色情報を使っているということになります。ちなみに24ビットRGBカラーは24bpp、32ビットARGBカラーは32bppとなります。

特記事項にある2の累乗というのは、2の掛け算だけで作れる数のことで、2・4・8・16・32・64・128・256・512・1024・2048……という数字になります。
そのためこの制限の例を挙げると、

 ○:2×2、32×32、256×512、1024×64・・・など 
 ×:3×3、48×48、640×480、1024×768・・・など

ということになります。

PVRTCってどんなところで使われているの?

Imagination Technologies社の開発したGPUであるPowerVRシリーズがiPhone用のGPUとして採用されることでPVRTCが広く普及するようになりました。
その他にも、Androidなどの端末(TI OMAP)にも対応しているものがあるほか、ゲーム機のPlayStation®Vitaでも使われています。

PVRTCの圧縮品質はどんな感じ?

PVRTCを使って画像を圧縮してみましょう。

サンプルとして、24ビットRGBカラーで画像サイズが512×256の画像を用意しました。
それぞれの画像容量は、無圧縮で 393,216 バイト(=512×256×3)です。

flower_512-256_quater-NearestNeighbor cat_512-256_quater-NearestNeighbor renga_512-256_quater-NearestNeighbor
無圧縮の元画像(各 393,216 バイト)
(クリックで等倍表示)

これらの画像をPVRTCの4bppモードで圧縮します。

flower_pvrtc4bpp_512-256_quater-NearestNeighbor cat_pvrtc4bpp_512-256_quater-NearestNeighbor renga_pvrtc4bpp_512-256_quater-NearestNeighbor
PVRTC圧縮後の画像(4bppモード 各 65,536 バイト)
(クリックで等倍表示)

圧縮するとそれぞれの画像容量は 65,536 バイト となり、圧縮前の6分の1になります。
写真や自然画のような画像では、PVRTCによる圧縮でもDXTCと同様に劣化はほとんど分からないぐらいのレベルに抑えられています。
注)ブログ内の画像は、ブラウザでそのまま表示できるように全てPNG形式に変換しているため、上記画像容量とは異なります。以下の画像についても同様です。

また上記の画像は4bppモードで圧縮したものですが、2bppモードを使うことで画像容量をさらに半分(32,768バイト)にすることが可能です。こちらのモードでは、クッキリした境目のあたり(花の周囲など)に若干ブロックノイズが見られます。

flower_pvrtc2bpp_512-256_quater-NearestNeighbor cat_pvrtc2bpp_512-256_quater-NearestNeighbor renga_pvrtc2bpp_512-256_quater-NearestNeighbor
PVRTC圧縮後の画像(2bppモード 各 32,768 バイト)
(クリックで等倍表示)

PVRTCには苦手な画像がある?

PVRTCは上で示したように写真や自然画のような画像は比較的綺麗に圧縮できていました。

それでは、アニメ調の画像ではどうなるのでしょうか? DXTCの時と同様に魔法少女の画像をPVRTCで圧縮してみましょう。

figure_magicalgirl_original_fullfigure_magicalgirl_pvrtc4bpp_fullfigure_magicalgirl_pvrtc2bpp_fullfigure_magicalgirl_dxtc_full
PVRTCとDXTCによって圧縮した魔法少女画像の比較

画像の変化が分かりやすいように、少女の頭上の犬部分を拡大してみます。

figure_magicalgirl_dog_original_fullfigure_magicalgirl_dog_pvrtc4bpp_fullfigure_magicalgirl_dog_pvrtc2bpp_fullfigure_magicalgirl_dog_dxtc_full
犬の部分を拡大(4倍)して比較

拡大するとよくわかるのですが、PVRTC圧縮した画像は絵の輪郭付近でにじむように色が変化してしまって画像が劣化しています。4bppモードより2bppモードの方がより劣化が激しいのも見てとれます。

またDXTCの劣化具合と比較すると、PVRTCでは輪郭付近で色がにじんでいるのに対し、DXTCはブロック状に色のおかしい部分(ブロックノイズ)が出現していて、劣化具合にそれぞれ特徴があることがわかります。

PVRTCってどんなアルゴリズム?

それではPVRTC形式(4bppモード)のアルゴリズムを解説しましょう。

1.4×4ピクセルのブロックに分割する

まずPVRTCではDXTCと同じように、4×4ピクセルを1つのブロックとして扱います。

figure_16pixel_to_1block_half-bycubic
4×4ピクセルを1ブロックとして扱う

2.代表色2色を決定する

次にそれぞれのブロックごとに代表色となる2色を決定します。

figure_select_2color_half-bycubic
1ブロックごとに2色の代表色を決定する

ここでは単純に代表色2色を決定すると書きましたが、後述する手順の関係上、実際にはそんなに簡単に代表色を決めることはできません。ブロックに無い色を代表色に設定することもできます。

3.各ピクセルの代表色のブレンド方法を決める

代表色2色を4種類の割り合いでブレンドして、各ピクセルを最も近い色で置き換えます。そして、各ピクセルのブレンド方法をデータとして保存します。

figure_modulation_data_half-bycubic
各ピクセルの代表色のブレンド方法を決めて、Modulation Dataとして保存する

この保存したブレンド方法のデータをModulation Dataと呼びます。

ブレンド方法は4種類なので2ビットで表現でき、1ブロックは16ピクセルで構成されているので、Modulation Dataは32ビットになります。

ところで、ここまで説明してきた代表色の2色ですが、Color Aは16ビット、Color Bは15ビットのデータになっています。

そしてこれらとは別に「Mode Bit」と呼ばれる1ビットのデータがあり、このMode Bitの値によって代表色2色のブレンド方法が切り替わる仕組みになっています。
(Color BがColor Aに比べて1ビット少ないのは、このMode Bitに1ビット使っているからです)

figure_modulation_data_mode_half-bycubic
Modulation Data には二つのモードがある

なお今回の記事では、すべてMode Bitが1の場合の例で説明しています。

4.完成・・・?

ここまでで、代表色2色とModulation Dataを生成しました。

代表色のColor Aが16ビット、Color Bが15ビット、Modulation Dataが32ビットで、これに1ビットのMode bitを加えた合計64ビットがPVRTC圧縮(4bpp)の1ブロックのデータになります。

figure_block_structure_image_half-bycubic
PVRTC圧縮(4bpp)画像の1ブロック分のデータ

しかし、この64ビットのデータを単純に復元したのではDXTC(過去の記事参照)とあまり変わりません。

figure_not_blend_original_color_half-bycubic
PVRTCでの画像復元は代表色の単純なブレンドではない

実はPVRTC圧縮形式では復元方法に大きな特徴があります。

5.代表色を使ってブロック内の色を補間する

PVRTC圧縮形式では、代表色のColor A と Color Bをブレンドする前に一つ処理を挟みます。
それは、代表色を使ってブロック内の色を補間するという処理です。
まず、代表色であるColor A と Color Bがブロック内の中心より右下のピクセルに入っているとします。

figure_before_interpolation_half-bycubic
代表色をブロック内の1つのピクセルのみに入れる

そしてブロック内の他のピクセルの色は、自分自身の代表色と周りのブロックの代表色を使って補間することで色を入れていきます。

figure_interpolation_half-bycubic
代表色が入っているピクセル以外は補間して色を入れる

この補間処理は、バイリニア補間方式で行われています。

6.補間結果を利用して Color A と Color B をブレンドする

Color AとColor Bを補間した後、Modulation Dataに従って各ピクセルの色をブレンドします。

figure_blend_after_interpolation_half-bycubic
補間した後でModulation Dataに従って色を混ぜ合わせる

このように、PVRTC圧縮形式では単純に代表色を混ぜ合わせるのではなく補間を利用することで、補間しない場合に比べて、隣接ブロックとの境界の色変化が滑らかになり、DXTCのようなブロックノイズ(ブロック境界で色が急激に変化して画像にブロックが浮き出てしまう現象)が発生しにくくなります。

ただし、補間という処理が入るためにColor AとColor Bの指定やModulation Dataの作成は補間結果を想定して行わなければなりません。そのためこれらのデータの選び方によって画質が大きく変わってしまうことが、この圧縮方法のもっとも難しい点と言えるでしょう。

2bppモードはどうなる?

2bppモードでは、1ブロックを32ピクセル(横8ピクセル×縦4ピクセル)とします。そして圧縮後の1ブロック分のデータは64ビットになります。

4bppモードが16ピクセルを64ビットで表現するのに対して、2bppモードは32ピクセルを64ビットで表現しているため、圧縮後のデータ量は4bppモードの半分になります。

当然のことながら、2bppモードの方が圧縮率が高い分、画質は更に低下します。

PVRTCでアニメ調の絵が劣化する原因を考える

それではこんな風に絵が劣化してしまう原因を考えてみましょう。

1ブロックで使用できる色数が少ない!

一つ目の原因はDXTCと同じで、1ブロックで使用できる色が少ないことがあげられます。

つまり、基本的には1ブロックで代表色2色とその補間色しか使えませんので、

figure_pvrtc_unlike_figure_half-bycubic
PVRTCでもDXTCと同様にこのような色の変換は起こっている

実際に、上記の魔法少女の画像でも黒の輪郭線周辺で色がおかしくなっているのが確認できます。

またPVRTC特有の色のにじむような劣化については、復元処理での補間が関係しています。

figure_diff_pvrtc_dxtc_half-bycubic
PVRTCとDXTCの劣化の違いについて(画像はイメージです、実際の圧縮結果とは異なります)

DXTCではブロック毎に色が独立しているので、一つのブロックで劣化が発生してしまっても他のブロックには影響しません。

しかしPVRTCでは復元時の補間の影響で、一つのブロックの劣化が他のブロックに影響を及ぼしたり、隣り合うブロックの色がお互いに影響を与えてしまうため、色がにじんだように見えているのです。

色情報のビット数が少ない!

また、PVRTCは代表色に使っている色情報のビット数が少ないことも劣化理由の一つになっています。以下はPVRTCの代表色についてのビット構造です。

figure_pvrtc_color_info_half-bycubic
PVRTCの代表色のビット構造

32ビットARGBカラー画像では、ARGBそれぞれで8ビット使用することができます。これは階調にすると各色256階調ということになります。

対して、PVRTCの代表色では各色5ビット~3ビットしか使うことができません。色情報が5ビットだと32階調、4ビットだと16階調、3ビットだと8階調となってしまいますから、256階調と比べると大幅に使用できる色が減っていることになり、これが画像の劣化原因になっています。

アルファ付き画像がより苦手?

上の魔法少女の画像はアルファ無しの画像でした。実はPVRTC圧縮は輪郭のくっきりした画像でもアルファ付きの画像がより苦手です。

次のような、文字とその周りをアルファチャンネルを使って透明にした画像をPVRTC圧縮してみます。

figure_rare_text_original_half-bycubic

figure_rare_text_pvrtc_by-cubic
アルファ付きテクスチャをPVRTC圧縮すると劣化が大きく出てしまう
※透過(アルファ)部を分かりやすくするため、背景色として水色を重ねてあります

上で説明したように、代表色のアルファチャンネルは3ビット、つまり8階調しか使えないので圧縮前の画像よりも階調が大きく落ちます。

さらに上の文字の例のように輪郭の外側をアルファを使って透明にしているような画像では、輪郭部分でアルファチャンネルが急激に変化しているのですが、PVRTCではアルファチャンネルにも補間を行うためにこの輪郭部分のアルファチャンネルがぼやけてしまいます。

この二つの理由によって、アニメ調の絵や書き文字のような、アルファ付きの画像でかつ輪郭部分の外側をアルファチャンネルで透明にしているような画像は特に劣化が大きくなってしまうことになります。

Clear PVRTC ってなんだ?

最後にちょっとだけ宣伝です。

ウェブテクノロジでは従来よりも高画質なPVRTC形式の画像を生成する新技術「Clear PVRTC」を開発いたしました。(プレスリリースはこちら)

この技術では、従来PVRTC形式との完全な互換性を保ったままでより高画質なPVRTC画像生成を実現しています。

上記の文字サンプル画像もこのとおり。

figure_rare_text_clear_pvrtc_half-bycubic
驚きの美しさに!!

「Clear PVRTC」は弊社製品 OPTPiX imesta for Mobile & Social の無償アップデートとして4/2から搭載中です。

無料トライアル版もご用意しておりますので、PVRTCの画像品質にお困りの方はぜひ一度お試しください。

メール配信ソフトウェア phpList を日本語対応&カスタマイズしてみた

$
0
0

通常業務のほとんどを Fedora 18 上で行なっている、当社では珍しい存在の yone です。GNOME 3 もなんのその、慣れれば何とかなります。

1. phpList とは

当社では、ニュースレターやプレスリリース等のメールを一括送信するために、phpList というオープンソース・ソフトウェアを使っています。phpList はニュースレターの発行に必要な機能を一通り揃えた高機能なメール配信用ソフトウェアですが、本家の最新バージョンは英語版として公開されるため、最新版の日本語対応と、いくつかのカスタマイズを行ったうえで運用しています。

この記事では、当社で行った日本語対応とカスタマイズ方法について紹介します。

  • phpList の日本語対応
  • HTML メールを扱わないようにする
  • <〜> のような箇所があると消えてしまう
  • ヘッダが本文まで飛び出さないようにする
  • unsubscribe リンクを非表示
  • powered by phpList を非表示

 

phpList 2.10.19 の日本語対応版は、下記でも配布されています。ご自身のニーズと近いものを選択されると良いでしょう。

2. phpList の日本語対応作業

phpList の日本語化といいますと、ディアイピィ社によるPHPist日本語化適用版が挙げられます。ところが、当社で導入しようとした当時、最新版 phpList は 2.10.17 がリリースされていましたが、日本語化適用版は 2.10.12 と大きな開きがありました。この間にセキュリティアップデートも含んでおり、当社で運用するものは最新の 2.10.17 をベースに使いたいと思い、作業を始めました。その後、本家phpList は 2.10.19 までバージョンアップがあり、それに追従して phpList 2.10.19 日本語対応 を作りました。

日本語対応作業にあたっては、ディアイピィ社の phplist-2.10.12日本語化適用版を参考にさせていただきました。また、日本語メッセージファイル (public_html/lists/texts/japanese.inc) は、当社の用途に合わせて文面を変更しています。

  • 日本語メールの強制設定
  • 文字コードを EUC-JP から UTF-8 へ変更
  • デフォルトフォーマットをテキストへ変更
  • 訳文の追加・変更

適用差分は下記になります。2.10.19 へ適用可能です。

  1. データベースは、character set utf8 で作成する
     
  2. 下記ファイルを、EUC-JP から UTF-8 へ変換
    • public_html/lists/texts/japanese.inc
    • public_html/lists/admin/phpmailer/language/phpmailer.lang-ja.php
  3. phplist-2.10.17-ja.patch
Japanized phplist patch
このパッチの他、下記のファイルを EUC-JP から UTF-8 へ変換する必要があります
	public_html/lists/texts/japanese.inc
	public_html/lists/admin/phpmailer/language/phpmailer.lang-ja.php


diff -urN phplist-2.10.17.orig/public_html/lists/admin/class.phplistmailer.php phplist-2.10.17/public_html/lists/admin/class.phplistmailer.php
--- phplist-2.10.17.orig/public_html/lists/admin/class.phplistmailer.php	2011-08-09 00:25:49.000000000 +0900
+++ phplist-2.10.17/public_html/lists/admin/class.phplistmailer.php	2011-10-17 17:51:01.656699810 +0900
@@ -8,7 +8,7 @@
 
 class PHPlistMailer extends PHPMailer {
     var $isText = false;
-    var $WordWrap = 75;
+    var $WordWrap = 0;
     var $encoding = 'base64';
     var $image_types = array(
                   'gif'  => 'image/gif',
@@ -24,7 +24,7 @@
 
     function PHPlistMailer($messageid,$email) {
     #  parent::PHPMailer();
-      parent::SetLanguage('en', dirname(__FILE__) . '/phpmailer/language/');
+      parent::SetLanguage('ja', dirname(__FILE__) . '/phpmailer/language/');
       $this->addCustomHeader("X-Mailer: phplist v".VERSION);
       $this->addCustomHeader("X-MessageID: $messageid");
       $this->addCustomHeader("X-ListMember: $email");
diff -urN phplist-2.10.17.orig/public_html/lists/admin/defaultconfig.inc phplist-2.10.17/public_html/lists/admin/defaultconfig.inc
--- phplist-2.10.17.orig/public_html/lists/admin/defaultconfig.inc	2011-09-20 23:35:49.000000000 +0900
+++ phplist-2.10.17/public_html/lists/admin/defaultconfig.inc	2011-10-17 17:50:04.603210442 +0900
@@ -484,12 +484,12 @@
 ),
 
 "html_charset" => array (
-  "UTF-8",
+  "ISO-2022-JP",
   "Charset for HTML messages",
   "text"
 ),
 "text_charset" => array (
-  "UTF-8",
+  "ISO-2022-JP",
   "Charset for Text messages",
   "text"
 ),
diff -urN phplist-2.10.17.orig/public_html/lists/admin/languages.php phplist-2.10.17/public_html/lists/admin/languages.php
--- phplist-2.10.17.orig/public_html/lists/admin/languages.php	2011-09-20 23:35:49.000000000 +0900
+++ phplist-2.10.17/public_html/lists/admin/languages.php	2011-10-17 17:50:04.603210442 +0900
@@ -14,6 +14,7 @@
 "es"=>array("espa&ntilde;ol","iso-8859-1","iso-8859-1, windows-1252"),
 #"fa" => array('Persian','utf-8','utf-8'),
 "fr"=>array("fran&ccedil;ais ","iso-8859-1","iso-8859-1, windows-1252 "),
+"ja"=>array("Japanese ","utf-8","shift_jis, iso-2022-jp, euc-jp, utf-8"),
 "pl"=>array("Polish ","iso-8859-2","iso-8859-2"),
 "pt-br"=>array("portugu&ecirc;s ","iso-8859-1","iso-8859-1, windows-1252"),
 "zh-tw" => array("Traditional Chinese","utf-8","utf-8"),
diff -urN phplist-2.10.17.orig/public_html/lists/admin/phpmailer/class.phpmailer.php phplist-2.10.17/public_html/lists/admin/phpmailer/class.phpmailer.php
--- phplist-2.10.17.orig/public_html/lists/admin/phpmailer/class.phpmailer.php	2011-05-12 23:09:40.000000000 +0900
+++ phplist-2.10.17/public_html/lists/admin/phpmailer/class.phpmailer.php	2011-10-17 17:50:04.604210468 +0900
@@ -33,7 +33,7 @@
      * Sets the CharSet of the message.
      * @var string
      */
-    var $CharSet           = "iso-8859-1";
+    var $CharSet           = "iso-2022-jp";
 
     /**
      * Sets the Content-type of the message.
@@ -46,7 +46,7 @@
      * "7bit", "binary", "base64", and "quoted-printable".
      * @var string
      */
-    var $Encoding          = "8bit";
+    var $Encoding          = "7bit";
 
     /**
      * Holds the most recent mailer error message.
@@ -397,6 +397,8 @@
      * @return bool
      */
     function SendmailSend($header, $body) {
+        $body = mb_convert_encoding($body, $this->CharSet);
+
         if ($this->Sender != "")
             $sendmail = sprintf("%s -oi -f %s -t", $this->Sendmail, $this->Sender);
         else
@@ -427,6 +429,8 @@
      * @return bool
      */
     function MailSend($header, $body) {
+        $body = mb_convert_encoding($body, $this->CharSet);
+
         $to = "";
         for($i = 0; $i < count($this->to); $i++)
         {
@@ -465,6 +469,8 @@
      * @return bool
      */
     function SmtpSend($header, $body) {
+        $body = mb_convert_encoding($body, $this->CharSet);
+
         include_once($this->PluginDir . "class.smtp.php");
         $error = "";
         $bad_rcpt = array();
@@ -1149,6 +1155,8 @@
      * @return string
      */
     function EncodeHeader ($str, $position = 'text') {
+      $str = mb_encode_mimeheader($str, $this->CharSet, 'B');
+
       $x = 0;
 
       switch (strtolower($position)) {
diff -urN phplist-2.10.17.orig/public_html/lists/config/config.php phplist-2.10.17/public_html/lists/config/config.php
--- phplist-2.10.17.orig/public_html/lists/config/config.php	2011-09-20 23:35:49.000000000 +0900
+++ phplist-2.10.17/public_html/lists/config/config.php	2011-10-17 17:50:04.604210468 +0900
@@ -10,13 +10,15 @@
 
 */
 
+mb_language("Japanese");
+mb_internal_encoding("UTF-8");
 
 # select the language module to use
 # Look for <country>.inc files in the texts directory
 # to find your language
 # this is the language for the frontend pages. In the admin pages you can
 # choose your language by using the dropdown in the pages.
-$language_module = "english.inc";
+$language_module = "japanese.inc";
 
 # what is your Mysql database server
 $database_host = "localhost";
  1. public_html/lists/texts/japanese.inc
<?php

# language dependent text used in the interface for users (not admin)

$strCharSet             = 'UTF-8';
$strName                = '名前';
$strAddress             = '住所';
$strEmail               = '電子メール';
$strTown                = '住所';
$strPostcode            = '郵便番号';
$strSubscribeInfo       = '以下のフォームを使い1つ以上のニュースレター・メールマガジンを登録してください。';
$strRequired            = '必須項目';
$strSubscribeTitle      = 'ニュースレター・メールマガジンの配信登録';
$strPleaseSelect        = '<p>配信登録したいニュースレター・メールマガジンを選択してください。</p><p>チェックされていないニュースレター・メールマガジンは配信されません。</p><p>配信中のニュースレター・メールマガジンを停止する場合は、チェックを外してください。</p>';
$strSubmit              = '選択したニュースレター・メールマガジンの配信';
$strNotAvailable        = '申し訳ございません。現在、利用可能なニュースレター・メールマガジンはありません。';
$strEnterName           = 'お名前を入力してください';
$strEnterEmail          = '電子メールアドレスを入力してください';
$strConfirmEmail        = '電子メール(確認)';
$strInvalidHostInEmail  = 'すいません。そのドメインへ電子メールを送ることはできません。正しい電子メールアドレスを入力しているか確認してください。';
$strEnterList           = '<p>配信登録したいニュースレター・メールマガジンを選択してください。</p><p>チェックされていないニュースレター・メールマガジンは配信されません。</p><p>配信中のニュースレター・メールマガジンを停止する場合は、チェックを外してください。</p>';
$strPleaseEnter         = '入力してください:';
# ありがとうメッセージにはプレースホルダーを含めることができます。
#$strThanks              = '[FIRST NAME]さん、<br/>ニュースレター・メールマガジンを配信登録していただきありがとうございます。';
$strThanks              = 'ニュースレター・メールマガジンを配信登録していただきありがとうございます。';
# 登録認証のお願いメッセージにはプレースホルダーを含めることができます。
#$strEmailConfirmation   = 'あなたの電子メールアドレス [email] がシステムに追加されました。間もなく配信登録したニュースレター・メールマガジンについて配信確定のお願いが電子メールで送信されます。';
$strEmailConfirmation   = 'あなたの電子メールアドレスがシステムに追加されました。間もなくニュースレター・メールマガジンについて配信確定のお願いが電子メールで送信されます。';
$strEmailFailed          = '申し訳ございません。。配信確定のお願いの電子メールを送信することに失敗しました。再度、「Reload」をクリックしてください。もし、まだ失敗するようでしたら、あなたの電子メールアドレスは"配信解除済リスト"に追加されているかもしれません。、これは、私たちの配信システムから電子メールを受け取れないことを意味しています。そのような場合、管理者までご連絡ください。';
$strUnsubscribeTitle    = 'ニュースレター・メールマガジンの配信登録解除';
$strUnsubscribeDone     = 'ニュースレター・メールマガジンの配信を解除しました。間もなく配信解除確認の電子メールが送信されますのでご確認ください。';
$strBack                = '戻る';
$strUnsubscribeInfo     = 'ニュースレター・メールマガジンの配信解除';
$strContinue            = '続ける';
$strUnsubscribeSelect   = '配信解除したいニュースレター・メールマガジンを選択してください。';
$strAllLists            = '全てのニュースレター・メールマガジン';
$strNoLists             = '該当はありません';
$strUnsubscribeRequestForReason = '<br/>ニュースレター・メールマガジンに興味がなくなったことを非常に残念に思います。私たちのサービスを改善するために、理由を教えていただければ幸いです。';
$strUnsubscribeFinalInfo = '<br/><p><b>注意</b>: もし配信停止ではなく、メールアドレスを変更したい場合は、<a href="[PREFERENCESURL]">情報の更新</a>をおこなってください。</p>';
$strNoListsFound        = '本メールアドレスは登録されていません。';
$strResubmit            = 'Re-Submit Email';
$strUnsubscribeSubmit   = '選択しているニュースレター・メールマガジンの配信解除';
$strValuesMissing       = '次の必須項目に入力されていません。';
$strConfirmInfo         = 'ニュースレター・メールマガジンを配信登録していただきありがとうございます。現在配信登録されているニュースレター・メールマガジンは以下のとおりです。';
$strConfirmTitle        = 'PHPlist メンバーシップ設定ページ';
$strUserNotFound        = '本メールアドレスは登録されていません。';
$strUserAlreadyInitialised = 'すいません。あなたのパスワードは既に設定されています。';
$strConfirmFailInfo     = 'すいません。配信確定はされませんでした。受け取った電子メールに記載されている完全なWEBアドレス(URL)を確かめてください。このWEBアドレス(URL)は複数行にわかれて表示されることがあります。';
$strPreferHTMLEmail     = '私はHTMLフォーマットで電子メールを受け取ります。';
$strPreferTextEmail     = '私はTextフォーマットで電子メールを受け取ります。';
$strPreferredFormat      = '電子メールの優先フォーマット:';
$strText                 = 'テキスト';
$strHTML                = 'HTML';
$strPreferencesTitle    = '配信情報の更新';
$strPreferencesInfo     = '下記の情報が正しいことを確認し、"更新"ボタンをクリックしてください。';
$strUpdatePreferences   = '更新';
$strPreferencesEmailChanged = 'メールアドレスを変更された場合は、新しいメールアドレスを確定するためのメールを送信します。メールに記載されたリンクをクリックしてください。';
$strYouAreBlacklisted = 'あなたの電子メールアドレスは配信解除済リストに掲載されています。いかなるメッセージもあなたには決して送信されません。 <br/>
また電子メールを受け取るために、管理者による手動の解除が必要です。またニュースレター・メールマガジンを受け取りたいことをはっきりと明言し、わたしたちにコンタクトしてください。配信解除済リストからあなたの電子メールアドレスを削除します。';
$strPreferencesNotificationSent = 'ご変更いただいた内容を電子メールでお送りいたしました。';
$strPreferencesUpdated = 'ありがとうございます。配信情報を更新いたしました。';
$strClickHere          = 'ここをクリック';
$strThisLink           = 'このリンク';
$strToUnsubscribe      = 'もし、いかなるニュースレター・メールマガジンも受け取りたくないならば、こちらをクリック:';
$strToUpdate           = 'メールアドレスを変更したり、配信停止をする場合は、こちらをクリック:';
$strSendHTML           = 'HTMLメールを受信する';
$strYes                = 'はい';
$strNo                 = 'いいえ';
$strUnsubscribe        = '配信停止';
$strAllMailinglists    = 'すべてのニュースレター・メールマガジン';
$strAttachmentIntro     = 'このメッセージにはウェブブラウザでみることができる添付ファイルが含まれています:';
$strLocation           = '登録情報';
$strFrequency           = 'メッセージを送る頻度';
$strHourly             = '毎時';
$strDaily               = '毎日';
$strWeekly             = '毎週';
$strMonthly             = '毎月';
$strChoosePassword      = 'パスワードを選択してください';
$strConfirmPassword    = 'パスワード(確認)';
$strEnterPassword       = 'パスワードを入力してください';
$strForgotPassword     = 'パスワードを忘れた方へ';
$strPassword           = 'パスワード';
$strPassword2           = 'パスワード(確認)';
$strPasswordsNoMatch    = 'パスワードが同じでありません。';
$strEmailsNoMatch     = '入力した電子メールアドレスがマッチしません。';
$strInvalidPassword     = 'エラー: 電子メールまたはパスワードが妥当でありません。';
$strPasswordSent        = 'あなたのパスワードを電子メールで送りましたので、数分後に電子メールを受信してください。';
$strPasswordRemindSubject = 'ニュースレター・メールマガジンに対するあなたのパスワード';
$strPasswordRemindMessage = 'Your password is ';
$strPasswordRemindInfo = '電子メールでパスワードを受け取るために、電子メールアドレスを入力して、"'.$strForgotPassword.'" ボタンをクリックしてください。';
$strLogin               = 'ログイン';
$strLoginTitle          = '電子メールアドレスとパスワードを入力してください。';
$strLoginInfo           = 'このページはパスワードが必要です。電子メールアドレスとパスワードを入力してください。';
$strPersonalLocationInfo = '
  <p>電子メールアドレスを入力して、"続ける"をクリックしてください。<br/>
  ご入力いただいたメールアドレス宛に「配信情報変更用URL」が記載されたメールをお送りいたします。</p>
';
$strPersonalLocationSent = '<h1>ご入力いただいたメールアドレス宛に「登録情報変更用URL」が記載されたメールをお送りしました。</h1>
<br/><p>※他に変更される電子メールアドレスがありましたら、下のフォームへご入力ください。</p>';
$strUserExists   = '<br/>その電子メールアドレスをもつユーザは既に存在し、異なったパスワードになっています。';
$strUserExistsExplanationStart = '<br/>あなたのパスワードを入手するために、クリック ';
$strUserExistsExplanationLink = 'ここ';
$strUserExistsExplanationEnd = '「配信情報変更用URL」をリクエストできるページにいく';
$strExplainBlacklist = 'ここで入力した電子メールアドレスは配信解除されます。';
$strForwardTitle = 'メッセージを誰かに転送する';
$strForwardSubtitle = 'メールタイトル付でメッセージを転送する ';
$strForwardEnterEmail = '転送するために妥当な電子メールアドレスを入力してください。';
$strForwardInvalidEmail = '%s は妥当な電子メールアドレスではありません';
$strForwardBlacklistedEmail = '転送先電子メールアドレスは配信解除されているため、登録できません。';
$strForwardEnterEmails = '転送先電子メールアドレス数を1行に1つずつ入力してください。最大 %s まで。';
$strForwardPersonalNote = '転送メールの前に追加するテキストメッセージ(最大 %d 文字)';
$strForwardSuccessInfo ='メッセージは転送されました。';
$strForwardFailInfo = 'メッセージの転送は失敗しました。';
$strForwardAlreadyDone = 'このメッセージは既にうまくその電子メールアドレスに転送されました。';
$strForwardCountReached = '転送先電子メールアドレス数が多すぎます';
$strForwardNoteLimitReached = '追加するテキストメッセージが長すぎます';
$strForwardFooter = 'このメッセージは[FORWARDEDBY]によりあなたに転送されました。
  このニュースレター・メールマガジンは自動的に登録されません。
  このニュースレター・メールマガジンを今後受信するには以下のアドレスをクリックしてください。';
$strForward = '転送';
$strFwd = 'Fwd'; # short version of forward for email subject
  1. 管理ページへアクセスし、設定を変更する
    • 「Charset for HTML messages」を「ISO-2022-JP」へ変更
    • 「Charset for Text messages」を「ISO-2022-JP」へ変更

3. HTML メールを扱わないようにする

当社の使い方では、メールはテキスト形式のみで送信します。HTML メールは使っていません。誤って HTML を選択しないよう、機能を殺しました。

  1. phplist-2.10.17-textmail.patch
diff -urN phplist-2.10.17.orig/public_html/lists/admin/send_core.php phplist-2.10.17/public_html/lists/admin/send_core.php
--- phplist-2.10.17.orig/public_html/lists/admin/send_core.php	2011-04-29 20:41:44.000000000 +0900
+++ phplist-2.10.17/public_html/lists/admin/send_core.php	2011-09-30 17:33:14.118971548 +0900
@@ -72,7 +72,7 @@
 if (!$id) {
   $defaulttemplate = getConfig('defaultmessagetemplate');
   Sql_Query(sprintf('insert into %s (subject,status,entered,sendformat,embargo,repeatuntil,owner,template,tofield,replyto)
-    values("(no subject)","draft",now(),"HTML",now(),now(),%d,%d,"","")',$GLOBALS["tables"]["message"],$_SESSION["logindetails"]["id"],$defaulttemplate));
+    values("(no subject)","draft",now(),"text",now(),now(),%d,%d,"","")',$GLOBALS["tables"]["message"],$_SESSION["logindetails"]["id"],$defaulttemplate));
   $id = Sql_Insert_id();
   # 0008720: Using -p send from the commandline doesn't seem to work
   if(!$GLOBALS["commandline"]){
@@ -1122,7 +1122,7 @@
     $formatting_content .= $_POST["sendformat"]=="HTML"?"checked":"";
     $formatting_content .= '>
   '.$GLOBALS['I18N']->get("text").' <input type=radio name="sendformat" value="text" ';
-    $formatting_content .= $_POST["sendformat"]=="text"?"checked":"";
+    $formatting_content .= $_POST["sendformat"]=="text" || !isset($_POST["sendformat"]) ?"checked":"";
     $formatting_content .= '>
   ';
 
@@ -1134,7 +1134,7 @@
 
 //  0009687: Confusing use of the word "Both", indicating one email with both text and html and not two emails
 //  $formatting_content .= $GLOBALS['I18N']->get("textandhtml").' <input type=radio name="sendformat" value="text and HTML" ';
-//  $formatting_content .= $_POST["sendformat"]=="text and HTML" || !isset($_POST["sendformat"]) ?"checked":"";
+//  $formatting_content .= $_POST["sendformat"]=="text and HTML"?"checked":"";
 //  $formatting_content .= '>';
 
   if (USE_PDF) {

4. 「<目次>」が書けない

<〜> のような HTML タグ形式らしい文字列を見つけると、テキストメール送信時は削除する動作になっていました。HTML メールは使わないと心に決めた身には不便でならないので、この機能を殺します。

public_html/lists/admin/send_core.php line:205
  $htmlformatted = strip_tags($_POST["message"]) != $_POST["message"];
        ↓
  $htmlformatted = false

public_html/lists/admin/send_core.php line:315-316
  if (!$htmlformatted  && strip_tags($_POST["message"]) !=  $_POST["message"])
    $errormsg = '<span  class="error">'.$GLOBALS['I18N']->get("htmlusedwarning").'</span>';
        ↑2行ともコメントアウト

なお、当社ではこの修正は使っていないため、無責任な動作未確認品です。

5. ヘッダが本文にまで飛び出ないようにする

メール本文にメールのヘッダ情報が含まれてしまう、という事象がありました。
Google さんに尋ねてみると、下記を見つけました。

RFC 的にどうなんだろうと思いつつ、下記の修正を適用しました。

  1. phplist-2.10.19-header-lf.patch
--- phplist-2.10.19/public_html/lists/admin/phpmailer/class.phpmailer.php.orig	2013-04-18 18:42:31.834738423 +0900
+++ phplist-2.10.19/public_html/lists/admin/phpmailer/class.phpmailer.php	2013-04-18 18:43:34.381341705 +0900
@@ -1155,7 +1155,7 @@
      * @return string
      */
     function EncodeHeader ($str, $position = 'text') {
-      $str = mb_encode_mimeheader($str, $this->CharSet, 'B');
+      $str = mb_encode_mimeheader($str, $this->CharSet, 'B', "\n");
 
       $x = 0;
 

6. unsubscribe リンクを非表示

[UNSUBSCRIBE] は、全てのニュースレターの配信を一括して停止します。
ニュースレターごとに配信停止をお願いしたいため、[UNSUBSCRIBE] リンクを非表示にしました。

--- phplist-2.10.17.orig/public_html/lists/index.php	2011-09-20 23:35:49.000000000 +0900
+++ phplist-2.10.17/public_html/lists/index.php	2012-03-09 15:41:06.107111589 +0900
@@ -266,7 +266,6 @@
     printf('<p><a href="./?p=subscribe">%s</a></p>',$strSubscribeTitle);
   }
 
-  printf('<p><a href="./?p=unsubscribe">%s</a></p>',$strUnsubscribeTitle);
   print $PoweredBy;
   print $data["footer"];
 }
@@ -296,8 +295,7 @@
   } else {
     $html .= '<input type=submit name="forgotpassword" value="'.$GLOBALS["strForgotPassword"].'">';
   }
-  $html .= '<br/><br/>
-    <p><a href="'.getConfig("unsubscribeurl").'&id='.$id.'">'.$GLOBALS["strUnsubscribe"].'</a></p>';
+  $html .= '<br/><br/>';
   $html .= '</form>'.$GLOBALS["PoweredBy"];
   $html .= $data["footer"];
   return $html;
@@ -322,8 +320,7 @@
   $html .= '<tr><td>'.$GLOBALS["strEmail"].'</td><td><input type=text name="email" value="'.$email.'" size="30"></td></tr>';
   $html .= '</table>';
    $html .= '<p><input type=submit name="sendpersonallocation" value="'.$GLOBALS["strContinue"].'"></p>';
-  $html .= '<br/><br/>
-    <p><a href="'.getConfig("unsubscribeurl").'&id='.$id.'">'.$GLOBALS["strUnsubscribe"].'</a></p>';
+  $html .= '<br/><br/>';
   $html .= '</form>'.$GLOBALS["PoweredBy"];
   $html .= $data["footer"];
   return $html;
@@ -395,7 +392,6 @@
 
   $html .= '<p><input type=submit name="update" value="'.$GLOBALS["strUpdatePreferences"].'" onClick="return checkform();"></p>
     </form><br/><br/>
-    <p><a href="'.getConfig("unsubscribeurl").'&id='.$id.'">'.$GLOBALS["strUnsubscribe"].'</a></p>
   '.$GLOBALS["PoweredBy"];
   $html .= $data["footer"];
   return $html;
@@ -519,7 +515,6 @@
     $html .= '<div style="display:none"><input type="text" name="VerificationCodeX" value="" size="20"></div>';
   $html .= '<p><input type=submit name="subscribe" value="'.$data["button"].'" onClick="return checkform();"></p>
     </form><br/><br/>
-    <p><a href="'.getConfig("unsubscribeurl").'&id='.$id.'">'.$GLOBALS["strUnsubscribe"].'</a></p>
   '.$GLOBALS["PoweredBy"];
   $html .= $data["footer"];
   return $html;

7. メール文末の「powered by phpList」を非表示

本家phpListでは、配信するメール文末に「powered by phpList」と必ず追記されます。メールを受信する大多数の方には意味不明なので、あえて非表示としました。
ソース内では「signature はなるべく残して欲しい」と表明されているので、大変心苦しい判断だったのですが、ニュースレターの購読申込・中止等を行うウェブフォームには、敬意を表してバナーとリンクを貼っています。また、この記事自体も phpList への謝意の表明として書いているつもりです。

X-Mailer: phplist ヘッダは残してあります。

--- phplist-2.10.17.orig/public_html/lists/admin/sendemaillib.php	2011-09-20 23:35:49.000000000 +0900
+++ phplist-2.10.17/public_html/lists/admin/sendemaillib.php	2011-10-27 15:57:19.060007553 +0900
@@ -299,20 +299,12 @@
     $htmlmessage = addHTMLFooter($htmlmessage,'<br /><br />'.$html["footer"]);
   if (eregi("\[SIGNATURE\]",$htmlmessage))
     $htmlmessage = eregi_replace("\[SIGNATURE\]",$html["signature"],$htmlmessage);
-  elseif ($html["signature"])
-# BUGFIX 0015303, 2/2
-//    $htmlmessage .= '<br />'.$html["signature"];
-      $htmlmessage = addHTMLFooter($htmlmessage, '
-'. $html["signature"]);
-# END BUGFIX 0015303, 2/2
   if (eregi("\[FOOTER\]",$textmessage))
     $textmessage = eregi_replace("\[FOOTER\]",$text["footer"],$textmessage);
   else
     $textmessage .= "\n\n".$text["footer"];
   if (eregi("\[SIGNATURE\]",$textmessage))
     $textmessage = eregi_replace("\[SIGNATURE\]",$text["signature"],$textmessage);
-  else
-    $textmessage .= "\n".$text["signature"];
 
 #  $req = Sql_Query(sprintf('select filename,data from %s where template = %d',
 #    $GLOBALS["tables"]["templateimage"],$cached[$messageid]["templateid"]));

8. そして続く

このように、メールを送信してみて不都合があったところを修正して、運用開始に至りました。まだ使わずに眠ったままの機能があります。まだまだ、いじり甲斐がありそうです。

この情報がお役に立てれば幸いです。また、phpList の開発関係者の皆様に感謝いたします。

参考文献

(yone)

【イベント】CORONA 01

Viewing all 263 articles
Browse latest View live