-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Images held in memory #444
Comments
Here's an example program of what I mean: {-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLabels #-}
module Main where
import Data.GI.Base
import qualified GI.Gtk as Gtk
import qualified GI.Gio as Gio
import qualified GI.GdkPixbuf as Pix
import qualified GI.Gdk as Gdk
import qualified GI.Adw as Adw
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as BL
import Data.Text (Text)
import qualified Data.Text as T
import Network.HTTP.Client
import Network.HTTP.Client.TLS
import Data.Int (Int32)
import Control.Concurrent
main :: IO ()
main = do
app <- new Gtk.Application [#applicationId := "com.image-memory-test"]
on app #activate $ activate app
void $ app.run Nothing
activate :: Gtk.Application -> IO ()
activate app = do
headerBar <- new Gtk.HeaderBar []
refresh <- new Gtk.Button [#iconName := "view-refresh-symbolic"]
headerBar.packStart refresh
flowbox <- new Gtk.FlowBox []
scroll <- new Gtk.ScrolledWindow [#child := flowbox]
on scroll #edgeReached $ \positionType -> do
when (positionType == Gtk.PositionTypeBottom) $ fillFlowbox flowbox
on refresh #clicked $ flowbox.removeAll >> fillFlowbox flowbox >>
scroll.setVadjustment (Nothing::Maybe Gtk.Adjustment)
window <- new Gtk.Window [ #application := app, #defaultWidth := 850
, #defaultHeight := 500, #child := scroll
, #titlebar := headerBar
, #title := "Image Memory Test" ]
window.present
fillFlowbox flowbox
fillFlowbox :: Gtk.FlowBox -> IO ()
fillFlowbox flowbox = do
let imageUrl = "https://s4.anilist.co/file/anilistcdn/\
\media/anime/cover/large/bx21-YCDoj1EkAxFn.jpg"
forM_ [(1::Int)..21] $ \_ -> do
picture <- new Gtk.Picture [#contentFit := Gtk.ContentFitCover]
clamp <- new Adw.Clamp [ #child := picture, #maximumSize := 100
, #widthRequest := 100, #heightRequest := 150 ]
loadImage 460 picture imageUrl
flowbox.append clamp
getUrlBytes :: Text -> IO ByteString
getUrlBytes url = do
manager <- getGlobalManager
request <- parseRequest $ T.unpack url
BL.toStrict . responseBody <$> httpLbs request manager
loadImage :: Int32 -> Gtk.Picture -> Text -> IO ()
loadImage width pic imageUrl = void $ forkIO $ do
bytes <- getUrlBytes imageUrl
inputStream <- Gio.memoryInputStreamNewFromData bytes Nothing
let onImageLoaded _ result = do
Just pixbuf <- Pix.pixbufNewFromStreamFinish result
texture <- Gdk.textureNewForPixbuf pixbuf
pic.setPaintable $ Just texture
Pix.pixbufNewFromStreamAtScaleAsync inputStream width (-1) True
(Nothing::Maybe Gio.Cancellable) $ Just onImageLoaded In this program, scrolling to the bottom of the page loads in more images, and more memory is used which is fine, but after hitting refresh and emptying the flowbox, memory usage never goes back down. |
Thanks for the report. This one is a little tricky. I am not sure there's a leak, or at least I cannot see it. I do see the memory increase, but I think it might be a combination of two factors:
For example when I run the code below (note the manual {- cabal:
build-depends: base, haskell-gi-base, gi-gobject, gi-gtk == 4.0.*, gi-gio, gi-gdkpixbuf, gi-gdk, gi-adwaita, text, bytestring, HTTP, http-client, http-client-tls
-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLabels #-}
module Main where
import Data.GI.Base
import qualified GI.Gtk as Gtk
import qualified GI.Gio as Gio
import qualified GI.GdkPixbuf as Pix
import qualified GI.Gdk as Gdk
import qualified GI.Adw as Adw
import Control.Monad
import qualified Data.ByteString as BS
import Data.IORef
import Data.Int (Int32)
import Control.Concurrent
import qualified System.Mem
import Foreign.C.Types (CSize(..), CInt(..))
foreign import ccall malloc_trim :: CSize -> IO CInt
main :: IO ()
main = do
app <- new Gtk.Application [#applicationId := "com.image-memory-test"]
_ <- on app #activate $ activate app
void $ app.run Nothing
activate :: Gtk.Application -> IO ()
activate app = do
headerBar <- new Gtk.HeaderBar []
refresh <- new Gtk.Button [#iconName := "view-refresh-symbolic"]
headerBar.packStart refresh
flowbox <- new Gtk.FlowBox []
scroll <- new Gtk.ScrolledWindow [#child := flowbox]
ref <- newIORef flowbox
_ <- on scroll #edgeReached $ \positionType ->
when (positionType == Gtk.PositionTypeBottom) $ do
refFlowbox <- readIORef ref
fillFlowbox refFlowbox
_ <- on refresh #clicked $ do
newFlowbox <- new Gtk.FlowBox []
writeIORef ref newFlowbox
fillFlowbox newFlowbox
scroll.setVadjustment (Nothing::Maybe Gtk.Adjustment)
scroll `set` [#child := newFlowbox]
_ <- malloc_trim 0
return ()
window <- new Gtk.Window [ #application := app, #defaultWidth := 850
, #defaultHeight := 500, #child := scroll
, #titlebar := headerBar
, #title := "Image Memory Test" ]
window.present
fillFlowbox flowbox
fillFlowbox :: Gtk.FlowBox -> IO ()
fillFlowbox flowbox = do
forM_ [(1::Int)..21] $ \_ -> do
picture <- new Gtk.Picture [#contentFit := Gtk.ContentFitCover]
clamp <- new Adw.Clamp [ #child := picture, #maximumSize := 100
, #widthRequest := 100, #heightRequest := 150 ]
loadImage 460 picture
flowbox.append clamp
System.Mem.performMajorGC
return ()
loadImage :: Int32 -> Gtk.Picture -> IO ()
loadImage width pic = void $ forkIO $ do
bytes <- BS.readFile "img.jpg"
inputStream <- Gio.memoryInputStreamNewFromData bytes Nothing
let onImageLoaded _ result = do
Just pixbuf <- Pix.pixbufNewFromStreamFinish result
texture <- Gdk.textureNewForPixbuf pixbuf
pic.setPaintable $ Just texture
Pix.pixbufNewFromStreamAtScaleAsync inputStream width (-1) True
(Nothing::Maybe Gio.Cancellable) $ Just onImageLoaded |
I wrote a function to load an image into a
Picture
that looks like this:I run this function for a series of images and then load the
Picture
s into aFlowbox
. When i refresh the view, I use the functionflowboxRemoveAll
to clear it out, and then fill it with new images. It works well, except for the fact that it seems to hold onto the images in memory. So the more I refresh the view, the more memory it uses. What can I do to make sure the images get dropped when I refresh the view?The text was updated successfully, but these errors were encountered: