2

画像の上にテキストを描画する必要がある Java アプリケーションがあります。テキスト、フォント、画像はすべて実行時に決定されます。テキストは見栄えがよく、画像の上で読みやすい (十分にコントラストが強い) 必要があります。

これらの要件を満たすために、ドロップ シャドウを作成します。これは、空白/透明な BufferedImage に不透明な黒でテキストを描画し、ガウスぼかしフィルターを適用することによって行われます。次に、ドロップ シャドウの上に不透明な白でテキストを再度描画します。そのため、不透明な白いテキストがあり、その周りに黒いぼやけた影があり、すぐに完全に透明になります。次に、この画像を背景画像の上に描画できます。

私が解決しようとしている問題は、ドロップ シャドウが透明すぎるように見えることです。そのため、明るく賑やかな背景に対しては、白いテキストが十分に分離されません。

では、影の不透明度を上げるにはどうすればよいでしょうか。ガウスぼかしの半径を大きくしてみました。これにより、影が広くなりますが、不透明にはなりません。

私が使用しているコードは、Romain Guy によるこの DropShadowDemoに基づいています。彼の createDropShadow() と gaussianBlurFilter() を使用します。ただし、ドロップ シャドウとテキストを別々にpaintComponent()描画する代わりに、事前に両方を BufferedImage に描画します。そして、この単一の BufferedImage を背景の上に描画しpaintComponent()ます. 多分それは私の問題ですか?しかし、それが影の不透明度をどのように減少させるかわかりません。g2.setComposite()中は使用していませんpaintComponent()

LookupOp などのある種のBufferedImageOpを使用して、ドロップ シャドウの不透明度を調整することを検討しました。しかし、単純な調整 (数値の 4 つの配列を作成することだと思います) には多くの作業が必要なようです。結果のアルファをソースのアルファと同じ範囲 (0 から 1) に収めたいので、RescaleOp は機能しないと思います。new alpha = sqrt(old alpha) などを設定する BufferedImageOp を指定できれば理想的です。しかし、私はそれを行う簡単な方法を知りません。

コードの詳細は次の場所で確認できます。

  • ShadowedText.java (ドロップ シャドウ付きのテキスト イメージを作成します)
  • SetBeforeMe.java (画像を描画する paintComponent() を実装)

関連するコード ブロックをここに含めますが、関連するコードの量が大きすぎるようです (コードの壁)... ソース ファイルへのリンクを提供するだけでもかまいません。

アップデート

BufferedImage のアルファ値を変更しますか? ドロップシャドウの不透明度を変更する方法です...基本的に、各ピクセルのアルファ値を1つずつ再計算します。TBD: 移植可能かどうか (64 ビット マシンなど)、および十分に高速かどうか。a = sqrt(a)またはa = sin(a * pi * 0.5)すべてのピクセルで (0 から 1 の範囲で考えて)実行aすると、遅くなりますか? 組み込みの BufferedImageOps がおそらく行うように、利用可能な最適化を利用するより簡単な方法があるかどうかを知りたいです。結局、答えは LookupOp の配列を構築することかもしれません。そのためのサンプルコードを知っている人はいますか?

最終更新

LookupOp を使用して解決しました。以下のコードを参照してください。

4

1 に答える 1

0

以下は、BufferedImage をより不透明にするために最終的に作成したコードです。各ピクセルの getRGB / setRGB の潜在的に移植性がなく遅いループではなく、先に進んで LookupOp を使用することにしました。ルックアップ配列をセットアップする作業はそれほど悪くはありませんでした。

/* Make alpha channel more opaque.
 * Modify the alpha (opacity) channel so that values are higher, but still
 * continuous and monotonically increasing.
 */
private static void adjustOpacity(BufferedImage shadowImage) {
    // Use a lookup table with four arrays;
    // the three for RGB are identity arrays (no change).
    byte identityArray[] = new byte[256];
    for (int i=0; i < 256; i++)
        identityArray[i] = (byte)i;

    byte alphaArray[] = new byte[256];
    // map the range (0..256] to (0..pi/2]
    double mapTo90Deg = Math.PI / 2.0 / 256.0;
    for (int i=0; i < 256; i++) {
        alphaArray[i] = (byte)(Math.sin(i * mapTo90Deg) * 256);
    }

    byte[][] tables = {
            identityArray,
            identityArray,
            identityArray,
            alphaArray
    };
    ByteLookupTable blut = new ByteLookupTable(0, tables);
    LookupOp op = new LookupOp(blut, null);

    // With LookupOp, it's ok for src and dest to be the same image.
    op.filter(shadowImage,  shadowImage);
}

動作しているようです (ただし、比較のために前後のスクリーンショットを撮っていません)。

于 2012-04-11T07:07:51.873 に答える