Murga-Projects Forums
slicing images - Printable Version

+- Murga-Projects Forums (http://www.murga-projects.com/forum)
+-- Forum: Project Forums (/forumdisplay.php?fid=1)
+--- Forum: MurgaLua - General (/forumdisplay.php?fid=2)
+--- Thread: slicing images (/showthread.php?tid=269)


slicing images - mikshaw - 01-26-2008 06:28 AM

I wonder if anyone knows if it's possible to slice up a single image in FLTK, specifically apply one portion of an image to one widget and another portion to a different image?

I was looking at draw(), which apparently allows for an offset, but I don't understand how it works. I either get a "Bad drawable" (something like that) error or segfault depending on what I try.

I'm building one of those old slide puzzles where you have to unscramble a picture, and was hoping I could use a single arbitrary image rather than having to create a series of pieces specifically for the puzzle.

Code:
ts=48 -- tile size
tc=6  -- number of tiles in one line
fr=10 -- frame size

function move_tile(t)
  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
    movex=tile[hidden]:x()
    movey=tile[hidden]:y()
    tile[hidden]:position(my_x,my_y)
    t:position(movex,movey)
    w:redraw()
  end
end

w=fltk:Fl_Window(ts*tc+fr*2,ts*tc+fr*2,"slide puzzle")

-- array of tiles, top left to right, then move down
tile={}
-- allow space for the frame
row=fr; col=fr
for i=0,tc*tc-1 do
  tile[i]=fltk:Fl_Button(col,row,ts,ts)
  tile[i]:callback(move_tile)
  tile[i]:label(i+1) -- simple numbers could be replaced by images
  -- 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

-- turn a random tile into the missing piece
math.randomseed(os.time())
hidden=math.random(0,24)
tile[hidden]:label("")
tile[hidden]:box(fltk.FL_DOWN_BOX)

-- next step would be to jumble the tiles, but I haven't worked that out.

w:show()
Fl:run()




RE: slicing images - JohnMurga - 01-26-2008 08:48 AM

Hey,

I have done this for you, unfortunately it required implementing the fl_read_image function, which wasn't there before.

Works pretty well ... Unfortunately you need new binaries that I won't release until tomorrow.

Cheers
JohnM


RE: slicing images - mikshaw - 01-26-2008 08:51 AM

sweet! Thanks.
I don't consider that unfortunate at all =o)


RE: slicing images - JohnMurga - 01-27-2008 06:38 AM

Hey,

I have something using fl_read_image but it is VERY flaky under Linux ...
Might post it later tonight.

I am thinking a tiling API is in order ...

Gimme a couple of days, my other half is demanding my time right now :-(

Cheers
JohnM


RE: slicing images - mikshaw - 01-27-2008 07:26 AM

No rush at all. I'm just playing around with the scrambling and the detection of a completed puzzle, and that's all I planned to do on that project until the next murgaLua release.


RE: slicing images - mikshaw - 02-25-2008 05:49 AM

Well, it seems to be working very well. Thank you!
Here is a new improved version including the new getTiles()

NOTE: I tested the previous version once in Windows, and the desktop froze. I don't know if it was a problem with the script or with Windows, but just beware!

Another note of warning:
For some reason it segfaults when trying to load a transparent PNG. John's tile script succeeds with the very same image, so I don't know what I did to break it. Adding os.exit() just before getTiles() doesn't segfault, so i'm confused.

Code:
#!/usr/bin/murgaLua

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

-- window and image size are determined by these variables
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

fltk.fl_register_images()
math.randomseed(os.time())

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 written by John Murga
fileName = fltk.fl_file_chooser("Choose an RGB image", "Image Files (*.{jpg,png})", 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)
  myImages = image1: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)
  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
    movex=tile[hidden]:x()
    movey=tile[hidden]:y()
    tile[hidden]:position(my_x,my_y)
    t:position(movex,movey)
    w:redraw()
  end
  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
      reset_hidden()
      fltk.fl_beep()
      scrambutt:label(prize[math.random(1,table.getn(prize))])
      for i=0,tc*tc-1 do tile[i]:set_output() end -- disable the buttons
      break
    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

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

Fl:visible_focus(0) -- get rid of the ants
Fl:set_boxtype(fltk.FL_UP_BOX,fltk.FL_THIN_UP_BOX)
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

w:show()
Fl:run()




RE: slicing images - iGame3D - 02-25-2008 08:08 AM

I'm not having any luck importing any image. Seg fault each time.

When clicking on a tile I get a crash with:

Code:
/bin/murgaLua: miktiles.lua:43: attempt to index field '?' (a nil value)
stack traceback:
        miktiles.lua:43: in function <miktiles.lua:40>
        [C]: in function 'run'
       miktiles.lua:118: in main chunk
        [C]: ?


Thats the value : hidden
setting hidden = 0 at outside the functions solves That crash but I don't know what else it breaks.

The previous version had an empty tile, this version doesn't.
Don't know if that makes a difference.


RE: slicing images - mikshaw - 02-25-2008 12:22 PM

Quote:
setting hidden = 0 at outside the functions solves That crash

I wonder if there might be a difference in the way variables are created. In Linux it seems that a global variable can be created within a function. Maybe on Mac a global variable has to be initialized outside a function before it can be set in a function? If so, I think the start variable may need to be initialized too (start=1).


RE: slicing images - iGame3D - 02-25-2008 07:15 PM

I put a print statement at the top of each of your functions, and the only thing that seems to get passed is "move_tile" when clicking on a tile.

"load_callback" will fire when attempting to load an image...and crashes.
I've attached the crash log in case it means anything to John.


RE: slicing images - iGame3D - 02-25-2008 07:31 PM

FIXED

in load_callback

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

is the problem

Code:
image1 = Fl_Shared_Image.get(fileName)

stops the segmentation fault.


RE: slicing images - iGame3D - 02-25-2008 08:15 PM

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?


RE: slicing images - mikshaw - 02-25-2008 10:26 PM

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.


RE: slicing images - mikshaw - 02-26-2008 04:47 AM

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()




RE: slicing images - iGame3D - 02-26-2008 06:22 AM

ok that does settle the issue with most files.
Some files, I guess with alpha are loading weird.



RE: slicing images - mikshaw - 02-26-2008 10:41 PM

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.


RE: slicing images - Juergen - 02-27-2008 04:42 AM

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


RE: slicing images - iGame3D - 02-27-2008 05:53 AM

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


RE: slicing images - iGame3D - 02-27-2008 06:30 AM

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.


RE: slicing images - mikshaw - 02-27-2008 07:08 AM

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.


RE: slicing images - mikshaw - 02-27-2008 07:18 AM

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.


RE: slicing images - iGame3D - 02-27-2008 07:52 AM

I noticed that green line of pixels at the bottom as well.

I'm having great luck with this function now:
It will resize large images and prevent most of the problems so far.

Code:
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})", nil, nil)
if fileName then
  if image1 then image1:uncache() end -- don't know if this is needed
  image1 = Fl_Shared_Image.get(fileName)
