|
16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"JpegのCMYKへの未対応バグ修正"
DelphiのJpegユニットはIJGのライブラリを利用して、Jpegの圧縮・伸張を行って
います。世に出まわっているJpegのほとんどはJFIFフォーマットと言われるもので
このフォーマットについては何らの問題もありません。が、Adobe Photoshop等で
作成することのできる、いわゆるCMYK-Jpegについては対応されていません(正常
に読込めない)。
単に対応していない(つまりエラーとなる)のであれば問題がないのですが、IJGラ
イブラリが曲がりなりにも対応していて、それを伸張しようとするのに対して、Jpeg
ユニットでは、それに対して何らの考慮もしていないところが問題です。
この結果、これらのJpegを読込もうとすると、壊れたテレビのような画像になって
しまいます(何と表現したら良いのか?やってみてください)。画像だけがおかしく
なるのならともかく、処理の過程でアロケートされていないメモリへの書き込みが起
こります。
なぜなら、CMYKでは1ピクセルあたり4バイトであるのに対して、Jpegユニットでは
常にRGBの値が来ると信じているので1ピクセルあたり3バイトのバッファしか提供
していません。
暫定的ですが、この修正は以下のようにします。TJPEGImage.GetBitmapメソッドに
以下の二つのプロシージャを挿入します。
function TJPEGImage.GetBitmap: TBitmap;
var
LinesPerCall, LinesRead: Integer;
DestScanLine: Pointer;
PtrInc: Integer;
jc: TJPEGContext;
GeneratePalette: Boolean;
// ここから---------------------------------------------------------------->
procedure CMYK2RGB(C, M, Y, K : byte; var R, G, B : byte);
begin
if (Integer(C) + Integer(K)) < 255 then
R := 255 - (C + K) else
R := 0;
if (Integer(M) + Integer(K)) < 255 then
G := 255 - (M + K) else
G := 0;
if (Integer(Y) + Integer(K)) < 255 then
B := 255 - (Y + K) else
B := 0;
end;
procedure getScanLines;
var
TempLine: Pointer;
Col: Integer;
begin
if jc.d.out_color_space = JCS_CMYK then
begin
TempLine := AllocMem(jc.d.output_width * 4);
while (jc.d.output_scanline < jc.d.output_height) do
begin
LinesRead := jpeg_read_scanlines(jc.d, @TempLine, LinesPerCall);
// CMYK --> RGB
for Col := 0 to jc.d.output_width - 1 do
begin
CMYK2RGB(
255 - JSAMPROW(TempLine)^[Col * 4 + 0],
255 - JSAMPROW(TempLine)^[Col * 4 + 1],
255 - JSAMPROW(TempLine)^[Col * 4 + 2],
255 - JSAMPROW(TempLine)^[Col * 4 + 3],
JSAMPROW(DestScanLine)^[Col * 3 + 2], // B, G, Rの順
JSAMPROW(DestScanLine)^[Col * 3 + 1],
JSAMPROW(DestScanLine)^[Col * 3 + 0]);
end;
Inc(Integer(DestScanline), PtrInc * LinesRead);
end;
FreeMem(Templine);
end
else
begin
while (jc.d.output_scanline < jc.d.output_height) do
begin
LinesRead := jpeg_read_scanlines(jc.d, @DestScanline, LinesPerCall);
Inc(Integer(DestScanline), PtrInc * LinesRead);
end;
end;
end;
//---------------------------------------------------------------->ここまで
begin
Result := FBitmap;
....
そして、同じくTJPEGBitmap.GetBitmapメソッドの中の一部を以下のように書き換え
ます。
// final image pass for progressive, first and only pass for baseline
{ 消去-------------------------------------------------------------->
while (jc.d.output_scanline < jc.d.output_height) do
begin
LinesRead := jpeg_read_scanlines(jc.d, @DestScanline, LinesPerCall);
Inc(Integer(DestScanline), PtrInc * LinesRead);
end;
---------------------> 以上をやめて、GetScanLinesを呼び出すようにする}
getScanLines; //<--挿入
if jc.d.buffered_image then jpeg_finish_output(jc.d);
jpeg_finish_decompress(jc.d);
これで、一応はCMYKのJpegを読込むことができます。ただし、以下のような制限があ
ります。
1. TJpegImageのプロパティはデフォルト値のままでなければならない。
つまり、GrayScale := False, PixelFormat := jf24bit です。
2. CMYK画像の色合いを再現できない。
これはCMYK色空間の特性によるものです。教科書通りであれば、上記のプロシージャ
CMYK2RGBで良いはずなのですが、もちろんこんなに簡単であれば何もわざわざRGBを
使わずにCMYKを使う必要は全然ありません。
(ただし、JFIFの場合でもRGBではなくYCbCrという色空間を使ってデータが保存され
ています。IJGライブラリではYCbCrを自動的にRGBに変換してくれます)。
私もよくわかっていないのですが、かみ砕いて説明すると、Photoshopの場合、CMYK
の値というのは、そのときに指定されている印刷インクの量を示しているものらし
いのです。例えば「大日本印刷インキで印刷するよ」という場合には、そのインクで
赤を表現するためにはどんな調合をすればよいのか、という値が格納されているよ
うです。
したがって、これらのCMYKの値から元の画像を再現させるためには、Photoshopで
指定されているインクのプロファイルが必要であり、なおかつインクというのは光
のように線形に強さが増すものではないので、非線形の演算を施してやらないと
いけないらしいです。
98/10/20(火) ytm PAF03212@niftyserve.or.jp
Original document by ytm 氏 ID:(PAF03212)
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。
Copyright 1996-2002 Delphi Users' Forum
|