ダミーテキスト

Webページのレイアウトを考えるとき、FireWorksでページのレイアウトデザインを作成して、それを元にパーツ画像・ボタン類などを作成、あとはDreamWeaverなり、テキストエディタなりでHTMLとCSSを組んでいく・・・という流れがごくごく一般的だと思います。
 

デザインだけで内容が全く決まっていない、というとき、テキスト部分には、たとえば、「ダミーテキスト」などの仮のテキストの羅列を入れることが多いのですが、やはり同じ内容のテキストの羅列だと、完成したときのイメージが湧かない、と言われたこともありました。
 

FireWorksで、ダミーテキスト生成して挿入する下のようなコマンドを作成して、%APPDATA%\Adobe\FireWorks CS4\Commands へ放り込んで使っています。やっていることは至極単純で、テキストファイルの各行を配列に読み込んで、乱数で得られた数字を使って数回、配列から読み出しているだけ。
 

問題は、それに使用するテキストファイルの内容。ある程度日本語として文法が正しく、かつ、それ文章自体は意味をなさない・・・という、とにかく訳のわからないテキストが理想です(笑)
ネットから勝手に取って使用すると「著作権が・・・」とかあると思うので、パブリックドメインのテキストがないか調べて、僕は3つのテキストを選択しました。パブリックドメインなので、どう使おうと自由、という非常にありがたいものです。。。
 

僕が選んだのは、聖書の英語訳と日本語訳。それと、平家物語の原文。もちろん全文読み込んだりすると処理に非常に時間がかかるので適当に抜き出し、加工を加えました。

ダミーテキストのソースファイル

でも、これだけだと不満があって、英文と日本文が適度に混じったテキストがないか探索中です。手っ取り早く、技術文書などの専門書から取るのがいいのですが・・・なかなかパブリックドメインのものがねぇ~、ないんですよねぇ~。
 

ダミーテキストとしての僕の理想は、日本語と英語が適度に混ざって、かつ、カタカナもある程度混じって、なおかつ、意味が分からないテキスト(笑) そんなのないですかねぇ~・・・。
 

/************************************************************************
 ダミーテキスト挿入.jsf for FireWorks CS4
 %APPDATA%\Adobe\FireWorks CS4\Commands へ入れとく。
 
 ファイルが本当にテキストファイルかどうかのチェックはしていない。
*************************************************************************/
var num   = 5;
var times = 1;

insertDummyText = function(n,t)
{
  var file = fw.browseForFileURL("open","ダミーテキストファイルを開く");

  if(file == null)
	  throw new Error("ファイルが指定されていません");

	var fin = Files.open(file);
  var lines = [];
  var line_ = null;
  var out = [];

  while((line_ = fin.readline()) != null)
    {
      if(line_.match(/^\s*$/))
        continue;

      lines.push(line_);
    }

  fin.close();

  for(var i = 0;i < t;i++)
    {
      for(var j = 0;j < n;j++)
        {
          var index = parseInt(Math.random() * lines.length);
          out.push(lines[index]);
        }
      if(i < t - 1)
        out.push("\n\n");
    }

  var dom = fw.getDocumentDOM();

  dom.setTextAutoExpand(false);
  
  if(typeof fw.selection[0] == "undefined")
    dom.addNewText({top:5,right:dom.width/2,bottom:120,left: 5},true);

  var runs = fw.selection[0].textRuns;

  runs.textRuns = [{changedAttrs: {}, characters: out.join('')}];
  fw.selection[0].textRuns = runs;
}

try{insertDummyText(num,times);}catch(e){alert(e.message);}

GDで角丸サムネール画像の生成~その2~

GDで角丸サムネール画像の生成の続きです。
 
 

前回のコードだと、円の方程式から得られた判定に対して単に背景色の点を打つ、打たないだけだったので、角丸部分がガタガタになってしまい、非常に見苦しいものでした。それを隠すために(笑)元画像にガタガタの角丸を適用して元画像をリサンプルして誤魔化していました。
 