-- set a window (or an object) image property to the image data
  w:image(image1,ts*tc,ts*tc)
-- set the image data to the a resized copy of the image data  
  image1 = w:image():copy(ts*tc,ts*tc)  
-- get table of tiles getTiles  
  myImages = image1:getTiles(ts,ts)
-- parse tile table into image data of objects
  for key,value in ipairs(myImages) do
  if  key < 16 then
    tile[key-1]:image(value)
tile[key-1]:redraw()
end
end
scramble()
  scrambutt:label("load an image")
end
end


However I broke the game play functionality so I have to go back a few steps somewhere.

I  think I've still segfaulted, and I've got some weird crash logs.

Code:
Thread 0 Crashed:
0   <<00000000>>     0xffff08a0 __memcpy + 256 (cpu_capabilities.h:228)
1   murgaLua                0x000595a6 0x1000 + 361894
..etc..etc
7   murgaLua                0x0015cd53 Fl_Widget::do_callback() + 29
8   murgaLua                0x0009d252 0x1000 + 639570
..etc..etc
12  com.apple.HIToolbox     0x92def4d7 DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) + 1093
13  com.apple.HIToolbox     0x92deeb7c SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) + 304
14  com.apple.HIToolbox     0x92df5f7c SendEventToEventTarget + 56

