15

元の質問(下記)から、私は現在、以下の報奨金を提供しています:

丸みを帯びた角のためのAlphaCompositeベースのソリューション。

  • でデモンストレーションしてくださいJPanel
  • コーナーは完全に透明でなければなりません。
  • JPGペイントをサポートできる必要がありますが、それでも角が丸い
  • setClip(またはクリッピング)を使用しないでください
  • まともなパフォーマンスが必要です

うまくいけば、誰かがこれをすばやく拾うのは簡単なようです。

他の人が同意する、これが決してできない理由がよく説明されている場合にも、私は賞金を授与します。

これが私が考えていることのサンプル画像です(しかし使用していますAlphaCompositeここに画像の説明を入力してください


元の質問

私は、Javaまたはhttp://weblogs.java.net/blog/campbell/archive/2006/07/で丸みを帯びた角の画像を作成する方法と非常によく似た、合成を使用して丸みを帯びた角を作成する方法を見つけようとしてきました。 java_2d_tricker.html

ただし、中間のBufferedImageを使用しない場合の試行は機能しません。丸められた宛先コンポジットは、ソースに影響を与えないようです。私はさまざまなことを試しましたが、何も機能しません。丸みを帯びた赤い長方形を取得する必要がありますが、代わりに正方形を取得しています。

だから、私は本当に2つの質問があります:

1)これを機能させる方法はありますか?

2)中間イメージは実際により良いパフォーマンスを生成しますか?

SSCCE:

テストパネルTPanel

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JLabel;

public class TPanel extends JLabel {
int w = 300;
int h = 200;

public TPanel() {
    setOpaque(false);
    setPreferredSize(new Dimension(w, h));
        setMaximumSize(new Dimension(w, h));
        setMinimumSize(new Dimension(w, h));
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();

    // Yellow is the clipped area.
    g2d.setColor(Color.yellow);
    g2d.fillRoundRect(0, 0, w, h, 20, 20);
    g2d.setComposite(AlphaComposite.Src);

    // Red simulates the image.
    g2d.setColor(Color.red);
    g2d.setComposite(AlphaComposite.SrcAtop);

    g2d.fillRect(0, 0, w, h);
    }
}

とそのサンドボックス

import java.awt.Dimension;
import java.awt.FlowLayout;

import javax.swing.JFrame;

public class Sandbox {
public static void main(String[] args) {
    JFrame f = new JFrame();
        f.setMinimumSize(new Dimension(800, 600));
        f.setLocationRelativeTo(null);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new FlowLayout());

        TPanel pnl = new TPanel();
        f.getContentPane().add(pnl);

        f.setVisible(true);
    }
}
4

2 に答える 2

7

パフォーマンスに関する懸念事項に関して、Java 2D Trickeryの記事には、中間画像の使用法に関するChetHaaseによる非常に優れた説明へのリンクが含まれています。

AlphaCompositeの動作と、中間イメージを使用するために必要な手法を理解するために 、O'ReillyのJava Foundation Classes inaNutshellからの次の抜粋が役立つと思います。

AlphaComposite合成ルール

SRC_OVER合成ルールは、デスティネーションカラーの上に半透明のソースカラーを描画します。これは、グラフィックス操作を実行するときに通常発生したいことです。ただし、AlphaCompositeオブジェクトでは、実際には他の7つのルールに従って色を組み合わせることができます。

合成ルールを詳細に検討する前に、理解しておく必要のある重要なポイントがあります。画面に表示される色にアルファチャネルが含まれることはありません。あなたが色を見ることができるならば、それは不透明な色です。正確な色の値は、透明度の計算に基づいて選択されている可能性がありますが、その色が選択されると、その色はビデオカードのメモリのどこかに存在し、それに関連付けられたアルファ値はありません。つまり、画面上の描画では、宛先ピクセルのアルファ値は常に1.0です。

ただし、画面外の画像に描画する場合は状況が異なります。この章の後半でJava2DBufferedImageクラスを検討するときにわかるように、オフスクリーン画像を作成するときに目的の色表現を指定できます。デフォルトでは、BufferedImageオブジェクトは画像をRGB色の配列として表しますが、ARGB色の配列である画像を作成することもできます。このような画像にはアルファ値が関連付けられており、画像に描画しても、アルファ値は描画したピクセルに関連付けられたままになります。

