simonkrauter / NiGui

Cross-platform desktop GUI toolkit written in Nim
MIT License
718 stars 50 forks source link

Image.loadFromFile() does not close the file #131

Open KnorrFG opened 3 years ago

KnorrFG commented 3 years ago

Reading an image with the method mentioned above will leave the file open, which makes it impossible to delete or overwrite it on Windows.

add-IV commented 1 year ago

I have this issue too, are there any workarounds?

simonkrauter commented 1 year ago

NiGui does not open the file itself, it calls GdipCreateBitmapFromFile to load the file. Apparently Windows does not close the file. I found this: https://stackoverflow.com/questions/4978419/c-gdi-loading-an-image-from-a-file-then-deleting-the-file-before-unloading

add-IV commented 1 year ago

I researched it a bit more yesterday too. It might be what you found, but since there is no option to close the file handle in the gdiplus flat api , it might be something to do with lazy compression instead. However, the problem might also be with the flat api itself: gdip from here

Seems like changing GdipCreateBitmapFromFile to GdipCreateBitmapFromStream might fix this problem tho.

add-IV commented 1 year ago

apparently GdipDisposeImage() should also work for bitmaps, however even if i use that i still get the problem

add-IV commented 1 year ago

here is an example of what im trying to do:

import nigui

app.init()

var window = newWindow("minimal")
window.width = 300.scaleToDpi
window.height = 300.scaleToDpi

var openImageButton = newButton("open")

var controlsContainer = newLayoutContainer(Layout_Vertical)
window.add controlsContainer

controlsContainer.add openImageButton

openImageButton.onClick = proc(event: ClickEvent) = 
    var image = newImage()
    image.loadFromFile("minimal.png")

    var imageWindow = newWindow("Preview")
    imageWindow.width = image.width
    imageWindow.height = image.height

    var control1 = newControl()
    control1.widthMode = WidthMode_Fill
    control1.heightMode = HeightMode_Fill

    imageWindow.add control1

    control1.onDraw = proc(event: DrawEvent) =
        let canvas = event.control.canvas
        canvas.drawImage(image, 0, 0, image.width, image.height)

    imageWindow.show()

var saveImageButton = newButton("save")

controlsContainer.add saveImageButton

saveImageButton.onClick = proc(event: ClickEvent) = 
    var image = newImage()
    image.loadFromFile("minimal2.png")
    image.saveToPngFile("minimal.png")

window.show()
app.run()

the open button opens minimal.png in a new window as preview the copy button overrides minimal.png

after i close the preview window, copy cant access minimal.png

add-IV commented 1 year ago

I tried adding:

# platform_impl.nim line 392
proc pGdipDisposeImage(bitmap: var pointer) =
  let status = GdipDisposeImage(bitmap)
  if status != 0:
    pCheckGdiplusStatus(status, "Failed to dispose of Image")

# platform_impl.nim line 842
method dispose(image: Image) =
  let canvas = cast[CanvasImpl](image.canvas)
  pGdipDisposeImage(canvas.fBitmap)

# nigui.nim line 653
method dispose*(image: Image) {.base.}

# minimal.nim
imageWindow.onDispose = proc(event: WindowDisposeEvent) =
    image.dispose()

which seems like it should work, but it doesn't. The image gets disposed but the file still is not closed.