Murga-Projects Forums

Full Version: slicing images
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I get  attempt to index field '?' (a nil value)  at

Code:
  tile[key-1]:image(value)
    tile[key-1]:redraw()



when trying some transparent images.
Johns globe example is working fine, tiling weird, but loads.

A lot of my png files are not working. But others are.
The ones that are not working have been run through
a PNG optimizer. I wonder what the difference is?

Quote:
image1 = Fl_Shared_Image.get(fileName,ts*tc,ts*tc)
is the problem
image1 = Fl_Shared_Image.get(fileName)
stops the segmentation fault.

Well that really sucks. I was counting on the ability to resize the image automatically so you don't need to manually resize all your images to specific dimensions. I've tried image=Fl_Shared_Image.get(fileName) followed by image1=image:copy(ts*tc,ts*tc), but that also segfaults. I haven't tried image1:h(ts*tc); image1:w(ts*tc)

Considering getTiles() seems to work only for RGB images, I could try replacing Fl_Shared_Image.get() with Fl_PNG_Image() and others specific to the selected file's type. Perhaps it would do a better job of handling the image, but I'm just speculating.

Quote:
I get attempt to index field '?' (a nil value) at

Code:
tile[key-1]:image(value)
tile[key-1]:redraw()

when trying some transparent images.

It seems one or more transparent tiles aren't being added to the table? Another possibility is that removing the image resizing creates a gap between the number of tiles in the interface and the number of tiles in the image.

I wonder if it's possible to dynamically remove transparency from a PNG before resizing it in FLTK. That might solve the whole thing.

I found a partial solution to the segfault. Using the data() method that John demonstrated in imageDecodingTest.lua, I was able to limit the image depth to 3, which messes up the look of transparent images but at least prevents the crash.

So apparently there is a problem with getTiles() reading an alpha channel on a resized image?

EDIT: Made some minor changes to the preview behavior, added bmp to the filename filter and more comments

Code:
#!/home/dsl/bin/murgaLua-0.6.4

-- slide puzzle for MurgaLua 0.6.4+
-- 2008 mikshaw

-- window and image size are determined by these variables
ts=64 -- tile size
--ts=48 -- tile size
tc=4  -- number of tiles in one line
fr=5 -- frame size

if not Fl_Image.getTiles then
fltk.fl_alert("This program requires murgaLua 0.6.4 or newer.\n\n"..
"http://www.murga-projects.com/murgaLua/index.html")
os.exit(1)
end


function reset_hidden()
  tile[hidden]:box(fltk.FL_UP_BOX)
  tile[hidden]:labeltype(fltk.FL_NORMAL_LABEL)
end

function load_callback(object)
-- mod of a function (and a piece) written by John Murga
fileName = fltk.fl_file_chooser("Choose an RGB image", "Image Files (*.{jpg,png,bmp})", nil, nil)
if fileName then
  if image1 then image1:uncache() end -- don't know if this is needed
  image1 = Fl_Shared_Image.get(fileName,ts*tc,ts*tc)
prev_img:image(image1); prev_img:redraw() -- load the preview
test = image1:data()
-- force d(3) to prevent problems with alpha channel
image2 = fltk:Fl_RGB_Image(test[1], test["w"], test["h"], 3, test["ld"])
  myImages = image2:getTiles(ts,ts)
  for key,value in ipairs(myImages) do
    tile[key-1]:image(value)
--    tile[key-1]:redraw()
  end
  scramble()
  scrambutt:label("load an image")
end
end

function move_tile(t)
if Fl:event_button() >1 then prev_butt:do_callback() elseif image2 then
  local my_x,my_y,movex,movey=t:x(),t:y()
  -- tile must be adjacent to the missing piece
  if (my_x == tile[hidden]:x() and math.abs(my_y-tile[hidden]:y()) == ts)
  or (my_y == tile[hidden]:y() and math.abs(my_x-tile[hidden]:x()) == ts)
  then
    -- swap selected tile with the hidden one
    movex=tile[hidden]:x()
    movey=tile[hidden]:y()
    tile[hidden]:position(my_x,my_y)
    t:position(movex,movey)
    w:redraw()
  end
  -- check to see if puzzle is solved
  -- "ok" is incremented each time a tile is found in its proper place
  if start==0 then -- don't check puzzle during scramble
  local ok=0
  for i=0,tc*tc-1 do
    if tile[i]:x()==pos[i].col and tile[i]:y()==pos[i].row then
      ok=ok+1
    if ok==tc*tc then -- if ok == total number of tiles
      reset_hidden() -- reset the hidden tile
      fltk.fl_beep()
      scrambutt:label(plize[math.random(1,table.getn(plize))])
      for i=0,tc*tc-1 do tile[i]:set_output() end -- disable the buttons
      break
    end
    end
  end
  end