etc...etc..

Once I've got the game play back I'll see if those crashes persist.


RE: slicing images - JohnMurga - 02-27-2008 08:04 AM

We have a saying where I come from about people who make assumptions :-)

Juergen, in a fraction of the time that took to compose your response you could have checked the source ...

This would have demonstrated that no assumptions are being made about colour depth, as I actually added an additional PNG specifically test the different RGB type.

I haven't had time to look at this properly yet, but I suspect that it could have something to do with the resizing process (which I am unfamiliar with), and what happens when the dimensions of the picture are not divisible by the dimensions of tiles.

Either way it segfaults, which it shouldn't, and sucks big time, so it's my priority one bug.

Cheers
JohnM


RE: slicing images - JohnMurga - 02-27-2008 08:28 AM

BTW ...

What I would suggest for this is :

+ Create an off-screen buffer of the required size (which is a multiple of the tile dimensions).
+ Draw the re-sized picture to this off-screen buffer.
+ Get the image for the off-screen buffer (which should be perfectly normal).
+ Call getTiles on this.

I'll try it myself if I get through all my mails.

Cheers
JohnM


RE: slicing images - mikshaw - 02-27-2008 08:31 AM

That sounds reasonable. I'll test it out after dinner.
Thank you again.


RE: slicing images - Juergen - 02-27-2008 09:41 AM

JohnMurga Wrote:
We have a saying where I come from about people who make assumptions :-)

Juergen, in a fraction of the time that took to compose your response you could have checked the source ...

Now you are under the assumption that I had the code readily available at the time. ;-)

JohnMurga Wrote:
This would have demonstrated that no assumptions are being made about colour depth, as I actually added an additional PNG specifically test the different RGB type.

In the meantime I have looked into the code and saw that it is depth independent.

JohnMurga Wrote:
I haven't had time to look at this properly yet, but I suspect that it could have something to do with the resizing process (which I am unfamiliar with), and what happens when the dimensions of the picture are not divisible by the dimensions of tiles.

After a first quick scan it looks like it doesn't matter. I first thought this also when I saw the code, but it looks like (if I haven't overlooked anything) that it truncates w()%xSize pixels at the right and h()%ySize lines at the bottom part of the image.

JohnMurga Wrote:
Either way it segfaults, which it shouldn't, and sucks big time, so it's my priority one bug.

I haven't had the time to look deeper into it, but have you checked if ld() is always !=0?

Juergen


RE: slicing images - mikshaw - 02-27-2008 12:40 PM

I didn't test yet. I have to learn about the offscreen drawing functions first, and since I got kinda buzzed at dinner I lost my ability to grasp new concepts =op

There is one thing I'm curious about, though. You said "I suspect that it could have something to do with the resizing process (which I am unfamiliar with), and what happens when the dimensions of the picture are not divisible by the dimensions of tiles". In this particular instance the dimensions of the picture are always divisible by the dimensions of the tiles because the dimensions of the picture are a multiple of the dimensions of the tiles.

A happy coincidence, though (or what seems to be a coincidence), is that your getTiles() seems to create the tiles from right to left, top to bottom, which is precisely the method I used to create the tiles in the interface. So the index of the image tiles table is the same as the index of the interface tiles, with the exception that I started at 0 and you start at 1

I've been wondering what other uses getTiles could have. Maybe an image map that is controlled by buttons rather than coordinates (buttons would be easier to program, i think). Maybe an image-based gui that is resizable, although I'm not sure if the images can be dynamically resized as a resizeable window is dragged.


RE: slicing images - iGame3D - 02-27-2008 03:04 PM

Ok to test bugs see:

Working file:
murgaLua/examples/images/Globe.png

and

segfault file:
murgaLua/gfx/murgaLua.png

