程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Merlin的魔力: 練習完全控制圖形顯示

Merlin的魔力: 練習完全控制圖形顯示

編輯:關於JAVA

您是否喜歡讓程序搞一些惡作劇,讓別人感到不舒服?如果您回答“是”,那麼這個月的技巧文章一定對您的胃口。使用 J2SE 1.4,您的 Java 程序現在可以更改視頻方式並獲得對屏幕的絕對控制。您不必讓別人隨心所欲地玩電腦;您差不多可以擁有整個控制權。感謝新的全屏幕獨占模式(FEM)API 為我們提供了這個無與倫比的強大功能。

即使您回答“不”,不想以惹惱他人來取樂,您也將發現 FEM API 提供了許多幫助。通過直接對顯存進行寫操作,FEM API 提供了對顯示的完全控制 ― 這對於游戲開發來說十分理想,雖然還有許多其它應用。例如,一些程序只有用特定大小的屏幕看上去才更好,並且才能更好地工作。請繼續讀下去,以發掘您內心有關控制方面的奇思怪想。

更改顯示方式

讓我們先從研究 FEM API 的 java.awt.DislayMode 類開始,該類包裝了特定顯示方式的屏幕大小和刷新頻率。受支持的方式特定於系統的硬件支持。

要找出特定系統的受支持方式,請查看 GraphicsEnvironment 。通過該環境,您可以獲得缺省屏幕設備 GraphicDevice ,通過該屏幕設備可以獲得顯示方式,如清單 1 所示:

清單 1. 查找顯示方式

GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphicsDevice =
graphicsEnvironment.getDefaultScreenDevice();
DisplayMode displayModes[] =
graphicsDevice.getDisplayModes();

還可以使用 getDisplayMode() 方法獲得當前的顯示模式,如清單 2 所示:

清單 2. 獲得當前的顯示方式

DisplayMode originalDisplayMode =
    graphicsDevice.getDisplayMode();

更改方式經證實相對容易些,但必須先利用 GraphicsDevice 的 isDisplayChangeSupported() 方法詢問所涉及的圖形設備是否支持更改。

一旦知道了這一點,要更改方式,使用 setDisplayMode() 方法,以傳入新方式。顯示方式更改一般在 try / finally 塊中發生,以便於 finally 塊將代碼復位成初始方式。雖然該過程不是絕對必需的,但它可以確保在程序完成時有一個安全的方式。清單 3 顯示了用於更改顯示方式的典型模式:

清單 3. 更改方式

GraphicsDevice graphicsDevice = ...
   DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
   DisplayMode newDisplayMode = ...
   try {
    if (graphicsDevice.isDisplayChangeSupported()) {
     graphicsDevice.setDisplayMode(newDisplayMode);
    }
   } finally {
    graphicsDevice.setDisplayMode(originalDisplayMode);
   }

使用全屏幕窗口

使用 FEM API,進入全屏幕窗口是輕而易舉的:只要將 window 傳遞給 GraphicsDevice 的 setFullScreenWindow() 方法,如清單 4 所示。當您想要返回非全屏幕方式時,將 null 傳遞給該方法。當然,先通過使用 isFullScreenSupported() 方法來檢查 GraphicsDevice 是否支持全屏幕方式。

清單 4. 進入全屏幕方式

GraphicsDevice graphicsDevice = ...
   Window window = ...
   try {
    if (graphicsDevice.isFullScreenSupported()) {
     graphicsDevice.setFullScreenWindow(window);
    }
   } finally {
    graphicsDevice.setFullScreenWindow(null);
   }

為了說明目前為止您所學的所有內容,清單 5 包含了一個完整示例。清單 5 中的代碼獲得顯示方式,隨機選擇一個方式進行更改,然後顯示一個帶有文本“Hello, World!”的全屏幕窗口。該示例顯示新顯示方式的特征,這樣您可以看到特定的屏幕大小、刷新頻率和色深(bit depth)。

清單 5. 方式更改示例

import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class DisplayModes {
  public static void main(String args[]) {
   GraphicsEnvironment graphicsEnvironment =
    GraphicsEnvironment.getLocalGraphicsEnvironment();
   GraphicsDevice graphicsDevice =
    graphicsEnvironment.getDefaultScreenDevice();
   DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
   DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
   JWindow window = new JWindow() {
    public void paint(Graphics g) {
     g.setColor(Color.blue);
     g.drawString("Hello, World!", 50, 50);
    }
   };
   try {
    if (graphicsDevice.isFullScreenSupported()) {
     graphicsDevice.setFullScreenWindow(window);
    }
    Random random = new Random();
    int mode = random.nextInt(displayModes.length);
    DisplayMode displayMode = displayModes[mode];
    System.out.println(displayMode.getWidth() + "x" +
     displayMode.getHeight() + " \t" + displayMode.getRefreshRate() +
     " / " + displayMode.getBitDepth());
    if (graphicsDevice.isDisplayChangeSupported()) {
     graphicsDevice.setDisplayMode(displayMode);
    }
    Thread.sleep(1000);
   } catch (InterruptedException e) {
   } finally {
    graphicsDevice.setDisplayMode(originalDisplayMode);
    graphicsDevice.setFullScreenWindow(null);
   }
   System.exit(0);
  }
}

全屏幕呈現