end
end

function scramble()
-- this picks a random tile to attempt to move and
-- repeats the process many times. Simply placing
-- tiles in random locations could potentially make
-- the puzzle impossible to solve
for i=0,tc*tc-1 do tile[i]:clear_output() end -- enable the buttons
if hidden then reset_hidden() end
-- turn a random tile into the missing piece
hidden=math.random(0,tc*tc-1)
tile[hidden]:labeltype(fltk.FL_NO_LABEL)
tile[hidden]:box(fltk.FL_DOWN_BOX)
start=1
local scram=0
while scram < 10000 do
  move_tile(tile[math.random(0,tc*tc-1)])
  scram=scram+1
end
start=0
w:redraw()
end

plize={"Great Job!", "Hooray for You!", "You don't suck!", "WIN!", "GODLIKE!", "CONGRATULATE YOU!"}

fltk.fl_register_images()
math.randomseed(os.time())
Fl:visible_focus(0) -- get rid of the ants
Fl:set_boxtype(fltk.FL_UP_BOX,fltk.FL_THIN_UP_BOX) -- looks a little more like tiles
Fl:set_boxtype(fltk.FL_DOWN_BOX,fltk.FL_THIN_DOWN_BOX)
w=fltk:Fl_Double_Window(ts*tc+fr*2,ts*(tc+0.5)+fr*3,"slide puzzle")

scrambutt=fltk:Fl_Button(fr,ts*tc+fr*2,ts*tc,ts/2,"load an image")
scrambutt:callback(load_callback)

-- array of tiles, top left to right, then move down
tile={}; pos={}
-- allow space for the frame
row=fr; col=fr
-- subtract one is used because the number of tiles starts at one
-- but the table starts at zero (easier to position them from zero)
for i=0,tc*tc-1 do
  tile[i]=fltk:Fl_Button(col,row,ts,ts)
  tile[i]:align(80)
  tile[i]:callback(move_tile)
  pos[i]={col=col,row=row}
  -- next piece is ts pixels to the right
  col=col+ts
  -- start the next row
  if col == ts*tc+fr then col=fr;row=row+ts end
end

-- offscreen button to call the preview window with a hotkey
prev_butt=fltk:Fl_Button(0,0,0,0,"&p")
prev_butt:callback(function() preview:show() end)
fltk:Fl_End() -- end of main window

-- image preview (cheat) window
preview=fltk:Fl_Window(ts*tc+fr*2,ts*tc+fr*2,"Image Preview")
prev_img=fltk.Fl_Box(fr,fr,ts*tc,ts*tc)

w:show()
Fl:run()

ok that does settle the issue with most files.
Some files, I guess with alpha are loading weird.
I'm assuming that's from the extra channel, but I don't want to say for sure. All I know is that is a result of forcing a color depth of 3 on images that would normally be 4. (i guess maybe those numbers would be bytes, so a depth of 4 would be an image with 32-bit color?). I'm hoping to find a way to avoid having to do this....maybe I'll just not allow any 32-bit images to be loaded.

Another potential segfault can be seen when increasing the size and number of tiles. This is another mystery to me.

mikshaw Wrote:
I'm assuming that's from the extra channel, but I don't want to say for sure. All I know is that is a result of forcing a color depth of 3 on images that would normally be 4. (i guess maybe those numbers would be bytes, so a depth of 4 would be an image with 32-bit color?). I'm hoping to find a way to avoid having to do this....maybe I'll just not allow any 32-bit images to be loaded.

I think that the PNG (and maybe also JPG) support needs some fixing. I haven't looked into the code but I guess the Fl_Shared_Image.get method returns (obviously) an RGBA image (when the loaded image contains an alpha channel). It might work better when you use Fl_PNG_Image() instead. It is supposed to "handle color- and alpha-based transparency". (Whatever that means)

The problem is that PNG has a few different color modes:
1) indexed with 1,2,4,8 bits per pixel
2) grey with 1,2,4,8,16 bit per pixel
3) grey with alpha: 16 or 32 bit per pixel (with 8 or 16 bit alpha channel)
4) RGB with 8 or 16 bits per channel this makes 24 or 48 bits per pixel
5) RGBA with 8 or 16 bits per channel this makes 32 or 64 bits per pixel

When this raw RGB(A) data is handled the pixel and channel size has to taken into account, when the memeroy is reserved and also (obviously) when the individual pixels accessed. I haven't looked into the sources, but I guess that gettiles() just assumes a pixel size of 3 bytes. Then you would get ecactly the effect that you can see. When gettiles scans the RGBA values you can see that alternating one of the RGB values is blanked out, because in memory it looks like RGB0RGB0RGB0RGB0.... which is then scanned as RGB,0RG,B0R,GB0,RGB,....