--------------------------------------------
I mangled that segfault file a dozen different ways.
The final difference between crash and no crash is that
segfault files load into photoshop with the initial layer set to "background".
If layers are flattened in photoshop, a working file will become a segfault file.

Files that work load into photoshop with the initial layer on "layer 0".
If that image is saved with a solid layer at the bottom, it segfaults.

If an alpha channel is then applied to that bottom layer and saved it works.

Ok here's something wierd.
It will segfault if the alpha channel is 100% solid.
I put a one pixel dot into the alpha channel.
It doesn't segfault.


RE: slicing images - mikshaw - 02-27-2008 11:51 PM

Your last mod of the load function, Bill, initially sets the image on a separate window, resizing it at that time. The size of the copy therefore doesn't need to be specified.

Do you think setting the image to a hidden window first and then tiling that data does any help over tiling the original data? Logically I don't see any difference between the two methods, but if there is an improvement I already have a "preview" window implemented that can be used for this purpose. The script I have locally is becoming quite different than the last one posted, so after I do some experiments with John's suggestion I'm going to post the newer version.


RE: slicing images - Juergen - 02-28-2008 03:50 AM

I have now tested it a little bit and I can't get any segmentation faults (is there a png somewhere available that faults?)
I have tested mikes sliding puzzle and I haven't found any problems with RGB or RGBA png's.

Although in the previously posted source the following lines:

Code:
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"])

will definitely not work, because it only "lies" to the Fl_RGB_Image constructor about the data, but doesn't actually change it.
image2=image1 works on my system without a problem. (If there is a png that pruduces a crash then could someone post it?)
Also if the picture should be scaled (and not cropped, which doesn't make sense since the program doesn't know which side it should crop) image1=image1:copy(math.floor(image1:w()),math.floor(image1:h())) would make sense.

Juergen

P.S.: On which OS have those tests been performed?


RE: slicing images - iGame3D - 02-28-2008 04:22 AM

The path to a segfaulting file is: murgaLua/gfx/murgaLua.png
All my tests have been Mac based.

mikshaw Wrote:
Your last mod of the load function, Bill, initially sets the image on a separate window, resizing it at that time. The size of the copy therefore doesn't need to be specified.
Do you think setting the image to a hidden window first and then tiling that data does any help over tiling the original data?


I set  the image of the main window, the preview window
never did anything so I didn't even notice the code for it until you mentioned it.

When importing images for the iGame3D interface icons,
I had to place the image inside of a button and copy the image at the size I needed it
to be, else I ended up with icons bigger than my buttons.
I don't think just setting the size of the image actually resizes it.

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


Doesn't do any resize to the image.
Adding:

Code:
  w:image(image1,ts*tc,ts*tc)
  image1 = w:image():copy(ts*tc,ts*tc)


gets the image resized. I could have added an object for that purpose, a button, whatever, but its easier to use the window as a storage device, since it can hold the image data and allow the copy just as easily.

If I don't resize then anything larger than 320 pixels crashes.


RE: slicing images - Juergen - 02-28-2008 04:30 AM

iGame3D Wrote:
See above for seg faulting images.
All my tests have been Mac based.


If you mean the murgaLua.png image, this isn't in the murgaLua distribution. But I went extra (before I posted the last message) to the igames3d site and picked it out of the SVN, because I guessed it could be found there. If it is this (https://igame3dsource.svn.sourceforge.net/svnroot/igame3dsource/ig3d_win_mingw/murgaLua/gfx/murgaLua.png) image, I have tested it and it works perfectly.

Where the tests performed on a big endian machine or an intel based one?

Juergen


RE: slicing images - mikshaw - 02-28-2008 04:55 AM

I believe I found a solution to the alpha problem. I haven't looked into the last post by juergen, but the solution seems to be related. The Fl_RGB_Image() line was definitely incorrect. To fix it I obtained the color depth from the original unscaled and unsliced image data, and applied that to Fl_RGB_Image():

Code:
image2 = fltk:Fl_RGB_Image(test[1],ts*tc,ts*tc,image1:d(),test["ld"])

