軟體截圖原理:為什麼視窗截圖
這三種來源,正好對應了三種截然不同的技術路線,也解釋了為什麼「視窗截圖」在實作上會出現那麼多流派與那麼多坑。
二、為什麼視窗截圖在技術上更困難
如果截圖只是「複製一塊記憶體」,那視窗截圖聽起來反而更簡單——範圍更小、資料更少。但實際上,視窗截圖的難度遠高於全螢幕截圖,原因在於它必須面對三個結構性的問題。
2.1 視窗不是一張圖片,而是一連串繪製指令
這是最核心的觀念差異。對使用者而言,「一個視窗」是一個看得見的矩形;但對作業系統而言,視窗只是一塊畫布(canvas)加上一串繪製指令。應用程式透過 GDI、Direct2D、Skia、Core Graphics、OpenGL、Vulkan 等各種繪圖 API,把文字、圖形、圖片一筆一筆「畫上去」。系統並不會自動保存「這個視窗長什麼樣子」的完整快照,除非平台刻意設計了對應機制。
換句話說,視窗的「內容」是一種過程,而不是一種狀態。當你要求系統「給我這個視窗的畫面」時,系統可能有三種反應:一、從它保留的備份緩衝區拿給你;二、請應用程式重新繪製一次;三、兩者都做不到,只能給你一塊空白。這就是為什麼同一支截圖程式,截某些視窗很正常,截另一些視窗卻一片黑。
2.2 遮蔽、透明與疊層:視窗「被看見」與「存在」是兩回事
全螢幕截圖有一個先天優勢:它抓的是最終合成結果,所以「被遮擋」這件事根本不存在——被擋住的部分,本來就不在畫面上,也就無所謂損失。
但視窗截圖的期待正好相反。使用者要的是「這個視窗完整的樣子」,即使它被瀏覽器蓋住、被丟到第二螢幕、甚至被最小化。問題是,許多系統為了節省資源,根本不會持續繪製看不見的視窗。當視窗被完全遮擋時,合成器可能直接跳過該視窗的更新;當視窗被最小化時,某些平台甚至會釋放它的繪製表面。這時候你要截圖,拿到的就是「上一次繪製的殘影」或「一片黑」。
透明視窗更麻烦。具備 alpha 通道的視窗(例如圓角、陰影、半透明面板),在合成時需要與背景混合。如果你直接抓視窗表面,拿到的是「未混合」的內容,貼到白色背景上可能出現黑邊或奇怪的色暈;如果你抓合成結果,又會把背後的桌面一起抓進來。要不要保留陰影、要不要去背,這些都是產品層面的設計抉擇。
2.3 GPU 合成時代的衝擊:畫面已經不在記憶體裡了
在 Windows XP 或更早的年代,視窗繪製基本上是 CPU 用 GDI 一筆一筆畫到記憶體裡的 DC(Device Context)。那個時代的截圖相對單純:記憶體裡有什麼,就複製什麼。
但從 Windows Vista 的 DWM(Desktop Window Manager)、macOS 的 Quartz Compositor,到 Linux 的各種 compositor(Mutter、KWin、picom),整個繪製流程被徹底改寫。現在應用程式多半把畫面渲染到 GPU 紋理(texture)上,再由合成器在 GPU 上完成疊層、縮放、模糊、陰影等效果,最後才輸出到螢幕。也就是說,那個「完整的畫面」可能從來沒有以一張完整圖片的形式存在於 CPU 可讀的記憶體中。
這個變化對截圖工具的衝擊非常直接:傳統的「讀記憶體」做法失效了,開發者必須改用GPU 側的擷取 API,或者請系統把紋理「下載」回 CPU 記憶體。前者效能好但平台相依性高,後者相容性佳但可能造成卡頓。這也是為什麼近十年來,各大平台紛紛推出新的螢幕擷取框架——它們本質上都是在補這個洞。
三、Windows 平台的視窗截圖原理
Windows 是截圖工具數量最多的平台,也是技術流派最複雜的平台。從 1990 年代的 GDI 到 2020 年代的 Windows Graphics Capture,同一件事有四、五種做法並存,而且各有各的適用場景。
3.1 BitBlt 與 GetDC:GDI 時代的古典做法
最古老、也最廣為人知的做法,是先用 GetWindowDC 或 GetDC 取得目標視窗的裝置上下文,再用 BitBlt 把像素複製到自己的記憶體 DC。這是無數教學文章與入門範例的標準答案,程式碼不過十行。
但它的限制也非常明顯。BitBlt 抓的是「該視窗 DC 上目前存在的內容」,而在 DWM 啟用之後,視窗的繪製實際上發生在 DWM 管理的表面(surface)上,傳統 DC 讀到的內容未必與螢幕一致。視窗被遮擋時,經常會得到黑塊或重複的殘影;視窗使用硬體加速繪製(DirectX、OpenGL)時,甚至可能完全抓不到東西。
儘管如此,BitBlt 並沒有完全退場。對於傳統 GDI 繪製的老式應用程式、簡單的工具視窗,它仍然是最輕量、最不需要權限的做法。很多擷取函式庫會把它列為「快速路徑」,失敗時再退回其他方案。
3.2 PrintWindow 與 WM_PRINT:請應用程式自己重畫一次
為了解決「被遮擋就抓不到」的問題,Windows 提供了 PrintWindow API。它的原理和 BitBlt 完全不同:它不是去讀既有像素,而是送一個 WM_PRINT 或 WM_PRINTCLIENT 訊息給目標視窗,請應用程式把畫面「重新繪製」到呼叫者提供的 DC 上。
這個設計的優點很漂亮:既然內容是重新畫出來的,視窗有沒有被遮擋、有沒有被最小化,都不影響結果。它甚至可以在視窗不在前景時,取得「乾淨、無遮蔽」的畫面。
但它的弱點同樣致命:它完全依賴應用程式正確處理 WM_PRINT 訊息。遺憾的是,大量現代應用程式——尤其是使用 Chromium、Electron、Qt、Unity 等框架開發的程式——根本沒有實作這個訊息處理,或者只實作了部分。結果就是:你送出請求,對方回你一片空白。此外,PrintWindow 抓到的內容可能是「重新繪製」而非「當前狀態」,若應用程式有動畫或即時更新的內容,兩者可能不一致。
因此在實務上,PrintWindow 通常被當成「第二順位」的備援方案,而不是首選。
3.3 DWM 與 Windows Graphics Capture:從合成器直接取料
Windows 10 1803 之後,微軟推出了 Windows Graphics Capture(WGC),並在後續版本持續強化。這是目前 Windows 上最「正確」的視窗擷取方式,因為它直接從 DWM 持有的視窗表面取料,而不是去猜測應用程式怎麼畫。
WGC 的幾個關鍵特性值得注意:
相對地,WGC 需要較新的 Windows 版本,且部分舊版系統或特殊環境(例如某些虛擬機、遠端桌面連線)支援度不佳,因此商業軟體通常會將它與舊方法並存,形成「多層備援」的架構。
3.4 DXGI Desktop Duplication:抓的是「眼睛看到的」
另一個常被混淆的 API 是 DXGI Desktop Duplication。它透過 DirectX 直接取得「桌面輸出」的畫面,效率極高,是許多錄影軟體與遠端桌面工具的核心。
但要注意:Desktop Duplication 抓的是「整個輸出(output)」的合成結果,而不是單一視窗。也就是說,它本質上更接近「全螢幕截圖」的思路。想用它做視窗截圖,只能先抓整個螢幕,再依照視窗位置裁切——而這樣一來,被遮擋的部分自然就拿不到了。此外,它一次只能擷取一個螢幕輸出,多螢幕環境需要分別處理。
技術
資料來源
能否處理被遮擋視窗
支援硬體加速內容
主要限制
BitBlt / GetDC
視窗 DC 內容
通常不行
多半不行
DWM 下結果不穩定
PrintWindow
應用程式重繪
可以
視實作而定
依賴程式支援 WM_PRINT
Windows Graphics Capture
DWM 視窗表面
可以
可以
需較新系統版本
DXGI Desktop Duplication
輸出合成結果
不行
可以
只能抓整個輸出
四、macOS 與 Linux 的視窗截圖原理
如果說 Windows 的問題是「方法太多、品質不一」,那麼 macOS 與 Linux 的問題就分別是「權限越來越嚴」與「協定之爭」。這兩個平台的演進,正好反映了現代作業系統對螢幕擷取這件事的态度轉變。
4.1 macOS:WindowServer、CGWindowListCreateImage 與 ScreenCaptureKit
macOS 的圖形架構由 WindowServer 統一管理,所有視窗的繪製結果都會被合成到最後的畫面。這套「保留式」的模型,讓視窗截圖在技術上相對優雅:只要向 WindowServer 詢問某個視窗的內容,就能拿到一張完整的圖,即使該視窗被其他視窗遮住也一樣。
長期以來,開發者使用的是 CGWindowListCreateImage 這個 API,只要帶上視窗 ID,就能取得該視窗的影像。它甚至支援「只取視窗本身、不含陰影」或是「含陰影」的選項,對做產品截圖的人來說非常方便。
不過這條路在近幾年出現了兩個重大變化。第一是權限:從 macOS 10.15 Catalina 開始,任何螢幕擷取行為都必須取得「螢幕錄製」權限,使用者必須在系統設定中明確授權,否則只能拿到桌面背景與自己的視窗。第二是API 汰換:Apple 在 macOS 12.3 推出 ScreenCaptureKit,並在後續版本逐步棄用舊的 CGWindowList 系列。ScreenCaptureKit 提供了更細緻的控制(可指定視窗、可過濾、可取得音訊、可設定影格率),也更能配合現代 GPU 合成流程。
對開發者而言,這意味著「視窗截圖」在 macOS 上不再是一個隨手可得的副作用,而是一項需要主動設計、需要引導使用者授權的功能。這也是為什麼很多跨平台截圖工具在 macOS 上第一次啟動時,都會跳出「請到系統設定開啟螢幕錄製權限」的引導畫面。
4.2 Linux:X11 的 XGetImage 與 Wayland 的授權式擷取
Linux 桌面環境的狀況最為分裂,因為它同時存在兩套截然不同的顯示協定。
在傳統的 X11 架構下,視窗截圖相對直接。XGetImage 可以指定任何視窗或根視窗,把像素讀回來。若啟用了 Composite 擴充(現代桌面幾乎都有),每個視窗都有自己的離屏緩衝區(offscreen pixmap),因此即使視窗被遮擋,也能取得完整內容。這也是為什麼在 X11 上,各種截圖工具(Scrot、Spectacle、Flameshot)功能都非常強大且行為一致。
但 X11 有一個根本性的安全問題:任何一個 X11 客戶端都可以讀取整個螢幕的內容,也可以模擬鍵盤輸入。这在意義上等同於「任何程式都能偷看你的畫面」。Wayland 正是為了修正這個問題而誕生。
在 Wayland 之下,應用程式無法直接存取 framebuffer,也不能任意讀取其他視窗。螢幕擷取必須透過 PipeWire 搭配 xdg-desktop-portal 進行:應用程式提出請求,系統跳出一個由使用者選擇「要分享哪個螢幕或哪個視窗」的對話框,使用者同意後才會建立串流。這個設計從隱私角度來看是巨大的進步,但對截圖工具的開發者而言,代表「視窗截圖」不再是單純的 API 呼叫,而是一整套授權流程與串流管線。
更麻煩的是,各家桌面環境(GNOME、KDE、wlroots 系)對 portal 的實作細節略有差異,導致同一支工具在不同發行版上的行為可能不一致。這也是 Wayland 普及過程中,截圖與錄影工具最常被使用者抱怨的原因之一。
五、軟體為什麼偏好「視窗截圖」:六個實務理由
談完技術原理,回到產品層面:既然全螢幕截圖又簡單又不會出錯,為什麼這麼多軟體還是要特別支援視窗截圖?這背後有六個非常實際的理由。
5.1 隱私與合規:少截一塊,少一份風險
這是最容易被忽略、卻最重要的理由。想像一位使用者在公司電腦上截圖,準備把某個軟體的操作畫面貼到客服工單裡。如果他用的是全螢幕截圖,那麼畫面上所有東西都會被一起帶走:旁邊開著的私人聊天視窗、桌面上的機密檔名、工作列上未讀的郵件通知、甚至另一個螢幕上的財報。
視窗截圖從根本上解決了這個問題——它只取使用者明確指定的那一個視窗。對於需要符合內部資安政策、GDPR、或是醫療金融法規的組織來說,這不只是方便,而是合規要求。許多企業版截圖工具甚至會強制預設為「視窗模式」,並在設定中關閉全螢幕選項。
5.2 畫面純淨度:省下大量後製時間
對於寫教學文章、製作產品文件、提交 bug report 的人來說,圖片的「乾淨程度」直接影響專業感。全螢幕截圖之後,通常還得進圖片編輯軟體裁切、去掉桌面背景、補上圓角與陰影,一張圖花三到五分鐘很正常。
視窗截圖則是把這些步驟自動化:直接取得視窗邊界、可選擇是否包含標題列與陰影、可自動加上一致的留白與背景。對於每天要產出數十張圖的技術寫手或 QA 人員而言,這個效率差距是數量級的。
5.3 效能與資源:只搬需要的像素
一張 4K 螢幕的全螢幕截圖,原始資料量約為 3840 × 2160 × 4 bytes,接近 33MB;若是雙螢幕或 5K 環境,數字還要翻倍。相對地,一個 800 × 600 的視窗,資料量不到 2MB。在連續截圖、錄影、或遠端傳輸的場景下,這個差距會直接反映在 CPU 使用率、記憶體佔用與網路頻寬上。
遠端桌面與雲端遊戲就是最好的例子:它們只傳輸「有變化的視窗區域」,而不是整個螢幕,才能在有限的頻寬下維持流暢。視窗級別的擷取,是這種效率的前提。
5.4 多螢幕、高 DPI 與縮放的一致性
現代工作環境中,多螢幕加上不同縮放比例(例如主螢幕 150%、副螢幕 100%)是常態。在這種環境下做全螢幕截圖,很容易遇到尺寸錯亂、跨螢幕拼接錯誤、或是在高 DPI 螢幕上截出模糊圖片的問題。
視窗截圖因為範圍明確,比較容易處理縮放:工具可以取得該視窗所在螢幕的 DPI 資訊,據此決定要以邏輯像素還是實體像素輸出,避免「截出來比看到的還模糊」這種常見抱怨。
5.5 自動化與程式化取用
在自動化測試、UI 比對、監控系統中,「截圖」往往不是給人看的,而是給程式處理的。這時候,視窗截圖的價值在於可預測性:同一個視窗,每次截圖的尺寸、位置、內容範圍都一致,方便做像素比對或 OCR 辨識。全螢幕截圖則會因為桌面圖示、時間顯示、通知彈窗而每次不同,雜訊太多。
此外,視窗截圖可以針對「背景執行中的視窗」進行,不需要把視窗切到前景,這讓自動化流程不必搶奪使用者的滑鼠與焦點。
5.6 可重複性:同一視窗,同一結果
最後一點比較抽象,但很關鍵:視窗是一個穩定的「語意單位」。使用者說「我要截這個視窗」,指的是「這個應用程式的這個畫面」,而不是「螢幕上這個位置的這塊像素」。當視窗被移動、被縮放、被移到另一個螢幕時,全螢幕截圖的結果會完全不同,但視窗截圖仍然指向同一個對象。
這種「以視窗為單位」的思維,正好符合現代作業系統以視窗為管理單位的設計,也讓截圖這件事從「複製一塊畫面」提升到「擷取一個對象」。
六、視窗截圖的常見失敗與除錯思路
理解了原理,就能理解為什麼視窗截圖老是出問題。以下整理四種最常見的失敗情境,以及背後的技術原因。
6.1 黑畫面與空白畫面
這是投訴量第一名。可能原因有三:
6.2 截到被遮擋的內容
反過來,有些使用者會發現「截圖裡出現了不該出現的東西」——例如視窗明明被其他程式蓋住,截出來的圖卻顯示了背後視窗的內容,或出現兩個視窗疊在一起的殘影。這通常是因為擷取來源是「過期的備份緩衝區」,而非當前狀態。除錯時應確認工具是否正確處理了合成器的更新通知。
6.3 高 DPI 造成的模糊與尺寸錯亂
在高 DPI 環境下,若工具沒有正確宣告 DPI 感知(DPI awareness),系統會對畫面進行「虛擬化縮放」,導致截出來的圖片被放大或模糊。Windows 上這表現為「截圖尺寸比視窗實際尺寸大 1.5 倍」,macOS 上則是「Retina 螢幕截出非 Retina 解析度」。這類問題通常只要在應用程式清單中正確設定 DPI 感知模式即可解決。
6.4 保護內容與 DRM
如果你曾經嘗試截圖 Netflix、Disney+ 或其他串流平台,卻只得到一片黑,那不是你的工具壞了,而是內容保護機制在運作。Windows 提供 SetWindowDisplayAffinity 讓應用程式宣告「此視窗不得被擷取」,macOS 有 NSWindow.sharingType,串流服務則透過 DRM 與 HDCP 確保內容不被側錄。這類黑畫面是設計上的正確行為,任何標榜「可以破解」的工具,通常都遊走在法律與平台政策的灰色地帶。
七、選型建議:什麼時候該用哪一種截圖方式
對於正在開發截圖功能、或正在挑選工具的讀者,以下提供一份實務導向的判斷邏輯:
還有一個常被忽略的原則:永遠不要假設某一種方法一定成功。成熟的截圖軟體通常會實作多重備援,並在失敗時給出明確的原因(而非只是一片黑),這才是良好的使用者體驗。
八、結語:截圖技術的核心其實是「信任」
回顧整篇文章,我們從像素、緩衝區、合成器一路談到各平台的 API,看起來談的都是技術細節。但如果拉高一個層次來看,截圖這件事之所以在近十年變得越來越複雜,本質上是因為作業系統對「誰有權看到畫面」這件事,態度變了。
在 X11 的時代,任何程式都能看到整個螢幕,方便,但也危險。在 Wayland、macOS 與現代 Windows 的時代,擷取畫面變成一件需要授權、需要提示、需要使用者明確同意的行為。視窗截圖在這個轉變中扮演了關鍵角色:它把「看整個螢幕」這種過度授權的行為,縮小成「只看這一個視窗」的最小必要範圍。
所以,「為什麼要視窗截圖」這個問題,答案不只是「比較乾淨」或「比較方便」,而是:它是一種更精確、更尊重隱私、也更符合現代系統設計哲學的擷取方式。對使用者而言,它讓分享畫面變得安全;對開發者而言,它是一道必須跨過的技術門檻;對整個生態而言,它是「便利」與「信任」之間取得平衡的具體實踐。
下次當你按下截圖鍵、選擇「視窗模式」,然後看著那張乾淨、帶著淡淡陰影的圖片出現在編輯器裡時,或許可以想想:在那零點幾秒的瞬間,你的作業系統、合成器、GPU 與應用程式,其實已經完成了一場相當精密的合作。