什么是有效实现图像渲染的纯功能数据结构?

时间:2015-10-05 12:52:08

标签: algorithm haskell data-structures time-complexity

图像和像素渲染是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的纯函数结构,显而易见的答案是使用映射来表示Image和(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的代数数据类型的结构,即没有IntArray等原生基元(尽管这些是技术上服务的)作为纯粹的数据结构,更少。)

2 个答案:

答案 0 :(得分:6)

如果精灵中的位置可以是任意的(例如[(0,x),(7,y),(5000,z)]),那么如果只允许使用数据结构,那么你似乎不能希望做得比O(P log N)更好。有限的分支因子。

如果精灵中的位置是连续的,那么你可以使用支持对数切片和连接的Seq(fingertree)来在O(log N)时间内实现render。如果您的精灵由k个不相交的连续序列组成,那么您可以对O(k log N)render重复此k次。

但是,我认为除非你愿意放弃额外的O因子(一维中的精灵的边长),否则扩展到两个维度并不像你说的那么容易。也许有某种手指k-d树可以避免这个额外的因素。

答案 1 :(得分:3)

您可以使用discrimination包在O(n + p)时间内构建Map

render sprites image
    = flip union image
    . toMapWith (\new old -> new)
    . concat
    $ sprites