請注意:在清單 5 中,用於全屏幕的窗口包括了十分有用的 paint() 方法。然而,因為窗口處於全屏幕方式,所以 paint() 方法所需的開銷、該方法如何處理剪裁以及其它與顯示處理相關的行為都是不必要的。實際上,已經證明了標准的被動呈現開銷太大,它減慢了全屏幕應用程序的速度。例如,您不必處理如重疊窗口或調整窗口大小之類的任務。而是可以采取更主動的方法,創建一個處理呈現窗口本身的緊湊循環(tight loop)。

如果您熟悉 雙緩沖,那麼您知道它在內存中管理兩個 Image 對象,並根據哪個對象擁有 當前的顯示信息來在這兩個對象之間進行交換。在一個 Image 顯示的同時,您繪制另一個 Image 並在各個“場景”之間交換 Image 對象。

顯卡利用了類似的概念,但不是使用實際的 Java Image 對象,而是交換內存頁。當交換內存頁時顯示就發生更改,所以您不需要將 Image 對象從程序內存復制到顯示內存;您只要更改視頻指針,顯示就會發生更改。盡管現在雙緩沖概念仍然存在,但已不是對程序空間中的 Image 進行寫操作,而是直接繪制到顯示內存空間。

BufferStrategy 類隱藏了使用的是上述兩種雙緩沖方法中的哪一種這一事實,並且它允許您利用系統所提供的任何基於硬件的緩沖。要創建 BufferStrategy ,使用 createBufferStrategy() 方法告訴系統您所期望的緩沖區數目,並使用 getDrawGraphics() 方法在緩沖區之間進行交換,該方法返回下一個要使用的緩沖區。從概念上講僅此而已,但如清單 6 所示,工作代碼需要花更多的精力:

清單 6. 使用 BufferStrategy

JFrame frame = ...
  frame.createBufferStrategy(2); // Number of buffers to have
  BufferStrategy bufferStrategy = frame.getBufferStrategy();
  while (!done()) { // Some condition to end
   Graphics g = null;
   try {
    g = bufferStrategy.getDrawGraphics();
    drawNextScene(g); // Method to draw to
   } finally {
    // Free resources
    if (g != null) {
     g.dispose();
    }
   }
   bufferStrategy.show();
  }

使用 BufferStrategy 時,您不能假設繪制到緩沖區的最後一項仍有效;必須使用 contentsLost() 方法進行詢問。如果丟失該項,則必須重新繪制整個緩沖區。否則,您只需繪制最後一次使用後發生的更改的項。

除了 BufferStrategy 的緩沖區支持外, BufferCapabilities 類允許您使用 getCapabilities() 方法來發現某個策略支持哪些能力,如全屏幕方式。

工作示例

將所有代碼段放在一起可產生清單 7 中的示例程序。由於我的美術技能有限,所以不要指望有什麼復雜的花樣。然而,您將發現一個使用 BufferStrategy 和全屏幕繪制方式的完整工作示例。示例程序通過記住什麼東西沒有繪制到當前緩沖區來有意 不讓緩沖區同步 ― 以便讓您更清楚地了解同時工作的多個緩沖區。程序的確隨意地將 100 個矩形繪制到屏幕,兩次繪制之間有 1/10 秒的延遲。

清單 7. 工作示例

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.Random;
public class MultipleBuffers {
  public static void main(String args[]) {
   GraphicsEnvironment graphicsEnvironment =
    GraphicsEnvironment.getLocalGraphicsEnvironment();
   GraphicsDevice graphicsDevice =
    graphicsEnvironment.getDefaultScreenDevice();
   DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
   DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
   JFrame frame = new JFrame();
   frame.setUndecorated(true);
   frame.setIgnoreRepaint(true);
   try {
    if (graphicsDevice.isFullScreenSupported()) {
     graphicsDevice.setFullScreenWindow(frame);
    }
    Random random = new Random();
    int mode = random.nextInt(displayModes.length);
    DisplayMode displayMode = displayModes[mode];
    System.out.println(displayMode.getWidth() + "x" +
     displayMode.getHeight() + " \t" + displayMode.getRefreshRate() +
     " / " + displayMode.getBitDepth());
    if (graphicsDevice.isDisplayChangeSupported()) {
     graphicsDevice.setDisplayMode(displayMode);
    }
    frame.createBufferStrategy(2);
    BufferStrategy bufferStrategy = frame.getBufferStrategy();
    int width = frame.getWidth();
    int height = frame.getHeight();
    for (int i=0; i<100; i++) {
     Graphics g = bufferStrategy.getDrawGraphics();
     g.setColor(new Color(random.nextInt()));
   g.fillRect(random.nextInt(width),
     random.nextInt(height), 100, 100);
     bufferStrategy.show();
     g.dispose();
     Thread.sleep(100);
    }
   } catch (InterruptedException e) {
   } finally {
    graphicsDevice.setDisplayMode(originalDisplayMode);
    graphicsDevice.setFullScreenWindow(null);
   }
   System.exit(0);
  }
}

示例程序將受益於一些增強功能:如使緩沖區保持同步,或者檢查當內容丟失時是否必須重新繪制整個緩沖區。前一個任務只需記住最後繪制的矩形(和顏色),而後者要求全都記住。

管中窺豹,可見一斑

有了新的全屏幕獨占模式 API,Java 開發就能成為游戲開發的主流選項。該 API 取代了使用特定於平台的 API(如 DirectX 或 OpenGL)的需求,而是依賴於跨所有支持 Java 平台的標准 API。這遠遠超出那些熱衷於編程的專家的想象。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved