圖像和像素渲染是Haskell中最後一件事情之一,我無法爲其選擇一個高效的純功能數據結構。爲了簡單起見,我們來討論一下1D
圖片,因爲這些圖片可以很容易地擴展到n-d圖片。我使用拆箱向量作爲代表和他們的可變視圖渲染:什麼是高效實現圖像渲染的純功能數據結構?
-- An 1D image is an unboxed vector of Pixels
type Position = Int
data Image = Image { size :: Position, buffer :: UV.Vector Pixel }
-- A sprite is a recipe to draw something to an Image
newtype Sprite = Sprite (forall s .
-> (Position -> Pixel -> ST s()) -- setPixel
-> ST s()) -- ST action that renders to Image
-- Things that can be rendered to screen provide a sprite
class Renderable a where
getSprite a :: a -> Sprite
-- `render` applies all sprites, mutably, in sequence. `O(P)` (where P = pixels to draw)
render :: [Sprite] -> Image -> Image
render draws (Image size buffer) = Image size $ runST $ do ...
這是CPU的渲染,我發現最高效的設計,但它是相當醜陋。對於實現render
的純功能結構,顯而易見的答案是使用地圖來表示圖像以及(Position, Pixel)
對的列表來表示精靈。類似:
-- 1D for simplicity
type Position = Int
-- An image is a map from positions to colors
type Image = Map Position Pixel
-- A sprite is, too, a map from positions to colors
type Sprite = [(Position, Pixel)]
-- Rendering is just inserting each pixel in sequence
-- (Not tested.)
render :: [Sprite] -> Image -> Image
render sprites image = foldr renderSprite image sprites
where renderSprite sprite image = foldr (uncurry insert) image sprites
哪個O(P * log(N))
(P =像素渲染,N =尺寸的圖像的)。它可能看起來log(N)
是不可逃避的,但如果你仔細看看它,render
幾次通過Image
行進相同的路徑(即,如果你插入位置0,然後在位置1,你一直跑到葉子,然後一路向上,然後一路下到鄰居的葉子......)。這看起來像是完全相同的模式,例如zippers
可以改善漸近性,這導致我懷疑render
可以得到改善。 是否有任何純粹的功能數據結構,允許實施render
比O(P*log N)
更好?
注:由「單純的功能性」,我特別是指單獨使用也不過Haskell的代數數據類型的結構,即沒有本地原語如Int
和Array
(即使那些在技術上充當純粹的數據結構幽冥少)。
怎麼樣'Array'或'UArray'? –
這並不能真正回答你的問題,但我想問一問。在第一種方法中,Sprite等同於調用'Position - > Pixel - > ST s()'的列表。無法將它改爲'[(位置,像素)],使它更好,同時保持相同的性能? – madjar
就我的技能而言,沒有。問題是'Position - > Pixel - > ST()'總是使用常量空間。爲了使用列表,您必須始終計算每個列表將被融合並編譯爲恆定的空間循環。也許這只是我,但即使使用簡單的渲染模式,我也無法得到它...... – MaiaVictor