それでは、あまりにも情けない・・・ということで、アンチエイリアス処理を施そう・・・ということで、アンチエイリアスのアルゴリズムをネットで検索して調べましたが・・・アルゴリズム自体の解説が少なく、ドキュメントを見つけても、高校・大学時代にやったような、複雑な数式のオンパレードで・・・頭がクラクラ(ーー;;;
 

目眩を起こしつつザラッと流し読みしたけど・・・ドキュメントが英語なので・・・ほとんど理解できん・・・ああ、柔らか頭に戻りたい。。。
 

前置きが長くなりましたが・・・これは、もう、思いつきで行きあたりバッタリで実装するしかない!というわけで・・・
 

「なんちゃってアンチエイリアス処理」を書いてみました。
あくまで、なんちゃってなので、ガタガタより、まぁマシかな・・・というレベルです。
 

角丸

角丸部分にする上下左右の半径Rの1/4円弧を囲んだ正方形部分のピクセルをおのおのスキャンして円の方程式によりピクセル色を決定するところまでは前回と同じです。
変えたところは、判定部分で、ピクセル座標(x,y)の点において、xのときの円周上の点を求め、それとの差(delta)が、0 ≦ delta ≦ 1のとき、
若干薄く描画。その差が1以上のとき、0以下のときで、点を打つ・何もしないを決定します。実際には、↓の setPixelサブルーチンのようにしています。
忠実にアンチエイリアス処理をしている訳ではなく、0 ≦ delta ≦ 1 部分の描画色(描画色というか、背景色の透明度(α)を単に変化させています)の決定は、かなり適当です。「なんちゃってアンチエイリアス」たる所以です(^^;;;

#
# perl script
#

#
#簡易アンチエイリアス処理をする点(ピクセル)の描画サブルーチン
#
#$thisはGD::Imageを継承したオブジェクト,$rは角丸の半径ピクセル、$x,$yはピクセル位置、
#$yfは$xのときの円周上の実際のy座標(実数でピクセル位置ではない),
#$colorは背景色,$bは角丸が画像の上部分(1)か、下部分(0)か。

our $PARAM = 0.75;
our $RANGE = 127;
our $NEAREST_INNER = 120;
our $NEAREST_OUTER = 15;

sub setPixel
{
  my ($this,$r,$x,$y,$yf,$color,$b) = @_;
  my $retVal = 1;
  my $delta = ($b ? $yf - $y : $y - $yf) * ($b ? $r - $y : $r - $this->height + $y) / ($r * $PARAM);
  
  if($delta >= 0 && $delta <= 1)
    {
      $delta = 0.1 if($delta == 0);
      #ほぼ円周上
      my $range = $RANGE - round($delta*$RANGE);
      my $alphacolor = $this->colorAllocateAlpha($this->rgb($color),$range);

      $this->GD::Image::setPixel($x,$y,$alphacolor);
    }
  elsif($delta > 1)
    {
      #円外
      $delta < 2 ? $this->GD::Image::setPixel($x,$y,$this->colorAllocateAlpha($this->rgb($color),$NEAREST_OUTER)) : $this->GD::Image::setPixel($x,$y,$color);
    }
  elsif($delta < 0)
    {
      #円内
      $this->GD::Image::setPixel($x,$y,$this->colorAllocateAlpha($this->rgb($color),$NEAREST_INNER)) if($delta > -1);
      $retVal = 0;
    }

  $retVal;
}

#
#角丸部分のピクセル値をスキャンする。実際の描画は、上記のsetPixelを使う。
#
# $this は、GD::Imageを継承したオブジェクト 、$radiusは円弧の半径ピクセル
sub applyRoundCorner
{
  my ($this,$radius,$color) = @_;
  my ($x,$y) = (0,0);
  my ($width,$height) = ($this->width-1,$this->height-1);
  my $diameter = 2 * $radius;
  my $limit = $radius;
  $this->alphaBlending(1);
  
  if($diameter > $width || $diameter > $height)
    {
      print "Radius of round corner must be less than width or height...\n";
      print "Process can not apply round corner...\n";
      return;
    }

  #画像の上部分の角丸
  for($y = 0;$y < $limit;$y++)
    {
      #左
      for($x = 0;$x <= $radius;$x++)
        {
          my $yf = abs(sqrt(abs($radius**2 - ($x - $radius)**2)) - $radius);
          last unless($this->setPixel($radius,$x,$y,$yf,$color,1));
        }
      #右
      for($x = $width;$x >= $width - $radius;$x--)
        {
          my $yf = abs(sqrt(abs($radius**2 - ($x - ($width - $radius))**2)) - $radius);
          last unless($this->setPixel($radius,$x,$y,$yf,$color,1));
        }
    }

  #画像の下部分の角丸
  $limit = $height + 1 - $radius;
  for($y = $height;$y > $limit;$y--)
    {
      #左
      for($x = 0;$x <= $radius;$x++)
        {
          my $yf = abs(sqrt($radius**2 - ($x - $radius)**2) + ($height - $radius));
          last unless($this->setPixel($radius,$x,$y,$yf,$color));
        }
      #右
      for($x = $width;$x >= $width - $radius;$x--)
        {
          my $yf = abs(sqrt($radius**2 - ($x - ($width - $radius))**2) + ($height - $radius));
          last unless($this->setPixel($radius,$x,$y,$yf,$color));
        }
    }
}

というわけで、自作のサムネール作成 perlモジュールに組み込んで、実際に角丸サムネールを作成してみました。
  

拡大すると・・・

こんな感じ。
アンチエイリアスしている風に見えるでしょう。。。あああ。

アンチエイリアスがかかった角丸が簡単にできるライブラリ・・・ないもんでしょうかねぇ・・・。
 

忘れてたけどImagickってのもありましたねぇ~、多機能すぎて、一度挫折した覚えが・・・。

GDで角丸サムネール画像の生成

公開・表示したい画像がいっぱいあるとき、サムネール画像を作るのは非常に手間です。
いちいち一つ一つ画像編集する人は・・・まぁ、いないでしょうね・・・(^^;

以前に、CGIでも使えるようにGDを使用してサムネール画像生成のPerlモジュールを作成していました。

で、今回、そのサムネール画像に角丸を適用したものを生成できるように、機能を付け足すことにしたんですが・・・、
画像に角丸を適用するスマートな方法がわからず、考えたあげく、少々チカラワザ的にインプリメントしてしまいました。

四隅のピクセル値をスキャンして、円弧外のピクセル値は背景色に、円弧内のピクセル値はそのままに・・・という感じです。

ピクセル値の判定には、円の方程式、X2 + Y2 = Radius2 を使用しています。

#
# perl script
#
# $this は、GD::Image 、$radiusは円弧の半径ピクセル
sub applyRoundCorner
{
  my ($this,$radius,$color) = @_;
  my ($x,$y) = (0,0);
  my ($width,$height) = ($this->width-1,$this->height-1);
  $this->alphaBlending(1);
  my ($lhs,$rhs) = (0,$radius**2);

  #上
  for($y = 0;$y <= $radius;$y++)
    {
      #左
      for($x = 0;$x <= $radius;$x++)
        {
          $lhs = ($x - $radius)**2 + ($y - $radius)**2;
          last unless(setPixel($this,$radius,$x,$y,$color,$lhs,$rhs));
        }
      #右
      for($x = $width;$x >= $width - $radius;$x--)
        {
          $lhs = ($x - ($width - $radius))**2 + ($y - $radius)**2;
          last unless(setPixel($this,$radius,$x,$y,$color,$lhs,$rhs));
        }
    }

  #下
  for($y = $height;$y >= $height-$radius;$y--)
    {
      #左
      for($x = 0;$x <= $radius;$x++)
        {
          $lhs = ($x - $radius)**2 + ($y - ($height - $radius))**2;
          last unless(setPixel($this,$radius,$x,$y,$color,$lhs,$rhs));
        }
      #右
      for($x = $width;$x >= $width - $radius;$x--)
        {
          $lhs = ($x - ($width - $radius))**2 + ($y - ($height - $radius))**2;
          last unless(setPixel($this,$radius,$x,$y,$color,$lhs,$rhs));
        }
    }
}

#
# ピクセル(x,y)にカラー値をセットする。
#
sub setPixel
{
  my ($this,$radius,$x,$y,$color,$lhs,$rhs) = @_;
  my $ret = 1;
 
  if($lhs > $rhs)
    {
      $this->setPixel($x,$y,$color);
    }
  else
    {
      $ret = 0;
    }

  return $ret;
}

ただ、サムネール画像にこれをそのまま適用すると、当然ですがアンチエリアス処理がされないので角丸部分がガタガタになってしまいます。
そこで、サムネール画像に角丸を適用するのではなく、メモリ上の元画像に角丸を適用し、GD::ImageのcopyResampledでサムネール画像を生成(縮小)することでアンチエリアス処理の替わりとすることにしました。

他の方法としては、角丸部分をアルファ付きのPNGファイルを用いて、四隅にコピーするという方法があって、そっちの方が簡単かな? とは思うのですが・・・

根本的に改善するには、やはり画像処理のアルゴリズム(主にアンチエリアス処理なんですが・・・)について勉強しなければいけませんねぇ・・・。

他の方が作ったライブラリを探した方がいいかも・・・(^^;;;

CommandLineToArgvAってないの???

ちょっとした小さなツールをC++で組むとき、CRTは使わないときはできるだけCRTをリンクしないようにしたいわけです。

でも、エントリポイントに WinMainとかしてしまうと、コマンドライン引数をうまくハンドリングできなくて悩む。#define UNICODE とかして、UNICODEにすると、CommandLineToArgvWというAPIがあるので、簡単に、argc(int) と argv(char**)がとれてラクができますが・・・。

なぜか・・・ANSIバージョンの CommandLineToArgvA がないのはなんでなんでしょうかねぇ・・・。
というわけで、解決方法は3つ。

  1. そもそもANSI文字列を使わない。
  2. 自分でコマンドライン文字列(GetCommandLine API)パーサーを書く
  3. CommandLineToArgvWで得られた引数リストをANSI文字列に変換する

一番ラクそうなのは3かな・・・ということで、やっつけで書いてみましたが・・・これでいいのかな・・・(^^;;;

こんなんでエエのかな・・・。。。


CommandLineToArgvA その2

コマンドプロンプトのログを取る(その3)

コマンドプロンプトのログを取る(その2)
コンソール(CMD.EXE)のログを取る (不完全版)
の続きです。

その2で、僕が望んだ動作はほぼ達成できました。だいぶ不完全だけど・・・。
その2の不満点は、CTRL-Cを押すとcmdlog.exe自体がガサッと落ちてしまうんです(子プロセス諸とも終了してしまう)。やはり、CTRL-Cを押すと、子プロセス側で走っているコンソールプロセスだけ死んで欲しいのは当然ですよねぇ。
ってことで、こういう場合は、親プロセス(cmdlog.exe)がCTRL-Cを受け取ったら、親プロセス側は何もせず、CTRL-Cイベント(シグナルかな?)をそのまま子プロセスに渡してしまうのがお約束かとおもいます。

で、そのまんまのSetConsoleCtrlHandlerというAPIがありますので、これを使います。

このAPIは名前のとおり、コンソールプロセスでCtrl-CとかCtrl-Homeを受け取ったときに呼び出される関数をセットできるもので、既存のハンドラルーチン(関数)を追加、削除ができます。
なわけで、

/*CTRLハンドラ*/
BOOL WINAPI CtrlHandler(DWORD dwCtrlType)
{
  BOOL bRetVal = FALSE;

  if(dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT)
    bRetVal = GenerateConsoleCtrlEvent(dwCtrlType,Context::ProcessInformation.dwProcessId);
  
  return bRetVal;
}

のようなシグナルハンドラ(っていうのかな?)を追加すれば、Ctrl-Cを子プロセスに送って自分(親プロセス)は何もしないってことができます。良かった。パチパチ。

ってなわけで・・・

若干手直ししたソースはこちらから。。。(cmdlog03.zip)
全てのソースはこっちに移動

あとは・・・そうですね・・・いいかげん、コマンド引数からログファイル名を指定するようにせんといかんな・・・ハードコードなんてダサすぎる・・・(ーー;;;


コマンドプロンプトのログを取る(完結)
コマンドプロンプトのログを取る(その3)
コマンドプロンプトのログを取る (その2)
コンソール(CMD.EXE)のログを取る (不完全版)