diff options
author | Campbell Barton <ideasman42@gmail.com> | 2021-10-20 02:16:36 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2021-10-20 02:31:30 +0300 |
commit | ef9269bd62f31e39d39cc59cd34354b53eae6661 (patch) | |
tree | 5ff2fb2f3b03678b7e58a23397d4d53bb83f35f8 /release/bin | |
parent | bca2701236ab60dce5e32a8731f3bba43783b5b2 (diff) |
Thumbnails: refactor extraction to use one code-path for all platforms
Thumbnail extraction now shares code between Linux/Windows,
allowing thumbnails from Zstd compressed blend files to be extracted.
The main logic is placed in blendthumb_extract.cc and is built as static
library. For windows there is DLL which is registered during blender
install and which then reads and generates thumbnails.
For other platforms there is blender-thumbnailer executable file which
takes blend file as an input and generates PNG file. As a result
Python script blender-thumbnailer.py is no longer needed.
The thumbnail extractor shares the same code-path as Blenders file
reading, so there is no need to duplicate any file reading logic.
This means reading compressed blend files is supported (broken since
the recent move Zstd compression - D5799).
This resolves T63736.
Contributors:
- @alausic original patch.
- @LazyDodo windows fixes/support.
- @campbellbarton general fixes/update.
- @lukasstockner97 Zstd support.
Reviewed By: sybren, mont29, LazyDodo, campbellbarton
Ref D6408
Diffstat (limited to 'release/bin')
-rwxr-xr-x | release/bin/blender-thumbnailer.py | 193 |
1 files changed, 0 insertions, 193 deletions
diff --git a/release/bin/blender-thumbnailer.py b/release/bin/blender-thumbnailer.py deleted file mode 100755 index e050a681ca0..00000000000 --- a/release/bin/blender-thumbnailer.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -""" -Thumbnailer runs with python 2.7 and 3.x. -To run automatically with a file manager such as Nautilus, save this file -in a directory that is listed in PATH environment variable, and create -blender.thumbnailer file in ${HOME}/.local/share/thumbnailers/ directory -with the following contents: - -[Thumbnailer Entry] -TryExec=blender-thumbnailer.py -Exec=blender-thumbnailer.py %u %o -MimeType=application/x-blender; -""" - -import struct - - -def open_wrapper_get(): - """ wrap OS specific read functionality here, fallback to 'open()' - """ - - class GFileWrapper: - __slots__ = ("mode", "g_file") - - def __init__(self, url, mode='r'): - self.mode = mode # used in gzip module - self.g_file = Gio.File.parse_name(url).read(None) - - def read(self, size): - return self.g_file.read_bytes(size, None).get_data() - - def seek(self, offset, whence=0): - self.g_file.seek(offset, [1, 0, 2][whence], None) - return self.g_file.tell() - - def tell(self): - return self.g_file.tell() - - def close(self): - self.g_file.close(None) - - def open_local_url(url, mode='r'): - o = urlparse(url) - if o.scheme == '': - path = o.path - elif o.scheme == 'file': - path = unquote(o.path) - else: - raise(IOError('URL scheme "%s" needs gi.repository.Gio module' % o.scheme)) - return open(path, mode) - - try: - from gi.repository import Gio - return GFileWrapper - except ImportError: - try: - # Python 3 - from urllib.parse import urlparse, unquote - except ImportError: - # Python 2 - from urlparse import urlparse - from urllib import unquote - return open_local_url - - -def blend_extract_thumb(path): - import os - open_wrapper = open_wrapper_get() - - REND = b'REND' - TEST = b'TEST' - - blendfile = open_wrapper(path, 'rb') - - head = blendfile.read(12) - - if head[0:2] == b'\x1f\x8b': # gzip magic - import gzip - blendfile.close() - blendfile = gzip.GzipFile('', 'rb', 0, open_wrapper(path, 'rb')) - head = blendfile.read(12) - - if not head.startswith(b'BLENDER'): - blendfile.close() - return None, 0, 0 - - is_64_bit = (head[7] == b'-'[0]) - - # true for PPC, false for X86 - is_big_endian = (head[8] == b'V'[0]) - - # blender pre 2.5 had no thumbs - if head[9:11] <= b'24': - return None, 0, 0 - - sizeof_bhead = 24 if is_64_bit else 20 - int_endian = '>i' if is_big_endian else '<i' - int_endian_pair = int_endian + 'i' - - while True: - bhead = blendfile.read(sizeof_bhead) - - if len(bhead) < sizeof_bhead: - return None, 0, 0 - - code = bhead[:4] - length = struct.unpack(int_endian, bhead[4:8])[0] # 4 == sizeof(int) - - if code == REND: - blendfile.seek(length, os.SEEK_CUR) - else: - break - - if code != TEST: - return None, 0, 0 - - try: - x, y = struct.unpack(int_endian_pair, blendfile.read(8)) # 8 == sizeof(int) * 2 - except struct.error: - return None, 0, 0 - - length -= 8 # sizeof(int) * 2 - - if length != x * y * 4: - return None, 0, 0 - - image_buffer = blendfile.read(length) - - if len(image_buffer) != length: - return None, 0, 0 - - return image_buffer, x, y - - -def write_png(buf, width, height): - import zlib - - # reverse the vertical line order and add null bytes at the start - width_byte_4 = width * 4 - raw_data = b"".join(b'\x00' + buf[span:span + width_byte_4] for span in range((height - 1) * width * 4, -1, - width_byte_4)) - - def png_pack(png_tag, data): - chunk_head = png_tag + data - return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)) - - return b"".join([ - b'\x89PNG\r\n\x1a\n', - png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)), - png_pack(b'IDAT', zlib.compress(raw_data, 9)), - png_pack(b'IEND', b'')]) - - -def main(): - import sys - - if len(sys.argv) < 3: - print("Expected 2 arguments <input.blend> <output.png>") - else: - file_in = sys.argv[-2] - - buf, width, height = blend_extract_thumb(file_in) - - if buf: - file_out = sys.argv[-1] - - f = open(file_out, "wb") - f.write(write_png(buf, width, height)) - f.close() - - -if __name__ == '__main__': - main() |