一部の合成ルールは、ソースピクセルのアルファ値ではなく、宛先ピクセルのアルファ値に基づいて合成を実行するため、画面上の描画と画面外の描画のこの区別は重要です。画面上の描画では、宛先ピクセルは常に不透明です(アルファ値は1.0)が、画面外の描画では、そうである必要はありません。したがって、一部の合成ルールは、アルファチャネルを持つオフスクリーン画像に描画する場合にのみ役立ちます。

少し一般化しすぎると、画面上で描画するときは、通常、デフォルトのSRC_OVER合成ルールを使用し、不透明な色を使用し、AlphaCompositeオブジェクトで使用されるアルファ値を変更すると言えます。ただし、アルファチャネルを持つオフスクリーン画像を操作する場合は、他の合成ルールを利用できます。この場合、通常、半透明の色と半透明の画像、およびアルファ値が1.0のAlphaCompositeオブジェクトを使用します。

于 2012-02-24T14:35:36.540 に答える
4

この問題を調べましたが、システムクラスへの1回の呼び出しでこれを行う方法がわかりません。

Graphics2Dは、SunGraphics2Dとして実装された抽象インスタンスです。ソースコードは、たとえばdocjarで入手できるため、コードをコピーすることで、「同じことを行うが、異なる」可能性があります。ただし、画像をペイントするメソッドは、使用できない一部の「パイプ」クラスに依存します。クラスローディングを使用して作業を行いますが、理論的に最適なアプローチを実行するために操作できない、ネイティブで最適化されたクラスにヒットする可能性があります。あなたが得るのは正方形としてのイメージペインティングだけです。

ただし、独自の非ネイティブ(read:slow?)のコードをできるだけ実行せず、画像サイズに依存せず、丸い長方形の(比較的)低い領域に依存するアプローチを作成できます。また、メモリ内の画像をコピーせずに、大量のメモリを消費します。ただし、メモリが多い場合は、明らかに、インスタンスが作成された後、キャッシュされたイメージの方が高速です。

代替案1:

import java.awt.Composite;
import java.awt.CompositeContext;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import javax.swing.JLabel;

public class TPanel2 extends JLabel implements Composite, CompositeContext {
private int w = 300;
private int h = 200;

private int cornerRadius = 20;
private int[] roundRect; // first quadrant
private BufferedImage image;
private int[][] first = new int[cornerRadius][];
private int[][] second = new int[cornerRadius][];
private int[][] third = new int[cornerRadius][];
private int[][] forth = new int[cornerRadius][];

public TPanel2() {
    setOpaque(false);
    setPreferredSize(new Dimension(w, h));
    setMaximumSize(new Dimension(w, h));
    setMinimumSize(new Dimension(w, h));

    // calculate round rect     
    roundRect = new int[cornerRadius];
    for(int i = 0; i < roundRect.length; i++) {
        roundRect[i] = (int)(Math.cos(Math.asin(1 - ((double)i)/20))*20); // x for y
    }

    image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); // all black
}

@Override
public void paintComponent(Graphics g) {
    // discussion:
    // We have to work with the passed Graphics object.

    if(g instanceof Graphics2D) {

        Graphics2D g2d = (Graphics2D) g;

        // draw the whole image and save the corners
        g2d.setComposite(this);
        g2d.drawImage(image, 0, 0, null);
    } else {
        super.paintComponent(g);
    }
}

@Override
public CompositeContext createContext(ColorModel srcColorModel,
        ColorModel dstColorModel, RenderingHints hints) {
    return this;
}

@Override
public void dispose() {

}

@Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
    // lets assume image pixels >> round rect pixels
    // lets also assume bulk operations are optimized

    // copy current pixels
    for(int i = 0; i < cornerRadius; i++) {
        // quadrants

        // from top to buttom
        // first
        first[i] = (int[]) dstOut.getDataElements(src.getWidth() - (cornerRadius - roundRect[i]), i, cornerRadius - roundRect[i], 1, first[i]);

        // second
        second[i] = (int[]) dstOut.getDataElements(0, i, cornerRadius - roundRect[i], 1, second[i]);

        // from buttom to top
        // third
        third[i] = (int[]) dstOut.getDataElements(0, src.getHeight() - i - 1, cornerRadius - roundRect[i], 1, third[i]);

        // forth
        forth[i] = (int[]) dstOut.getDataElements(src.getWidth() - cornerRadius + roundRect[i], src.getHeight() - i - 1, cornerRadius - roundRect[i], 1, forth[i]);
    }

    // overwrite entire image as a square
    dstOut.setRect(src);

    // copy previous pixels back in corners
    for(int i = 0; i < cornerRadius; i++) {
        // first
        dstOut.setDataElements(src.getWidth() - cornerRadius + roundRect[i], i, first[i].length, 1, second[i]);

        // second
        dstOut.setDataElements(0, i, second[i].length, 1, second[i]);

        // third
        dstOut.setDataElements(0, src.getHeight() - i - 1, third[i].length, 1, third[i]);

        // forth
        dstOut.setDataElements(src.getWidth() - cornerRadius + roundRect[i], src.getHeight() - i - 1, forth[i].length, 1, forth[i]);
    }
}

}