This will use 4 for RGBA images and 3 for RGB images. I haven't tested many images yet, but it works for the transparent PNGs that were crashing before.

There are also some changes to the puzzle behavior:
The preview/cheat is a button that overlays the puzzle instead of a separate window (right-click or middle-click to toggle it). It also behaves as an image-load button initially and when the puzzle has been solved.
I hopefully have fixed the most recent bug I found by returning all tiles to their original positions before scrambling.
The Escape key closes the program only if the preview is not displayed while the puzzle is scrambled. Otherwise it hides the preview.

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=80 -- tile size
tc=4  -- number of tiles in one line
fr=5 -- frame size
--img_dir="/mnt/hda4/shared/image/slide_puzzle"
--img_dir="/home/mik/image/slide_puzzle"

if not Fl_Image.getTiles then
err="This program requires murgaLua 0.6.4 or newer.\n\n"..
"http://www.murga-projects.com/murgaLua/index.html"
if fltk then fltk.fl_alert(err) else print(err) end
os.exit(1)
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})", img_dir, nil)
if fileName then
  start=1 -- used for controlling window callback and scramble behavior
  if image1 then image1:uncache() end -- don't know if this is needed
  image1 = Fl_Shared_Image.get(fileName)
  test = image1:copy(ts*tc,ts*tc):data()
  image2 = fltk:Fl_RGB_Image(test[1],ts*tc,ts*tc,image1:d(),test["ld"])
  myImages = image2:getTiles(ts,ts)
  for key,value in ipairs(myImages) do
    tile[key-1]:image(value)
    -- make sure all tile locations are reset correctly
    -- fixes a bug if image is changed while tiles are scrambled
    tile[key-1]:position(pos[key-1].col,pos[key-1].row)
  end
  preview:image(image2)  -- load the preview
  scramble()
  scrambutt:label("load an image")
end
end

function move_tile(t)
if Fl:event_button() >1 then preview:show() 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
      fltk.fl_beep()
      scrambutt:label(plize[math.random(1,table.getn(plize))])
      preview:show()
      start=1
      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
preview:hide()
if hidden then -- reset hidden tile
  tile[hidden]:box(fltk.FL_UP_BOX)
  tile[hidden]:labeltype(fltk.FL_NORMAL_LABEL)
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)
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)
scrambutt:tooltip([[
Click this button at any time to load a new image.

** Puzzle Controls **
Left click:  Move a tile.
Right or Middle click:  Toggle image display.
Esc:  Quit program, or close image display if it is visible.
]])

-- 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

-- image preview (cheat)
preview=fltk:Fl_Button(fr,fr,ts*tc,ts*tc)
preview:callback(function()
if start==1 then load_callback()
else preview:hide() end
end)

--preview:hide()
w:callback(function()
  -- Esc just hides the preview if it's visible
  if preview:visible()==1 and start==0 then preview:hide()
  else os.exit() end
end)

start=1
w:show()
Fl:run()




RE: slicing images - iGame3D - 02-28-2008 07:23 AM

Works awesome, nothing broke or crashed in anyway yet.
What ever mikshaw did fixed the problem, so far.

To answer Juergen quickly, that image is close enough, I had the path wrong earlier, its murgaLua/gfx/murgaLua.png, same thing. I'm on an Intel iMac.


RE: slicing images - mikshaw - 02-28-2008 08:04 AM

It's still crashing when the tile size is "too big" for the number of tiles used (I don't know what makes it too big).
So far I haven't had any success creating an offscreen image as John suggested...can't seem to translate his offscreen drawing method to images.

I haven't seen any other problems apart from that and the slight pixel shift mentioned earlier.

I think the next step will be to see if I can create a tileable image from a xpm/xbm/gif. It seems like it should be easy enough by borrowing more from John's imageDecodingTest.lua


RE: slicing images - JohnMurga - 02-28-2008 08:43 AM

I'll do the off-screen thing ... Work has been kinda crazy and my better half is very sick, so murgaLua has been taking a back seat at the moment, but as soon as I get a minute ;-)

Off-screen would work transparently for all image types too, as they'd be converted to RGB.

Cheers
JohnM


