Hey guys! The app I work on uses an avatar system. The images are stored on a user’s device and from time to time they are updated.
I would like to show an app logo image if a downloaded image is corrupted.
I produce a corrupted png by removing its entire content leaving it as an empty file. A warning about a non-valid image follows. It is great, but how to make an app detect and react to that warning?
local image
local status, err = pcall( function() image = display.newImage('img.png', 100, 100) end )
if status and image then
print( 'no errors ' )
else
print( 'errors ' )
end
It returns NO errors, though a warning is hopefully shown on the console.
No matter what, image is always a table at the end of the execution of the function. It would be reasonable to have it as nil.
A good app has to work out all the possible scenarios to be “unsinkable”. So what ever goes from a user (An sql-injection, corrupted image, json/xml, a file of 1000GB, etc.) can hang the app in the best scenario…
I think I got a work-around solution.
After opening image, I will save it and re-open. If it was saved, then it is a valid image, if not… image will return nil
(still testing…)
function image_check_sub(path_str_A, a_dir)
local image_A = display.newImage(path_str_A, a_dir, 100, 100)
local path_str_B = "tmp_" .. path_str_A
display.save(image_A, path_str_B, a_dir)
local image_B = display.newImage(path_str_B, a_dir, 100, 100)
if image_B == nil then
-- here we can remove both files physically via os.delete
if image_A ~= nil then
image_A:removeSelf()
end
return false
end
-- here we must remove file B physically via os.delete
image_A:removeSelf()
image_B:removeSelf()
return true
end
I was just working on something related to this and I remembered this old thread and I figured I’d come share the solution here in case someone runs into this at a later time.
If you create an invalid image, the display object’s width and height will be 0, i.e.
local image = display.newImage( "img.png" )
if image.width == 0 then
print( "image is invalid" )
end
Main Function: loadImage(path, parentGrp)
– path -> Path to your image (In this example an PNG File in Folder “assets/”)
– parentGrp -> parent DisplayGroup the image will insert (OPTIONAL)
– return-value is the DisplayObject and hasError (BOOLEAN)
If an error happen:
If the load process has an error, the function return a DisplayObject(white rectangle).
The advantage the source-code will not directly crash later. You get a the visual feedback and error log feedback.
Already an error, no double outputs: local hasImageDir = {} hasImageDir save the state if already got an invalid Image, to avoid double log errors outputs.
---@type table<string, boolean>
local hasImageDir = {}
---@return DisplayObject, boolean
---@param parentGrp DisplayGroup
---@param path string
function loadImage(path, parentGrp)
if (path ~= nil) then
local myImagePath = "assets/" .. path .. ".png" -- < HERE complete the asset-path and ending.
---@type DisplayObject
local myImage
local hasError = false
if (hasImageDir[myImagePath] == nil or hasImageDir[myImagePath] == true) then
myImage = display.newImage(myImagePath)
if (hasImageDir[myImagePath] ~= nil) then
hasImageDir[myImagePath] = not (myImage == nil)
end
if (myImage == nil) then
print("assetManager <loadImage> not found:", myImagePath) --- here you can add your error log profiler
hasError = true
myImage = display.newRect(5, 5, 5, 5)
end
else
hasError = true
myImage = display.newRect(5, 5, 5, 5)
end
if (parentGrp ~= nil and parentGrp.insert ~= nil) then
parentGrp:insert(myImage)
end
return myImage, hasError
else
print("assetModel <loadImage> param path is NIL!")
myImage = display.newRect(5, 5, 5, 5)
if (parentGrp ~= nil and parentGrp.insert ~= nil) then
parentGrp:insert(myImage)
end
return display.newRect(5, 5, 5, 5), true
end
end
---@type DisplayGroup
local knightGrp = display.newGroup()
local plateLeft, hasError = loadImage("knight/plate_left")
knightGrp:insert(plateLeft)
print("image plateLeft has Error:", hasError)
local plateRight, hasError = loadImage("knight/plate_right", plateRightGrp)
print("image plateRight has Error:", hasError)
You are missing the problem that was being discussed in this thread. Your solution will fail to address aforementioned issue.
The problem was that if you had a file, say “image.png” in your folder, but that image file itself is corrupted or if it was downloaded remotely, it may not even really be an image, but just some html code or server response saved as png or jpg.
In this case, if you try to create a display object using: local image = display.newImage( "image.png" )
Then you will receive the following warning from the engine: WARNING: Failed to find image 'image.png'
However, image will still be created as if it were a display object, so the handle won’t be nil. I hadn’t encountered this behaviour before as I hadn’t run into any errors with downloading remote files that aren’t what they were supposed to be.
You can, for instance, create a file: “test.txt” and write something inside of it. Then change the extension to “test.png” and try loading it in Solar2D. Solar2D will recognise it as an image, so the display object gets created, but it will fail to create the fill, so it won’t be rendered.
When it fails to render, you just need to check for the display object’s width or height (there are other properties that will also be affected, but these two are the easiest). If the width or height is 0, then there’s nothing rendered, so the display object didn’t get created properly.