代替案2:

import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.CompositeContext;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import javax.swing.JLabel;

public class TPanel extends JLabel implements Composite, CompositeContext {
private int w = 300;
private int h = 200;

private int cornerRadius = 20;
private int[] roundRect; // first quadrant
private BufferedImage image;

private boolean initialized = false;
private int[][] first = new int[cornerRadius][];
private int[][] second = new int[cornerRadius][];
private int[][] third = new int[cornerRadius][];
private int[][] forth = new int[cornerRadius][];

public TPanel() {
    setOpaque(false);
    setPreferredSize(new Dimension(w, h));
    setMaximumSize(new Dimension(w, h));
    setMinimumSize(new Dimension(w, h));

    // calculate round rect     
    roundRect = new int[cornerRadius];
    for(int i = 0; i < roundRect.length; i++) {
        roundRect[i] = (int)(Math.cos(Math.asin(1 - ((double)i)/20))*20); // x for y
    }

    image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); // all black
}

@Override
public void paintComponent(Graphics g) {
    if(g instanceof Graphics2D) {

        Graphics2D g2d = (Graphics2D) g;

        // draw 1 + 2 rectangles and copy pixels from image. could also be 1 rectangle + 4 edges
        g2d.setComposite(AlphaComposite.Src);

        g2d.drawImage(image, cornerRadius, 0, image.getWidth() - cornerRadius - cornerRadius, image.getHeight(), null);
        g2d.drawImage(image, 0, cornerRadius, cornerRadius, image.getHeight() - cornerRadius - cornerRadius, null);
        g2d.drawImage(image, image.getWidth() - cornerRadius, cornerRadius, image.getWidth(), image.getHeight() - cornerRadius, image.getWidth() - cornerRadius, cornerRadius, image.getWidth(), image.getHeight() - cornerRadius, null);

        // draw the corners using our own logic
        g2d.setComposite(this);

        g2d.drawImage(image, 0, 0, null);

    } else {
        super.paintComponent(g);
    }
}

@Override
public CompositeContext createContext(ColorModel srcColorModel,
        ColorModel dstColorModel, RenderingHints hints) {
    return this;
}

@Override
public void dispose() {

}

@Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
    // assume only corners need painting

    if(!initialized) {
        // copy image pixels
        for(int i = 0; i < cornerRadius; i++) {
            // quadrants

            // from top to buttom
            // first
            first[i] = (int[]) src.getDataElements(src.getWidth() - cornerRadius, i, roundRect[i], 1, first[i]);

            // second
            second[i] = (int[]) src.getDataElements(cornerRadius - roundRect[i], i, roundRect[i], 1, second[i]);

            // from buttom to top
            // third
            third[i] = (int[]) src.getDataElements(cornerRadius - roundRect[i], src.getHeight() - i - 1, roundRect[i], 1, third[i]);

            // forth
            forth[i] = (int[]) src.getDataElements(src.getWidth() - cornerRadius, src.getHeight() - i - 1, roundRect[i], 1, forth[i]);
        }
        initialized = true;
    }       

    // copy image pixels into corners
    for(int i = 0; i < cornerRadius; i++) {
        // first
        dstOut.setDataElements(src.getWidth() - cornerRadius, i, first[i].length, 1, second[i]);

        // second
        dstOut.setDataElements(cornerRadius - roundRect[i], i, second[i].length, 1, second[i]);

        // third
        dstOut.setDataElements(cornerRadius - roundRect[i], src.getHeight() - i - 1, third[i].length, 1, third[i]);

        // forth
        dstOut.setDataElements(src.getWidth() - cornerRadius, src.getHeight() - i - 1, forth[i].length, 1, forth[i]);
    }
}

}

これがお役に立てば幸いです。これは次善の解決策ですが、それが人生です(これは、グラフィックの第一人者がやって来て、私が間違っていることを証明するときです(??)..);-)

于 2012-02-24T12:19:00.690 に答える