1. --!parent
  2.  
  3. -- ************************************************************************
  4. -- Animated GIF Encoder
  5. --
  6. -- @author Valentin Schmidt
  7. -- @version 0.2
  8. -- @requires xtra "ImgXtra", xtra "Shell", convert.exe (ImageMagick)
  9. -- ************************************************************************
  10.  
  11. property _ix
  12. property _sx
  13. property _tmp_dir
  14. property _tmp_name
  15. property _tmp_type
  16. property _params
  17. property _num_frames
  18. property _palette_colors
  19.  
  20. -- constants for dispose_op parameter
  21. property DISPOSE_OP_NONE
  22. property DISPOSE_OP_BACKGROUND
  23. property DISPOSE_OP_PREVIOUS
  24.  
  25. ----------------------------------------
  26. -- @constructor
  27. ----------------------------------------
  28. on new (me)
  29.     me._ix = xtra("ImgXtra").new()
  30.     me._sx = xtra("Shell").new()
  31.     me._tmp_dir = me._sx.shell_getEnvVar("TMP")
  32.  
  33.     me._tmp_type = ".png" -- BMP is faster, but doesn't support transparency
  34.  
  35.     -- no disposal is done on this frame before rendering the next; the contents
  36.     -- of the output buffer are left as is.
  37.     me.DISPOSE_OP_NONE = 1
  38.  
  39.     -- the frame's region of the output buffer is to be cleared to fully
  40.     -- transparent black before rendering the next frame.
  41.     me.DISPOSE_OP_BACKGROUND = 2
  42.  
  43.     -- the frame's region of the output buffer is to be reverted to the previous
  44.     -- contents before rendering the next frame.
  45.     me.DISPOSE_OP_PREVIOUS = 3
  46.  
  47.     return me
  48. end
  49.  
  50. ----------------------------------------
  51. -- Utility function: pass a "typical" image for the animation; returns a palette as list of colors
  52. -- @param {image} master_image
  53. -- @param {integer} [palette_size=256]
  54. -- @return {list|FALSE}
  55. ----------------------------------------
  56. on findPalette (me, master_image, palette_size)
  57.     props = [:]
  58.     props["image"] = master_image
  59.     if integerP(palette_size) then props["palette_size"] = palette_size
  60.     img = me._ix.ix_quantizeImage(props)
  61.     if ilk(img)<>#image then return FALSE
  62.     palette_colors = me._ix.ix_getPaletteColors(["palette":img.paletteRef])
  63.     img.paletteRef.erase()
  64.     return palette_colors
  65. end
  66.  
  67. ----------------------------------------
  68. -- Starts a new animated GIF
  69. -- @param {list} palette_colors
  70. -- @param {integer} [num_plays=0] - how often to loop, 0 means endless looping (default)
  71. ----------------------------------------
  72. on init (me, palette_colors, num_plays)
  73.     if voidP(num_plays) then num_plays = 0
  74.     me._palette_colors = palette_colors
  75.     me._params = "-loop " & num_plays & " "
  76.     me._tmp_name = random(9999) & "_"
  77.     me._num_frames = 0
  78. end
  79.  
  80. ----------------------------------------
  81. -- Adds new frame
  82. -- @param {image} frame_image - the frame as Lingo image object
  83. -- @param {integer} ms - duration of the frame in milliseconds
  84. -- @param {integer} [x=0] - x-offset of the image
  85. -- @param {integer} [y=0] - y-offset of the image
  86. -- @param {integer} [dispose_op=1] - how frame is disposed, see comments above
  87. -- @param {integer} [trans] - index of transparency in palette color list
  88. -- @return {bool} success
  89. ----------------------------------------
  90. on addFrame (me, frame_image, ms, x, y, dispose_op, trans)
  91.     if voidP(x) then x = 0
  92.     if voidP(y) then y = 0
  93.  
  94.     -- map to color palette
  95.     props = [:]
  96.     props["image"] = frame_image
  97.     props["reserved_colors"] = me._palette_colors
  98.     frame_image_indexed = me._ix.ix_quantizeImage(props)
  99.  
  100.     -- export image as temporary image file in TMP dir
  101.     fn = me._tmp_name & me._num_frames & me._tmp_type
  102.     props = [:]
  103.     props["image"] = frame_image_indexed
  104.     props["filename"] = me._tmp_dir & "\" & fn
  105.     props["palette"] = me._palette_colors
  106.     if not voidP(trans) then props["transparency"] = 256 - trans -- fix in ImgXtra ???
  107.     ok = me._ix.ix_saveImage(props)
  108.     if not ok then return FALSE
  109.  
  110.     put "-delay "&integer(ms/10)&" " after _params
  111.     if not voidP(dispose_op) then put "-dispose " & dispose_op & " " after _params
  112.     if x>0 or y>0 then
  113.         put "-page +" & x & "+" & y & " " after _params
  114.     end if
  115.     put fn & " " after _params
  116.  
  117.     me._num_frames = me._num_frames + 1
  118.     return TRUE
  119. end
  120.  
  121. ----------------------------------------
  122. -- Saves animation as GIF file
  123. -- @param {string} gif_file
  124. -- @return {bool} success
  125. ----------------------------------------
  126. on writeFile (me, gif_file)
  127.     me._sx.shell_setCurrentDir(me._tmp_dir)
  128.  
  129.     -- run convert.exe
  130.     props = [:]
  131.     props["show_cmd"] = 0
  132.     props["wait"] = 1
  133.     props["parameters"] = me._params & QUOTE & gif_file & QUOTE
  134.     exit_code = me._sx.shell_execex(the moviePath & "bin\convert.exe", props)
  135.     ok = (exit_code=0)
  136.  
  137.     -- delete temporary files
  138.     cmd = "del /Q " & QUOTE & "%TMP%\" & me._tmp_name & "*" & me._tmp_type & QUOTE
  139.     me._sx.shell_cmd(cmd)
  140.  
  141.     return ok
  142. end
  143.  
[raw code]