2010-06-27 28 views
6

Tôi đang cố tải một tệp PNG, nhận các byte RGBA chưa nén, sau đó gửi chúng đến các gói gzip hoặc zlib.Làm thế nào tôi có thể chuyển đổi một (StorableArray (Int, Int) Word8) thành một ByteString lười biếng?

Gói pngload trả về dữ liệu hình ảnh dưới dạng (StorableArray (Int, Int) Word8) và các gói nén mất ByteStrings lười. Vì vậy, tôi đang cố gắng để xây dựng một (StorableArray (Int, Int) Word8 -> ByteString) chức năng.

Cho đến nay, tôi đã thử như sau:

import qualified Codec.Image.PNG as PNG 
import Control.Monad (mapM) 
import Data.Array.Storable (withStorableArray) 
import qualified Data.ByteString.Lazy as LB (ByteString, pack, take) 
import Data.Word (Word8) 
import Foreign (Ptr, peekByteOff) 

main = do 
    -- Load PNG into "image"... 
    bytes <- withStorableArray 
     (PNG.imageData image) 
     (bytesFromPointer lengthOfImageData) 

bytesFromPointer :: Int -> Ptr Word8 -> IO LB.ByteString 
bytesFromPointer count pointer = LB.pack $ 
    mapM (peekByteOff pointer) [0..(count-1)] 

Điều này làm cho chồng để chạy ra khỏi bộ nhớ, vì vậy rõ ràng tôi đang làm một cái gì đó rất sai. Có nhiều thứ tôi có thể thử với Ptr và ForeignPtr, nhưng có rất nhiều chức năng "không an toàn" trong đó.

Mọi trợ giúp ở đây sẽ được đánh giá cao; Tôi khá bối rối.

Trả lời

7

Nói chung, đóng gói và giải nén là một ý tưởng tồi về hiệu suất. Nếu bạn có một PTR, và chiều dài tính bằng byte, bạn có thể tạo ra một bytestring nghiêm ngặt theo hai cách khác nhau:

Như thế này:

import qualified Codec.Image.PNG as PNG 
import Control.Monad 
import Data.Array.Storable (withStorableArray) 

import Codec.Compression.GZip 

import qualified Data.ByteString.Lazy as L 
import qualified Data.ByteString.Unsafe as S 

import Data.Word 
import Foreign 

-- Pack a Ptr Word8 as a strict bytestring, then box it to a lazy one, very 
-- efficiently 
bytesFromPointer :: Int -> Ptr Word8 -> IO L.ByteString 
bytesFromPointer n ptr = do 
    s <- S.unsafePackCStringLen (castPtr ptr, n) 
    return $! L.fromChunks [s] 

-- Dummies, since they were not provided 
image = undefined 
lengthOfImageData = 10^3 

-- Load a PNG, and compress it, writing it back to disk 
main = do 
    bytes <- withStorableArray 
     (PNG.imageData image) 
     (bytesFromPointer lengthOfImageData) 
    L.writeFile "foo" . compress $ bytes 

Tôi đang sử dụng phiên bản O (1), mà chỉ đóng gói lại Ptr từ StorableArray. Bạn có thể muốn sao chép nó trước, thông qua "packCStringLen".

+0

Điều này hoạt động rất tốt. Cảm ơn đã giúp đỡ! –

3

Vấn đề với "bytesFromPointer" của bạn là bạn lấy một biểu diễn đóng gói, StorableArray từ pngload, và bạn muốn chuyển đổi nó sang một biểu diễn đóng gói khác, một ByteString, trải qua một danh sách trung gian. Đôi khi sự lười biếng có nghĩa là danh sách trung gian sẽ không được xây dựng trong bộ nhớ, nhưng đó không phải là trường hợp ở đây.

Chức năng "mapM" là người phạm tội đầu tiên. Nếu bạn mở rộng mapM (peekByteOff pointer) [0..(count-1)] bạn nhận được

el0 <- peekByteOff pointer 0 
el1 <- peekByteOff pointer 1 
el2 <- peekByteOff pointer 2 
... 
eln <- peekByteOff pointer (count-1) 
return [el0,el1,el2,...eln] 

vì tất cả các hành động này xảy ra trong đơn IO, chúng được thực hiện theo thứ tự. Điều này có nghĩa là mọi phần tử của danh sách đầu ra phải được xây dựng trước khi danh sách được xây dựng và sự lười biếng không bao giờ có cơ hội để giúp bạn.

Ngay cả khi danh sách được xây dựng một cách uể oải, vì Don Stewart chỉ ra chức năng "gói" sẽ vẫn làm hỏng hiệu suất của bạn. Vấn đề với "gói" là nó cần phải biết có bao nhiêu phần tử trong danh sách để phân bổ số lượng bộ nhớ chính xác. Để tìm độ dài của một danh sách, chương trình cần di chuyển nó đến cuối. Do tính cần thiết của việc tính toán độ dài, danh sách sẽ cần phải được tải hoàn toàn trước khi nó có thể được đóng gói thành một phần thưởng.

Tôi xem xét "mapM", cùng với "gói", là một mùi mã. Đôi khi bạn có thể thay thế "mapM" bằng "mapM_", nhưng trong trường hợp này, tốt hơn nên sử dụng các hàm tạo hàm lồng, ví dụ: "packCStringLen".

Các vấn đề liên quan