Le repo des sources pour le site web des JM2L
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 

199 linhas
5.5 KiB

  1. #!/usr/bin/env python
  2. # ##### BEGIN GPL LICENSE BLOCK #####
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # of the License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software Foundation,
  16. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. #
  18. # ##### END GPL LICENSE BLOCK #####
  19. # <pep8 compliant>
  20. """
  21. Thumbnailer runs with python 2.7 and 3.x.
  22. To run automatically with a file manager such as Nautilus, save this file
  23. in a directory that is listed in PATH environment variable, and create
  24. blender.thumbnailer file in ${HOME}/.local/share/thumbnailers/ directory
  25. with the following contents:
  26. [Thumbnailer Entry]
  27. TryExec=blender-thumbnailer.py
  28. Exec=blender-thumbnailer.py %u %o
  29. MimeType=application/x-blender;
  30. """
  31. import struct
  32. def open_wrapper_get():
  33. """ wrap OS spesific read functionality here, fallback to 'open()'
  34. """
  35. class GFileWrapper:
  36. __slots__ = ("mode", "g_file")
  37. def __init__(self, url, mode='r'):
  38. self.mode = mode # used in gzip module
  39. self.g_file = Gio.File.parse_name(url).read(None)
  40. def read(self, size):
  41. return self.g_file.read_bytes(size, None).get_data()
  42. def seek(self, offset, whence=0):
  43. self.g_file.seek(offset, [1, 0, 2][whence], None)
  44. return self.g_file.tell()
  45. def tell(self):
  46. return self.g_file.tell()
  47. def close(self):
  48. self.g_file.close(None)
  49. def open_local_url(url, mode='r'):
  50. o = urlparse(url)
  51. if o.scheme == '':
  52. path = o.path
  53. elif o.scheme == 'file':
  54. path = unquote(o.path)
  55. else:
  56. raise(IOError('URL scheme "%s" needs gi.repository.Gio module' % o.scheme))
  57. return open(path, mode)
  58. try:
  59. from gi.repository import Gio
  60. return GFileWrapper
  61. except ImportError:
  62. try:
  63. # Python 3
  64. from urllib.parse import urlparse, unquote
  65. except ImportError:
  66. # Python 2
  67. from urlparse import urlparse
  68. from urllib import unquote
  69. return open_local_url
  70. def blend_extract_thumb(path):
  71. import os
  72. open_wrapper = open_wrapper_get()
  73. # def MAKE_ID(tag): ord(tag[0])<<24 | ord(tag[1])<<16 | ord(tag[2])<<8 | ord(tag[3])
  74. REND = 1145980242 # MAKE_ID(b'REND')
  75. TEST = 1414743380 # MAKE_ID(b'TEST')
  76. blendfile = open_wrapper(path, 'rb')
  77. head = blendfile.read(12)
  78. if head[0:2] == b'\x1f\x8b': # gzip magic
  79. import gzip
  80. blendfile.close()
  81. blendfile = gzip.GzipFile('', 'rb', 0, open_wrapper(path, 'rb'))
  82. head = blendfile.read(12)
  83. if not head.startswith(b'BLENDER'):
  84. blendfile.close()
  85. return None, 0, 0
  86. is_64_bit = (head[7] == b'-'[0])
  87. # true for PPC, false for X86
  88. is_big_endian = (head[8] == b'V'[0])
  89. # blender pre 2.5 had no thumbs
  90. if head[9:11] <= b'24':
  91. blendfile.close()
  92. return None, 0, 0
  93. sizeof_bhead = 24 if is_64_bit else 20
  94. int_endian_pair = '>ii' if is_big_endian else '<ii'
  95. while True:
  96. bhead = blendfile.read(sizeof_bhead)
  97. if len(bhead) < sizeof_bhead:
  98. return None, 0, 0
  99. code, length = struct.unpack(int_endian_pair, bhead[0:8]) # 8 == sizeof(int) * 2
  100. if code == REND:
  101. blendfile.seek(length, os.SEEK_CUR)
  102. else:
  103. break
  104. if code != TEST:
  105. blendfile.close()
  106. return None, 0, 0
  107. try:
  108. x, y = struct.unpack(int_endian_pair, blendfile.read(8)) # 8 == sizeof(int) * 2
  109. except struct.error:
  110. blendfile.close()
  111. return None, 0, 0
  112. length -= 8 # sizeof(int) * 2
  113. if length != x * y * 4:
  114. blendfile.close()
  115. return None, 0, 0
  116. image_buffer = blendfile.read(length)
  117. if len(image_buffer) != length:
  118. blendfile.close()
  119. return None, 0, 0
  120. blendfile.close()
  121. return image_buffer, x, y
  122. def write_png(buf, width, height):
  123. import zlib
  124. # reverse the vertical line order and add null bytes at the start
  125. width_byte_4 = width * 4
  126. raw_data = b"".join(b'\x00' + buf[span:span + width_byte_4] for span in range((height - 1) * width * 4, -1, - width_byte_4))
  127. def png_pack(png_tag, data):
  128. chunk_head = png_tag + data
  129. return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))
  130. return b"".join([
  131. b'\x89PNG\r\n\x1a\n',
  132. png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
  133. png_pack(b'IDAT', zlib.compress(raw_data, 9)),
  134. png_pack(b'IEND', b'')])
  135. def main():
  136. import sys
  137. if len(sys.argv) < 3:
  138. print("Expected 2 arguments <input.blend> <output.png>")
  139. else:
  140. file_in = sys.argv[-2]
  141. buf, width, height = blend_extract_thumb(file_in)
  142. if buf:
  143. file_out = sys.argv[-1]
  144. f = open(file_out, "wb")
  145. f.write(write_png(buf, width, height))
  146. f.close()
  147. if __name__ == '__main__':
  148. main()