9

私は現在、ビデオゲームのように通常の速度で画面に画像を描画しようとしています。

残念ながら、画像の移動速度のため、画像がまだ完全なピクセルを移動していないため、一部のフレームは同一です。

float値ではなく、画像を描画するための画面上の位置の値をGraphics2Dに提供する方法はありintますか?

最初に私がしたことは次のとおりです。

BufferedImage srcImage = sprite.getImage ( );
Position imagePosition = ... ; //Defined elsewhere
g.drawImage ( srcImage, (int) imagePosition.getX(), (int) imagePosition.getY() );

もちろんこれはしきい値なので、画像はピクセル間を移動しませんが、あるピクセルから次のピクセルにスキップします。

次の方法は、代わりにペイントカラーをテクスチャに設定し、指定された位置に描画することでした。残念ながら、これにより、正しいアンチエイリアスではなくタイリングを示す誤った結果が生成されました。

g.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );

BufferedImage srcImage = sprite.getImage ( );

g.setPaint ( new TexturePaint ( srcImage, new Rectangle2D.Float ( 0, 0, srcImage.getWidth ( ), srcImage.getHeight ( ) ) ) );

AffineTransform xform = new AffineTransform ( );

xform.setToIdentity ( );
xform.translate ( onScreenPos.getX ( ), onScreenPos.getY ( ) );
g.transform ( xform );

g.fillRect(0, 0, srcImage.getWidth(), srcImage.getHeight());

Javaで画像のサブピクセルレンダリングの望ましい効果を達成するにはどうすればよいですか?

4

4 に答える 4

9

とを使用しBufferedImageAffineTransform、バッファリングされたイメージに描画してから、ペイントイベントでバッファリングされたイメージをコンポーネントに描画できます。

    /* overrides the paint method */
    @Override
    public void paint(Graphics g) {
        /* clear scene buffer */
        g2d.clearRect(0, 0, (int)width, (int)height);

        /* draw ball image to the memory image with transformed x/y double values */
        AffineTransform t = new AffineTransform();
        t.translate(ball.x, ball.y); // x/y set here, ball.x/y = double, ie: 10.33
        t.scale(1, 1); // scale = 1 
        g2d.drawImage(image, t, null);

        // draw the scene (double percision image) to the ui component
        g.drawImage(scene, 0, 0, this);
    }

ここで私の完全な例を確認してください:http://pastebin.com/hSAkYWqM

于 2012-01-05T18:09:27.793 に答える
3

サブピクセルの精度を使用して自分で画像を合成することもできますが、それはあなたの側でより多くの作業を行います。単純な双一次補間は、ゲームには十分に機能するはずです。以下は、それを行うための疑似C++コードです。

通常、場所(a、b)にスプライトを描画するには、次のようにします。

for (x = a; x < a + sprite.width; x++)
{
    for (y = b; y < b + sprite.height; y++)
    {
        *dstPixel = alphaBlend (*dstPixel, *spritePixel);
        dstPixel++;
        spritePixel++;
    }
    dstPixel += destLineDiff; // Move to start of next destination line
    spritePixel += spriteLineDiff; // Move to start of next sprite line
}

サブピクセルレンダリングを行うには、同じループを実行しますが、次のようにサブピクセルオフセットを考慮します。

float xOffset = a - floor (a);
float yOffset = b - floor (b);
for (x = floor(a), spriteX = 0; x < floor(a) + sprite.width + 1; x++, spriteX++)
{
    for (y = floor(b), spriteY = 0; y < floor (b) + sprite.height + 1; y++, spriteY++)
    {
        spriteInterp = bilinearInterp (sprite, spriteX + xOffset, spriteY + yOffset);
        *dstPixel = alphaBlend (*dstPixel, spriteInterp);
        dstPixel++;
        spritePixel++;
    }
    dstPixel += destLineDiff; // Move to start of next destination line
    spritePixel += spriteLineDiff; // Move to start of next sprite line
}

bilinearInterp()関数は次のようになります。

Pixel bilinearInterp (Sprite* sprite, float x, float y)
{
    // Interpolate the upper row of pixels
    Pixel* topPtr = sprite->dataPtr + ((floor (y) + 1) * sprite->rowBytes) + floor(x) * sizeof (Pixel);
    Pixel* bottomPtr = sprite->dataPtr + (floor (y) * sprite->rowBytes) + floor (x) * sizeof (Pixel);

    float xOffset = x - floor (x);
    float yOffset = y - floor (y);

    Pixel top = *topPtr + ((*(topPtr + 1) - *topPtr) * xOffset;
    Pixel bottom = *bottomPtr + ((*(bottomPtr + 1) - *bottomPtr) * xOffset;
    return bottom + (top - bottom) * yOffset;
}

これは追加のメモリを使用しないはずですが、レンダリングに追加の時間がかかります。

于 2011-12-30T19:42:08.600 に答える
1

ローレンスアランが提案したようなことをした後、私は問題をうまく解決しました。

元々、次のコードがありgました。メソッドが呼び出される前に、16:9の座標系に変換されます。

private void drawStar(Graphics2D g, Star s) {

    double radius = s.getRadius();
    double x = s.getX() - radius;
    double y = s.getY() - radius;
    double width = radius*2;
    double height = radius*2;

    try {

        BufferedImage image = ImageIO.read(this.getClass().getResource("/images/star.png"));
        g.drawImage(image, (int)x, (int)y, (int)width, (int)height, this);

    } catch (IOException ex) {
        Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex);
    }

}

ただし、質問者のKaushik Shankarが指摘しているように、doubleの位置を整数に変換すると、画像が「ジャンプ」し、doubleの次元を整数に変換すると、縮尺が「ジャンプ」になります(なぜ地獄はg.drawImagedoubleを受け入れないのですか?!)。私が私のために働いているのを見つけたのは次のとおりでした:

private void drawStar(Graphics2D g, Star s) {

    AffineTransform originalTransform = g.getTransform();

    double radius = s.getRadius();
    double x = s.getX() - radius;
    double y = s.getY() - radius;
    double width = radius*2;
    double height = radius*2;

    try {

        BufferedImage image = ImageIO.read(this.getClass().getResource("/images/star.png"));

        g.translate(x, y);
        g.scale(width/image.getWidth(), height/image.getHeight());

        g.drawImage(image, 0, 0, this);

    } catch (IOException ex) {
        Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex);
    }

    g.setTransform(originalTransform);

}

しかし、それを行うための愚かな方法のようです。

于 2014-11-27T08:33:38.170 に答える
0

それに応じて画像の解像度を変更します。サブピクセル座標のビットマップなどはありません。基本的には、画面にレンダリングしたいものよりも大きいメモリ内画像を作成できますが、「サブピクセル」を使用できます。 " 正確さ。

メモリ内の大きな画像に描画するときは、それをコピーして、エンドユーザーに表示される小さなレンダリングにリサンプリングします。

例:100x100の画像で、50x50のサイズ変更/リサンプリングされた画像:

リサンプリング

参照: http: //en.wikipedia.org/wiki/Resampling_%28bitmap%29

于 2011-12-30T07:03:35.293 に答える