RE: slicing images - iGame3D - 02-28-2008 03:41 PM

JohnMurga Wrote:
Off-screen would work transparently for all image types too, as they'd be converted to RGB.


RGBA I hope?
Else  every off screen render will produce a black background in the resulting image.

I was having some fun with this script, my old random square test, with the off screen drawing.
But you'll see there is always a black bacground. Need that alpha!


Code:
-- random squares drawn offscreen with murgaLua
-- modification of murgaLua/examples/new/offscreen.lua

math.randomseed(os.time())
-- these values will depend on the project
   offscreenHeight, offscreenWidth  = 320, 320

-- these values are used against copy() and are randomized later in this script.
   redrawHeight , redrawWidth  = 200, 200

-- counter values keep track of the color cycling on the random filled rects
   colorcounter, basecounter, counterStep, counterLimit = 0 ,1, 7, 255

-- if the quads are larger than the grid there will be overlap
   gridSize , quadSize = 8, 8  

function random_colorsquares()
-- cycle colors
    colorcounter = colorcounter + counterStep
    if colorcounter > counterLimit then colorcounter = counterStep end
    fltk.fl_color(basecounter + colorcounter)  
-- randomized quadsize
    local Rs = math.random(quadSize-15,quadSize)
    
-- distance from center on each side of the quad  
    local quadHalf = math.floor(Rs/2)
    
    -- find a Random Point in the buffer
    local Rx, Ry = math.random(1,offscreenHeight) ,  math.random(1,offscreenWidth)
    
    -- match x,y to a valid point based on grid size
    local Px = (math.floor((Rx+(quadSize/2))/gridSize)*gridSize)-1
    local Py = (math.floor((Ry+(quadSize/2))/gridSize)*gridSize)-1
    
    -- find the top left corner of the new quad
    local Left,Top = Px-quadHalf, Py-quadHalf
    -- draw the filled rectangle
    fltk.fl_rectf(Left , Top ,Rs,Rs)
end  

function offScreenDraw(w)
    collectgarbage()
    offScreenBuffer = murgaLua.createOffscreenBuffer(offscreenWidth , offscreenHeight)
    offScreenBuffer:startOffScreenDrawing()    
  -- On Linux we have to clear the buffer (not on Mac or Win32)
    fltk.fl_color(0)
  -- Linux will segfault unless we set the font before using fl_draw
     fltk.fl_font(fltk.FL_HELVETICA_BOLD,14)
     fltk.fl_draw("OffScreen Drawing",10,20)
  -- the offscreen bufffer is now ready for input on all platforms

  -- some random colored squares
     for i = 1 , 2048 do random_colorsquares() end
      
  -- randomized redraw height for fun
     local rH = math.random(redrawHeight,redrawHeight*4)
     local rW =  math.random(redrawWidth,redrawWidth*4)
  
  -- the offscreen buffer can be copied and resized
     imageData = offScreenBuffer:getOffScreenImage():copy(rH , rW)
  
  -- insert the offscreen buffer as image data of an object
     getOffScreen_Button:image(imageData)
    getOffScreen_Button:redraw()
  
-- end the off screen drawing
    offScreenBuffer:endOffScreenDrawing()
  
end
  
  do local object = fltk:Fl_Double_Window(210, 210, "OffScreen drawing");
    window = object;
    do getOffScreen_Button = fltk:Fl_Button(5, 5, 200, 200, "Get off screen image");
    end -- Fl_Button* getOffScreen
  end
  
  getOffScreen_Button:callback(offScreenDraw);
  getOffScreen_Button:when(9)
window:show();
Fl:run();




RE: slicing images - mikshaw - 02-28-2008 07:29 PM

I'm just guessing here, but I think maybe RGB/RGBA images require either fl_draw_image() or fl_read_image() rather than fl_draw()


RE: slicing images - iGame3D - 02-29-2008 05:29 AM

mikshaw Wrote:
I'm just guessing here, but I think maybe RGB/RGBA images require either fl_draw_image() or fl_read_image() rather than fl_draw()


