2007 年 2 月 8 日 0 時 18 分

RFBCanvas #2


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


昨日に引き続き、ヘルパメソッドの実装をしていこう。

========== RFBCanvas#recreateDisplayBuffer ==========

    // クライアントの画素形式に合った画面バッファを用意
    private void recreateDisplayBuffer() throws IOException {

        PixelFormat format = context.getPixelFormat();
       
        if (!format.isTrueColor())
                throw new IOException("Can't handle indexed pixel format.");

        int redMask = format.getRedMax() << format.getRedShift();
        int greenMask = format.getGreenMax() << format.getGreenShift();
        int blueMask = format.getBlueMax() << format.getBlueShift();
   
        ColorModel model = new DirectColorModel(
                format.getBitPerPixel(), redMask, greenMask, blueMask);

        WritableRaster raster = model.createCompatibleWritableRaster(
                getWidth(), getHeight());
       
        synchronized (paintingSystem) {

            // 以前の画面バッファがあればデータを引き継ぐ
            if (displayBuffer != null) {
                raster.setRect(displayBuffer.getRaster());
            }
       
            displayBuffer = new BufferedImage(model, raster, false, null);
            unsentRegion = null;
            requestedRegion = null;

        }
    }

========== end of RFBCanvas#recreateDisplayBuffer ==========

recreateDisplayBuffer メソッドでメンバを初期化する。
この実装については、以前に考察した時のものを利用した。
グラフィックス系のクラスは奥が深い。
http://mixi.jp/view_diary.pl?id=318269104&owner_id=2300658

========== RFBCanvas#firePaint ==========

    // 指定範囲に対して paint を発行
    private void firePaint(Rectangle region) {

        synchronized (paintingSystem) {

            // 描画範囲を計算
            Rectangle r = region.intersection(clientRegion);
            if (r.isEmpty()) return;
           
            // 描画
            Graphics2D g = displayBuffer.createGraphics();
            try {
                g.setClip(r);
                paint(g);
            } finally {
                g.dispose();
            }
   
            // 描画された範囲を未送信領域に追加
            unsentRegion = addShapes(unsentRegion, r);

        }
    }

========== end of RFBCanvas#firePaint ==========

========== RFBCanvas#fireUpdate ==========

    // 指定範囲に対して update を発行
    void fireUpdate(Rectangle region) {
        synchronized (paintingSystem) {

            // 描画範囲を計算
            Rectangle r = region.intersection(clientRegion);
            if (r.isEmpty()) return;
   
            // 描画
            Graphics2D g = displayBuffer.createGraphics();
            try {
                g.setClip(r);
                update(g);
            } finally {
                g.dispose();
            }
   
            // 更新された範囲を未送信領域に追加
            unsentRegion = addShapes(unsentRegion, r);
           
        }
    }

========== end of RFBCanvas#fireUpdate ==========

firePaint/fireUpdate の実装はほぼ同じだ。
displayBuffer の createGraphics で Graphics を作成し、
実装者が描画できる範囲にクリッピングを施した上で、
paint/update コールバックを呼び出す。
もし更新領域がなければイベントは発行しない。

このメソッドで更新された領域は、
後にクライアントに送信するため、
unsentRegion に追加しておく。

addShapes は、Shape 同士を合成するためのメソッドだ。

========== RFBCanvas#addShapes ==========

    // Shape 2 つを合成する
    static private Shape addShapes(Shape augend, Shape addend) {
        if (augend == null) return addend;
        if (addend == null) return augend;
       
        Area a = new Area(augend);
        a.add(new Area(addend));
        return a;
    }

========== end of RFBCanvas#addShapes ==========

unsentRegion を Shape にしたのには理由がある。
更新される領域は基本的に矩形(Rectangle)なのだが、
クライアントが要求する領域が一致しているとは限らない。
そのため、未送信の領域は矩形に収まらない、
不定形の領域になる可能性が高いのだ。

Area は、Shape の 1 種であり、
任意の形状の領域を保持することができる。
しかし、Area は処理コストが高くつくため、
矩形領域の場合は Rectangle を使うことで高速化を図る。

さて、最後に sendFramebuffer を実装しなければならない。
このメソッドではクライアントにデータを送るのだが、
実はサーバメッセージをまだ解説していないため、
現時点では処理する方法が分からないのだ。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.