mikshaw Wrote:
Another potential segfault can be seen when increasing the size and number of tiles. This is another mystery to me.

If the C/C++ code makes wrong assumptions about the pixel size, most likely it tries to access a memory location which is not backed by physical memory, which will result in a segmentation fault. Even if the program accessed a wrong address it is not necessary that a segmentation fault occures. There are up to 4095 bytes after the last valid address it can access, before a segfault occures. It is even possible that valid addresses, used by other objects follow, so even if something goes wrong, it may go unnoticed under some conditions and under some others not.
(If you want, I can elaborate it a little more.)

Juergen

changing

Code:
myImages = image2:getTiles(ts,ts)

to

Code:
myImages = image1:getTiles(ts,ts)


prevented the destruction of the the image as posted previously.

I'll get a screen shot up in a bit and we'll see what else goes wrong :-)

also note I changed

Code:
  image1 = Fl_Shared_Image.get(fileName,ts*tc,ts*tc)

to

Code:
   image1 = Fl_Shared_Image.get(fileName)
  image1:h(ts*tc); image1:w(ts*tc)


But that didn't seem to make a world of difference.

see attached file for my version of script

hmm still crashing out on some files, I put a trace at

Code:
for key,value in ipairs(myImages) do
  print(key,value)
    tile[key-1]:image(value)
tile[key-1]:redraw()
end


John's globe.png returns:

Code:
1       userdata: 0x59e184
2       userdata: 0x59e134
3       userdata: 0x59dc64
4       userdata: 0x59dcb4


another working file returns similiar to 15.
a crashing file returns and crashes after 17.

The Problem appears to be image size.
The same image that crashes at image size 640 or 320 works fine at image size 300.

Quote:
I guess the Fl_Shared_Image.get method returns (obviously) an RGBA image (when the loaded image contains an alpha channel). It might work better when you use Fl_PNG_Image() instead. It is supposed to "handle color- and alpha-based transparency". (Whatever that means)

That makes sense. It's something that crossed my mind but I haven't done any tests yet. Still, I don't see that explaining why an RGBA file loaded with Fl_Shared_Image.get() would tile properly if it's not resized.

Quote:
When gettiles scans the RGBA values you can see that alternating one of the RGB values is blanked out, because in memory it looks like RGB0RGB0RGB0RGB0.... which is then scanned as RGB,0RG,B0R,GB0,RGB,...
...
If the C/C++ code makes wrong assumptions about the pixel size, most likely it tries to access a memory location which is not backed by physical memory, which will result in a segmentation fault.

You see, this is exactly the sort of thing that amazes me. I wouldn't have a clue to even begin speculating what the prblems are, but your explanations makes complete sense =o)

Quote:
If you want, I can elaborate it a little more.

I wouldn't mind at all, if you don't mind. I might not be able to follow it, but at the least I might have a little better understanding of how images are handled and about why I have to tweak things so often to prevent crashes.

I noticed two more things that "puzzle" me, one about getTile() and one about my script. The first is that getTile() seems to vertically compress the tiles slightly (maybe by a pixel?). This creates a shift in the tiled image and a small line along the bottom of the image:

[attachment=33]

The other is the fact that the scrambling occasionally results in teh unsolvable puzzle that I had consciously tried to avoid by not simply choosing random locations for the tiles:

[attachment=34]
The two tiles in the bottom right are apparently impossible to swap. Maybe I haven't thought it through completely yet, but it seems like this should never happen if the scramble is done in a loop that checks to make sure a tile is adjacent only to the "hidden" tile before doing a swap.
Then again, it could be an issue of changing images before the puzzle is solved. If that's the case then maybe the hidden tile is in an inappropriate place to start with. If so I think I'll need to force the tile postitions to reset with every image change.

Quote:
myImages = image1:getTiles(ts,ts)
prevented the destruction of the the image as posted previously.

Yes, that's how I had it originally, but was the cause of my original problem...crashing with resized 32-bit images. I'm still considering simply refusing 32-bit fr the time being.

Quote:
also note I changed
image1 = Fl_Shared_Image.get(fileName,ts*tc,ts*tc)
to
image1 = Fl_Shared_Image.get(fileName)
image1:h(ts*tc); image1:w(ts*tc)
But that didn't seem to make a world of difference.

I tried that too, but w() and h() of images are protected methods. I never quite understood the definition of protected as it relates to FLTK, but it seems that you cannot use it to directly set the dimensions of an image. That's why I specify height and width in Fl_Shared_Image.get().
I also tried image2=image1:copy(ts*tc,ts*tc), which results in the same behavior.

Pages: 1 2 3 4 5
Reference URL's