Fl draw just creates that text.
FL draw over a button or in a window will act as if it has an alpha, only drawing
the pixels for the text, when it comes back from the buffer however, it has
an unwanted background. The same with any other offscreen data.

Is there a way to force an alpha channel?


RE: slicing images - mikshaw - 02-29-2008 05:42 AM

All I can say is that alpha is mentioned in fl_read_image(), but I don't yet know how to use it.

Quote:
uchar *fl_read_image(uchar *p, int X, int Y, int W, int H, int alpha = 0);

Read a RGB(A) image from the current window or off-screen buffer. The p argument points to a buffer that can hold the image and must be at least W*H*3 bytes when reading RGB images and W*H*4 bytes when reading RGBA images. If NULL, fl_read_image() will create an array of the proper size which can be freed using delete.

The alpha parameter controls whether an alpha channel is created and the value that is placed in the alpha channel. If 0, no alpha channel is generated




RE: slicing images - iGame3D - 02-29-2008 04:16 PM

mikshaw Wrote:
The p argument points to a buffer that can hold the image and must be at least W*H*3 bytes when reading RGB images and W*H*4 bytes when reading RGBA images.


How do you create a buffer ?


Also can you believe this.
FLTK 1.1.8rc1 released yesterday, this is in the notes:

Quote:
fl_read_image() was broken on Intel-based Macs (STR #1490)
Fixed Quartz fl_read_image


Its been a while since a compiled murgaLua looks like I get my chance.


RE: slicing images - mikshaw - 02-29-2008 11:36 PM

Quote:
How do you create a buffer ?

I know less about that than you. I tried modifying the offScreen example to use an image rather than shape, but got nowhere. It looks like it would require the use of features found in a few of John's latest examples...I just can't make any sense of it.

Quote:
fl_read_image() was broken on Intel-based Macs (STR #1490)
Fixed Quartz fl_read_image
Its been a while since a compiled murgaLua looks like I get my chance.

You may not need to do that. The changelog includes everything that was changed since 1.1.7, but murgaLua has been including the newer 1.1.x releases (the stuff that's released as weekly snapshots).


RE: slicing images - Juergen - 03-01-2008 04:27 AM

mikshaw Wrote:

Quote:
How do you create a buffer ?

I know less about that than you. I tried modifying the offScreen example to use an image rather than shape, but got nowhere. It looks like it would require the use of features found in a few of John's latest examples...I just can't make any sense of it.

Of course you know it! you used it in your sliding puzzle. (remember what data() returns)

Code:
> image=fltk.Fl_PNG_Image("murgaLua.png")
> image_data=image:data()
> =type(image_data)
table
> for field,value in pairs(image_data) do print(field,type(value)) end
1       string
d       number
h       number
w       number
ld      number
> =image_data.d
3
> =image_data.h
155
> =image_data.w
167
> =image_data.ld
0


Here is a small example that should make it clear:

Code:
w=fltk.Fl_Double_Window(200,200,"Image buffer Test")
red=string.char(255,0,0)
green=string.char(0,255,0)
blue=string.char(0,0,255)

buffer=string.rep(string.rep(red,50)..string.rep(green,50)..string.rep(blue,50),
150)
image=fltk.Fl_RGB_Image(buffer,150,150,3,0)
box=fltk.Fl_Box(25,25,150,150)
box:image(image)
box2=fltk.Fl_Box(25,175,150,25,"That sure wasn't hard!")
w:show()
Fl:run()


Juergen


RE: slicing images - mikshaw - 03-01-2008 06:57 AM

Quote:
Of course you know it! you used it in your sliding puzzle. (remember what data() returns)

Not really. That was a direct copy of one of John's examples. I thought I understood *what* it was doing (I've looked into it as you did here in the first example), but not *how* it was doing it. I thought this was not the same as creating a buffer in a format that could be used by fl_read_image, particularly considering that was one of the things I tried and failed.
One of the things I found frustrating was that some RGB images seem to have no useable value for image_data[1].

So you're saying an image buffer is nothing more than a number representing the desired length of a string?
That sounds much too simple to be true =o)

In any case, I think what I'm having the most trouble understanding is putting an actual image file (possibly an indexed image) into that buffer so it can be read back as RGB or RGBA. The drawing docs don't seem to say much about using existing image data, although it must be possible or else it would be nearly pointless to create an empty RGB image. Maybe I'm making it more difficult than it is...I do have a tendency to overlook the obvious....


RE: slicing images - mikshaw - 03-01-2008 09:20 AM

Well, I'm finally making some progress!
Although I still don't understand using offscreen buffers for storing image data (as in data from loaded image files), I realized that I don't need it for this particular project. Since the image preview is exactly the image that needs to be converted to RGB for slicing, I used that area to grab the data with fl_read_image()

I didn't understand before that it was simply a matter of specifying an onscreen area from which to grab the image, but after poking around with various parts of Johns examples, here's something that will tile any image file that murgaLua can read.

I still don't have a solution to the line accross the bottom, but that's minor for now. Maybe a series of fl_read_image and Fl_RGB_Image could create the tiles instead of using getTiles(), but I think that might slow things down. There is already a little pause from doing it just once.

Code:
#!/usr/bin/murgaLua

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

-- window and image size are determined by these variables
ts=80 -- tile size
tc=4  -- number of tiles in one line
fr=5 -- frame size
--img_dir="/mnt/hda4/shared/image/slide_puzzle"
--img_dir="/home/mik/image/slide_puzzle"

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

function load_callback(object)
-- a stew of pieces from John Murga's functions
fileName = fltk.fl_file_chooser("Choose an RGB image", "Image Files (*.{jpg,png,bmp,xbm,xpm,gif})", img_dir, 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)
  start=1 -- used for controlling window callback and scramble behavior
  preview:image(image1); preview:redraw(); preview:show() -- load the preview
  Fl:check() -- seems to keep fl_read_image from grabbing the file chooser
  imageString=fltk.fl_read_image(fr,fr,ts*tc,ts*tc) -- grab the preview image
  image2 = fltk:Fl_RGB_Image(imageString,ts*tc,ts*tc,3) -- create RGB image for tiling
  myImages = image2:getTiles(ts,ts)
  for key,value in ipairs(myImages) do
    tile[key-1]:image(value)
    -- make sure all tile locations are reset correctly
    -- fixes a bug if image is changed while tiles are scrambled
    tile[key-1]:position(pos[key-1].col,pos[key-1].row)
  end
  scramble()
  scrambutt:label("load an image")
end
end

function move_tile(t)
if Fl:event_button() >1 then preview:show() 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^2-1 do
    if tile[i]:x()==pos[i].col and tile[i]:y()==pos[i].row then
      ok=ok+1
    if ok==tc^2 then -- if ok == total number of tiles
      fltk.fl_beep()
      scrambutt:label(plize[math.random(1,table.getn(plize))])
      preview:show()
      start=1
      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
preview:hide()
if hidden then -- reset hidden tile
  tile[hidden]:box(fltk.FL_UP_BOX)
  tile[hidden]:labeltype(fltk.FL_NORMAL_LABEL)
end
-- turn a random tile into the missing piece
hidden=math.random(0,tc^2-1)
tile[hidden]:labeltype(fltk.FL_NO_LABEL)
tile[hidden]:box(fltk.FL_DOWN_BOX)
local scram=0
while scram < 10000 do
  move_tile(tile[math.random(0,tc^2-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)
scrambutt:tooltip([[
Click this button at any time to load a new image.

** Puzzle Controls **
Left click:  Move a tile.
Right or Middle click:  Toggle image display.
Esc:  Quit program, or close image display if it is visible.
]])

-- 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^2-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

-- image preview (cheat)
preview=fltk:Fl_Button(fr,fr,ts*tc,ts*tc)
preview:callback(function()
if start==1 then load_callback()
else preview:hide() end
end)

--preview:hide()
w:callback(function()
  -- Esc just hides the preview if it's visible
  if preview:visible()==1 and start==0 then preview:hide()
  else os.exit() end
end)

start=1
w:show()
Fl:run()