gif.lua 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. -- GIF encoder specialized for PICO-8
  2. -- by gamax92.
  3. -- Updated for liko12 by RamiLego4Game
  4. local palmap={}
  5. for i=1, 16 do
  6. local palette=_ColorSet[i]
  7. local value=bit.lshift(palette[1], 16)+bit.lshift(palette[2], 8)+palette[3]
  8. palmap[i-1]=value
  9. palmap[value]=i-1
  10. end
  11. local function num2str(data)
  12. return string.char(bit.band(data, 0xFF), bit.rshift(data, 8))
  13. end
  14. local gif={}
  15. function gif:frame(data)
  16. --[[data:mapPixel(function(x,y,r,g,b,a)
  17. local lk12id = _GetColorID(r,g,b,255) or 1
  18. return unpack(_ColorSet[lk12id])
  19. end)]]
  20. self.file:write("\33\249\4\4\3\0\0\0")
  21. local last=self.last
  22. local x0, y0, x1, y1=0, nil, 192*_GIFSCALE-1, 128*_GIFSCALE-1
  23. if self.first then
  24. y0=0
  25. self.first=nil
  26. else
  27. for y=0, y1 do
  28. local kill=false
  29. for x=x0, x1 do
  30. local r1, g1, b1=last:getPixel(x, y)
  31. local r2, g2, b2=data:getPixel(x, y)
  32. if r1~=r2 or g1~=g2 or b1~=b2 then
  33. y0=y
  34. kill=true
  35. break
  36. end
  37. end
  38. if kill then
  39. break
  40. end
  41. end
  42. if y0==nil then
  43. -- TODO: Output longer delay instead of bogus frame
  44. x0, y0, x1, y1=0, 0, 0, 0
  45. end
  46. for x=x0, x1 do
  47. local kill=false
  48. for y=y0, y1 do
  49. local r1, g1, b1=last:getPixel(x, y)
  50. local r2, g2, b2=data:getPixel(x, y)
  51. if r1~=r2 or g1~=g2 or b1~=b2 then
  52. x0=x
  53. kill=true
  54. break
  55. end
  56. end
  57. if kill then
  58. break
  59. end
  60. end
  61. for y=y1, y0, -1 do
  62. local kill=false
  63. for x=x0, x1 do
  64. local r1, g1, b1=last:getPixel(x, y)
  65. local r2, g2, b2=data:getPixel(x, y)
  66. if r1~=r2 or g1~=g2 or b1~=b2 then
  67. y1=y
  68. kill=true
  69. break
  70. end
  71. end
  72. if kill then
  73. break
  74. end
  75. end
  76. for x=x1, x0, -1 do
  77. local kill=false
  78. for y=y0, y1 do
  79. local r1, g1, b1=last:getPixel(x, y)
  80. local r2, g2, b2=data:getPixel(x, y)
  81. if r1~=r2 or g1~=g2 or b1~=b2 then
  82. x1=x
  83. kill=true
  84. break
  85. end
  86. end
  87. if kill then
  88. break
  89. end
  90. end
  91. end
  92. self.file:write("\44"..num2str(x0)..num2str(y0)..num2str(x1-x0+1)..num2str(y1-y0+1).."\0\4")
  93. local codetbl={}
  94. for i=0, 15 do
  95. codetbl[string.char(i)]=i
  96. end
  97. local last=17
  98. local buffer=""
  99. local stream={16}
  100. for y=y0, y1 do
  101. for x=x0, x1 do
  102. local r, g, b=data:getPixel(x, y)
  103. local index=string.char(palmap[bit.lshift(r, 16)+bit.lshift(g, 8)+b]) --FIXME PLEASE
  104. local temp=buffer..index
  105. if codetbl[temp] then
  106. buffer=temp
  107. else
  108. stream[#stream+1]=codetbl[buffer]
  109. last=last+1
  110. if last<4095 then
  111. codetbl[temp]=last
  112. else
  113. stream[#stream+1]=16
  114. codetbl={}
  115. for i=0, 15 do
  116. codetbl[string.char(i)]=i
  117. end
  118. last=17
  119. end
  120. buffer=tostring(index)
  121. end
  122. end
  123. end
  124. stream[#stream+1]=codetbl[buffer]
  125. stream[#stream+1]=17
  126. local output={}
  127. local size=5
  128. local bits=0
  129. local pack=0
  130. local base=-16
  131. for i=1, #stream do
  132. pack=pack+bit.lshift(stream[i], bits)
  133. bits=bits+size
  134. while bits>=8 do
  135. bits=bits-8
  136. output[#output+1]=string.char(bit.band(pack, 0xFF))
  137. pack=bit.rshift(pack, 8)
  138. end
  139. if i-base>=2^size then
  140. size=size+1
  141. end
  142. if stream[i]==16 then
  143. base=i-17
  144. size=5
  145. end
  146. end
  147. while bits>0 do
  148. bits=bits-8
  149. output[#output+1]=string.char(bit.band(pack, 0xFF))
  150. pack=bit.rshift(pack, 8)
  151. end
  152. output=table.concat(output)
  153. while #output>0 do
  154. self.file:write(string.char(math.min(#output, 255))..output:sub(1, 255))
  155. output=output:sub(256)
  156. end
  157. self.file:write("\0")
  158. self.last=data
  159. end
  160. function gif:close()
  161. self.file:write("\59")
  162. self.file:close()
  163. self.file=nil
  164. self.last=nil
  165. end
  166. local gifmt={
  167. __index=function(t, k)
  168. return gif[k]
  169. end
  170. }
  171. local giflib={}
  172. function giflib.new(filename)
  173. local file, err=love.filesystem.newFile(filename, "w")
  174. if not file then
  175. return nil, err
  176. end
  177. file:write("GIF89a"..num2str(192*_GIFSCALE)..num2str(128*_GIFSCALE).."\243\0\0")
  178. for i=1, 16 do
  179. local palette=_ColorSet[i]
  180. file:write(string.char(palette[1], palette[2], palette[3]))
  181. end
  182. file:write("\33\255\11NETSCAPE2.0\3\1\0\0\0")
  183. local last=love.image.newImageData(192*_GIFSCALE, 128*_GIFSCALE)
  184. return setmetatable({filename=filename, file=file, last=last, first=true}, gifmt)
  185. end
  186. return giflib