From 1159eacbc62c2ab7c566be078c43c08bdfda8778 Mon Sep 17 00:00:00 2001 From: Pepper Date: Wed, 2 Mar 2016 20:24:12 -0500 Subject: pluginloader --- photoblaster/_file.py | 2 +- photoblaster/modules/__init__.py | 107 +++++++++++- photoblaster/modules/base.py | 100 ----------- photoblaster/modules/pbbreaker.py | 213 ------------------------ photoblaster/modules/pbbreaker/__init__.py | 217 ++++++++++++++++++++++++ photoblaster/modules/pbgenerate.py | 200 ---------------------- photoblaster/modules/pbgenerate/__init__.py | 204 +++++++++++++++++++++++ photoblaster/modules/pbgradient.py | 235 -------------------------- photoblaster/modules/pbgradient/__init__.py | 239 +++++++++++++++++++++++++++ photoblaster/modules/pbgrid.py | 234 -------------------------- photoblaster/modules/pbgrid/__init__.py | 239 +++++++++++++++++++++++++++ photoblaster/modules/pblandscape/__init__.py | 4 +- photoblaster/modules/pbpattern.py | 115 ------------- photoblaster/modules/pbpattern/__init__.py | 119 +++++++++++++ pluginloader.py | 19 +++ run_module_examples.py | 12 +- share/install/requirements.txt | 1 + 17 files changed, 1149 insertions(+), 1111 deletions(-) delete mode 100644 photoblaster/modules/base.py delete mode 100755 photoblaster/modules/pbbreaker.py create mode 100755 photoblaster/modules/pbbreaker/__init__.py delete mode 100755 photoblaster/modules/pbgenerate.py create mode 100755 photoblaster/modules/pbgenerate/__init__.py delete mode 100755 photoblaster/modules/pbgradient.py create mode 100755 photoblaster/modules/pbgradient/__init__.py delete mode 100755 photoblaster/modules/pbgrid.py create mode 100755 photoblaster/modules/pbgrid/__init__.py delete mode 100755 photoblaster/modules/pbpattern.py create mode 100755 photoblaster/modules/pbpattern/__init__.py create mode 100644 pluginloader.py diff --git a/photoblaster/_file.py b/photoblaster/_file.py index 4e4a382..5b51acc 100644 --- a/photoblaster/_file.py +++ b/photoblaster/_file.py @@ -4,7 +4,6 @@ import random import sha import sys import time -from photoblaster.s3.cli import S3Cli from subprocess import Popen, PIPE, call from photoblaster.config import WORKING_DIR, BIN_IDENTIFY, DEFAULT_WIDTH, \ DEFAULT_HEIGHT, BIN_CONVERT, LOCAL, BASE_URL, DEFAULT_FINALFORMAT @@ -137,6 +136,7 @@ class File(object): } def s3move(self): + from photoblaster.s3.cli import S3Cli s3cli = S3Cli() s3cli.s3move( self.get_filepath(), "im/{}/{}".format( diff --git a/photoblaster/modules/__init__.py b/photoblaster/modules/__init__.py index abd4380..b71f67b 100644 --- a/photoblaster/modules/__init__.py +++ b/photoblaster/modules/__init__.py @@ -1,7 +1,100 @@ -from photoblaster.modules.base import Pb, PbProcessError -from photoblaster.modules.pbgrid import PbGrid -from photoblaster.modules.pbbreaker import PbBreaker -from photoblaster.modules.pbpattern import PbPattern -from photoblaster.modules.pbgenerate import PbGenerate -from photoblaster.modules.pblandscape import PbLandscape -from photoblaster.modules.pbgradient import PbGradient +""" +contains only the Pb class which is used by the Pb.* modules for inheritance +""" + +import sys +from subprocess import call +from photoblaster.params import Params +import simplejson as json +from photoblaster.db.models.imcmd import ImCmd + + +class PbProcessError(Exception): + pass + + +class ModuleBase(object): + """Base Pb class. USED ONLY FOR INHERITANCE""" + def __init__(self, **kwargs): + self._input_kwargs = kwargs + self.params = Params( + classname=self.__class__.__name__ + ) + self._files_created = [] + self.commands = [] + self.tag = self.__class__.__name__ + self._db_url_param = None + + self._output_file = None + + self.width = None + self.height = None + + def _call_cmd(self, cmd): + try: + cmd = map(lambda i: str(i), cmd) + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception: + raise Exception("Unable to call cmd {}".format(str(cmd))) + + def err_warn(self, s): + sys.stderr.write("ERROR:{} - {}\n".format(self.__class__.__name__, s)) + raise PbProcessError + + def cleanup(self): + if not self._files_created: + return + map(lambda n: n.delete(), self._files_created) + + def err_fatal(self, s): + sys.stderr.write("ERROR[FATAL]:%s - %s\n" % ( + self.__class__.__name__, s)) + sys.exit(1) + + @classmethod + def example_run(cls, params=None, verbose=True): + example_params = params or cls.example_params + if not example_params: + raise AttributeError( + "Must supply test params to test %s" % cls.__name__ + ) + b = cls(**example_params) + b.create() + if verbose: + sys.stderr.write( + "generated %s\n" % b.get_output_file().get_filepath()) + sys.stderr.write("files created %s\n" % b._files_created) + sys.stderr.write("commands:\n %s\n" % ";\n ".join(b.commands)) + return b + + def db_send( + self, + remote_addr=None, + db_connection=None + ): + try: + _insert_data = { + 'date': self.get_output_file().get_creation_time(), + 'remote_addr': remote_addr, + 'name': str(self.params.username), + 'url': self._db_url_param, + 'dir': self.get_output_file().get_hashdir(), + 'oldfile': None, + 'newfile': self.get_output_file().get_filename(), + 'dataobj': json.dumps(dict(self._input_kwargs)), + 'cmd': "; ".join(self.commands), + 'tag': self.tag, + } + ImCmd.create(**_insert_data) + except Exception as e: + self.err_warn("Problem sending to database:\n %s" % str(e)) + + def create(self): + pass + + def get_output_file(self): + return self._output_file + + def set_output_file(self, output_file): + self._output_file = output_file diff --git a/photoblaster/modules/base.py b/photoblaster/modules/base.py deleted file mode 100644 index cae728e..0000000 --- a/photoblaster/modules/base.py +++ /dev/null @@ -1,100 +0,0 @@ -""" -contains only the Pb class which is used by the Pb.* modules for inheritance -""" - -import sys -from subprocess import call -from photoblaster.params import Params -import simplejson as json -from photoblaster.db.models.imcmd import ImCmd - - -class PbProcessError(Exception): - pass - - -class Pb(object): - """Base Pb class. USED ONLY FOR INHERITANCE""" - def __init__(self, **kwargs): - self._input_kwargs = kwargs - self.params = Params( - classname=self.__class__.__name__ - ) - self._files_created = [] - self.commands = [] - self.tag = self.__class__.__name__ - self._db_url_param = None - - self._output_file = None - - self.width = None - self.height = None - - def _call_cmd(self, cmd): - try: - cmd = map(lambda i: str(i), cmd) - call(cmd) - self.commands.append(" ".join(cmd)) - except Exception: - raise Exception("Unable to call cmd {}".format(str(cmd))) - - def err_warn(self, s): - sys.stderr.write("ERROR:{} - {}\n".format(self.__class__.__name__, s)) - raise PbProcessError - - def cleanup(self): - if not self._files_created: - return - map(lambda n: n.delete(), self._files_created) - - def err_fatal(self, s): - sys.stderr.write("ERROR[FATAL]:%s - %s\n" % ( - self.__class__.__name__, s)) - sys.exit(1) - - @classmethod - def example_run(cls, params=None, verbose=True): - example_params = params or cls.example_params - if not example_params: - raise AttributeError( - "Must supply test params to test %s" % cls.__name__ - ) - b = cls(**example_params) - b.create() - if verbose: - sys.stderr.write( - "generated %s\n" % b.get_output_file().get_filepath()) - sys.stderr.write("files created %s\n" % b._files_created) - sys.stderr.write("commands:\n %s\n" % ";\n ".join(b.commands)) - return b - - def db_send( - self, - remote_addr=None, - db_connection=None - ): - try: - _insert_data = { - 'date': self.get_output_file().get_creation_time(), - 'remote_addr': remote_addr, - 'name': str(self.params.username), - 'url': self._db_url_param, - 'dir': self.get_output_file().get_hashdir(), - 'oldfile': None, - 'newfile': self.get_output_file().get_filename(), - 'dataobj': json.dumps(dict(self._input_kwargs)), - 'cmd': "; ".join(self.commands), - 'tag': self.tag, - } - ImCmd.create(**_insert_data) - except Exception as e: - self.err_warn("Problem sending to database:\n %s" % str(e)) - - def create(self): - pass - - def get_output_file(self): - return self._output_file - - def set_output_file(self, output_file): - self._output_file = output_file diff --git a/photoblaster/modules/pbbreaker.py b/photoblaster/modules/pbbreaker.py deleted file mode 100755 index bddd07a..0000000 --- a/photoblaster/modules/pbbreaker.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/python2.7 -import os -import random -import re -from photoblaster.config import BIN_CONVERT -from photoblaster.modules import Pb -from photoblaster._file import File - -DEFAULT_FINALFORMAT = "png" - -_subtle_break_mark = 'pron' -_extreme_break_mark = 'sugar' - -_header_offset = 2000 -_default_breakmode = "subtle" - -_BREAKTYPE_TRANSLATE = { - 'CLASSIC': 'jpg', - 'REDUX': 'pcds', - 'BLURRY_BREAK': 'viff', - 'BLURRY_BREAK_2': 'mat', - 'SWIPE': 'miff', - 'RGB_WASH': 'psd', - 'RGB_WASH_2': 'psb', - 'NOISY_BREAK': 'palm', - 'NOISY_BREAK_2': 'fig', - 'BROKEN_VIGNETTE': 'pbm', - 'FAX_MACHINE': 'cals', - 'STRIPES': 'exr', - 'PHOTOCOPY': 'art', -} - - -class PbBreaker(Pb): - example_params = { - "url": "http://i.asdf.us/im/de/HolyMountain2_1322275112_seamonkey.gif", - "breaktype": "RGB_WASH", - "finalformat": "png", - "breakmode": "extreme", - "breakangle": "10", - "username": "donkey", - "expanded": "false" - } - - def __init__(self, **kwargs): - super(PbBreaker, self).__init__(**kwargs) - _definitions = { - 'username': {'type': 'string'}, - 'breaktype': {'type': 'string'}, - 'breakmode': { - 'type': 'enum', - 'enum_values': ['subtle', 'extreme', 'gradual'], - 'default': _default_breakmode - }, - 'breakangle': {'type': 'float'}, - 'expanded': {'type': 'bool'}, - 'url': {'type': 'img_url'}, - 'finalformat': {'type': 'enum', - 'enum_values': ['png', 'gif', 'jpg']} - } - self.params.definitions_import(_definitions, - kwargs, - classname=self.__class__.__name__) - self._files_created.append(self.params.url.get_file()) - self.params.breaktype.set_val(self._get_breaktype( - str(self.params.breaktype))) - - # psd returns an animation - if not self.params.finalformat and self.params.url.mimetype == "gif": - self.params.finalformat.set_val("gif") - elif self.params.breaktype == 'miff': - self.params.finalformat.set_val("jpg") - self.params.breakmode.set_val("subtle") - elif not self.params.finalformat: - self.params.finalformat.set_val(DEFAULT_FINALFORMAT) - self.width, self.height = self.params.url.get_file().get_dimensions() - - self.set_output_file( - File.from_url( - self.params.url.url, - extension=self.params.finalformat, - classname=self.__class__.__name__, - username=self.params.username) - ) - self._conversion_file = File( - is_temp=True, - namepart="conversion", - classname=self.__class__.__name__, - username=self.params.username, - extension=self.params.breaktype - ) - - self._db_url_param = str(self.params.url['url']) - - def _get_breaktype(self, key): - return _BREAKTYPE_TRANSLATE[key] - - def _rotate(self): - cmd = [ - BIN_CONVERT, self.params.url.get_file().get_filepath(), - "-rotate", self.params.breakangle, - "+repage", self.params.url.get_file().get_filepath() - ] - self._call_cmd(cmd) - - def _rotate_back(self): - angle = str(360-int(self.params.breakangle)) - cmd = [BIN_CONVERT, - self.get_output_file().get_filepath(), - "-rotate", angle, "+repage", - self.get_output_file().get_filepath()] - self._call_cmd(cmd) - if not self.params.expanded: - cmd = [BIN_CONVERT, - self.get_output_file().get_filepath(), - "-gravity", - "Center", - "-crop", - "{}x{}+0+0".format(self.width, self.height), - "+repage", - self.get_output_file().get_filepath()] - self._call_cmd(cmd) - - def _subtle_break(self): - # assume the header is no longer than _header_offset bytes - breakpoint = random.randint(_header_offset, len(self._file_data)) - newfile = self._file_data[0:breakpoint] +\ - _subtle_break_mark +\ - self._file_data[breakpoint:] - self._file_data = newfile[0:len(self._file_data)] - - def _extreme_break(self): - increment = len(self._file_data)/10 - i = 0 - newfile = "" - for b in self._file_data: - if i > _header_offset and not i % increment: - b += _extreme_break_mark - newfile += b - i += 1 - self._file_data = newfile[0:len(self._file_data)] - - def _enforce_jpg(self): - if self.params.breaktype in ["exr", "bmp", "miff"] and not \ - re.match( - r'jpe?g$', - self.params.url.mimetype, - re.IGNORECASE - ): - jpg_file = File( - is_temp=True, - extension="jpg", - ) - self._call_cmd( - [BIN_CONVERT, self.params.url.path, jpg_file.get_filepath()]) - self._files_created.append(jpg_file) - self._conversion_file = jpg_file - - def _first_conversion(self): - if self.params.url.mimetype == self.params.breaktype: - self._conversion_file = self.params.url.path - return - self._call_cmd([BIN_CONVERT, - self.params.url.get_file().get_filepath(), - self._conversion_file.get_filepath()]) - self._files_created.append(self._conversion_file) - - def _prepare_filedata(self): - if self.params.url.mimetype == "gif" and\ - self.params.breaktype not in ['mat', 'psd']: - self.params.url.get_file().choose_gif_frame(module=self) - if self.params.breakangle: - self._rotate() - self._enforce_jpg() - self._first_conversion() - self._file_data = self._conversion_file.get_raw_data() - if not self._file_data: - self.err_warn("Unable to get file data") - - def _add_false_data(self): - if self.params.breakmode == "subtle": - self._subtle_break() - elif self.params.breakmode == "extreme": - self._extreme_break() - f = open(self._conversion_file.get_filepath(), 'w') - f.write(self._file_data) - f.close() - - def _final_conversion(self): - self._call_cmd([ - BIN_CONVERT, - self._conversion_file.get_filepath(), - self.get_output_file().get_filepath()]) - - def psd_psbfilepath(num): - return os.path.join( - re.sub( - r'\.', "-%s." % num, self.get_output_file().get_filepath()) - ) - if str(self.params.breaktype) == 'psd': - self.get_output_file().set_filepath(psd_psbfilepath(1), module=self) - self._files_created.append(psd_psbfilepath(0)) - if str(self.params.breaktype) == 'psb': - self.get_output_file().set_filepath(psd_psbfilepath(0), module=self) - self._files_created.append(psd_psbfilepath(1)) - if self.params.breakangle: - self._rotate_back() - - def create(self): - self._prepare_filedata() - self._add_false_data() - self._final_conversion() - super(PbBreaker, self).create() diff --git a/photoblaster/modules/pbbreaker/__init__.py b/photoblaster/modules/pbbreaker/__init__.py new file mode 100755 index 0000000..7e6c92c --- /dev/null +++ b/photoblaster/modules/pbbreaker/__init__.py @@ -0,0 +1,217 @@ +#!/usr/bin/python2.7 +import os +import random +import re +from photoblaster.config import BIN_CONVERT +from photoblaster.modules_src import ModuleBase +from photoblaster._file import File + +DEFAULT_FINALFORMAT = "png" + +_subtle_break_mark = 'pron' +_extreme_break_mark = 'sugar' + +_header_offset = 2000 +_default_breakmode = "subtle" + +_BREAKTYPE_TRANSLATE = { + 'CLASSIC': 'jpg', + 'REDUX': 'pcds', + 'BLURRY_BREAK': 'viff', + 'BLURRY_BREAK_2': 'mat', + 'SWIPE': 'miff', + 'RGB_WASH': 'psd', + 'RGB_WASH_2': 'psb', + 'NOISY_BREAK': 'palm', + 'NOISY_BREAK_2': 'fig', + 'BROKEN_VIGNETTE': 'pbm', + 'FAX_MACHINE': 'cals', + 'STRIPES': 'exr', + 'PHOTOCOPY': 'art', +} + + +class PbBreaker(ModuleBase): + example_params = { + "url": "http://i.asdf.us/im/de/HolyMountain2_1322275112_seamonkey.gif", + "breaktype": "RGB_WASH", + "finalformat": "png", + "breakmode": "extreme", + "breakangle": "10", + "username": "donkey", + "expanded": "false" + } + + def __init__(self, **kwargs): + super(PbBreaker, self).__init__(**kwargs) + _definitions = { + 'username': {'type': 'string'}, + 'breaktype': {'type': 'string'}, + 'breakmode': { + 'type': 'enum', + 'enum_values': ['subtle', 'extreme', 'gradual'], + 'default': _default_breakmode + }, + 'breakangle': {'type': 'float'}, + 'expanded': {'type': 'bool'}, + 'url': {'type': 'img_url'}, + 'finalformat': {'type': 'enum', + 'enum_values': ['png', 'gif', 'jpg']} + } + self.params.definitions_import(_definitions, + kwargs, + classname=self.__class__.__name__) + self._files_created.append(self.params.url.get_file()) + self.params.breaktype.set_val(self._get_breaktype( + str(self.params.breaktype))) + + # psd returns an animation + if not self.params.finalformat and self.params.url.mimetype == "gif": + self.params.finalformat.set_val("gif") + elif self.params.breaktype == 'miff': + self.params.finalformat.set_val("jpg") + self.params.breakmode.set_val("subtle") + elif not self.params.finalformat: + self.params.finalformat.set_val(DEFAULT_FINALFORMAT) + self.width, self.height = self.params.url.get_file().get_dimensions() + + self.set_output_file( + File.from_url( + self.params.url.url, + extension=self.params.finalformat, + classname=self.__class__.__name__, + username=self.params.username) + ) + self._conversion_file = File( + is_temp=True, + namepart="conversion", + classname=self.__class__.__name__, + username=self.params.username, + extension=self.params.breaktype + ) + + self._db_url_param = str(self.params.url['url']) + + def _get_breaktype(self, key): + return _BREAKTYPE_TRANSLATE[key] + + def _rotate(self): + cmd = [ + BIN_CONVERT, self.params.url.get_file().get_filepath(), + "-rotate", self.params.breakangle, + "+repage", self.params.url.get_file().get_filepath() + ] + self._call_cmd(cmd) + + def _rotate_back(self): + angle = str(360-int(self.params.breakangle)) + cmd = [BIN_CONVERT, + self.get_output_file().get_filepath(), + "-rotate", angle, "+repage", + self.get_output_file().get_filepath()] + self._call_cmd(cmd) + if not self.params.expanded: + cmd = [BIN_CONVERT, + self.get_output_file().get_filepath(), + "-gravity", + "Center", + "-crop", + "{}x{}+0+0".format(self.width, self.height), + "+repage", + self.get_output_file().get_filepath()] + self._call_cmd(cmd) + + def _subtle_break(self): + # assume the header is no longer than _header_offset bytes + breakpoint = random.randint(_header_offset, len(self._file_data)) + newfile = self._file_data[0:breakpoint] +\ + _subtle_break_mark +\ + self._file_data[breakpoint:] + self._file_data = newfile[0:len(self._file_data)] + + def _extreme_break(self): + increment = len(self._file_data)/10 + i = 0 + newfile = "" + for b in self._file_data: + if i > _header_offset and not i % increment: + b += _extreme_break_mark + newfile += b + i += 1 + self._file_data = newfile[0:len(self._file_data)] + + def _enforce_jpg(self): + if self.params.breaktype in ["exr", "bmp", "miff"] and not \ + re.match( + r'jpe?g$', + self.params.url.mimetype, + re.IGNORECASE + ): + jpg_file = File( + is_temp=True, + extension="jpg", + ) + self._call_cmd( + [BIN_CONVERT, self.params.url.path, jpg_file.get_filepath()]) + self._files_created.append(jpg_file) + self._conversion_file = jpg_file + + def _first_conversion(self): + if self.params.url.mimetype == self.params.breaktype: + self._conversion_file = self.params.url.path + return + self._call_cmd([BIN_CONVERT, + self.params.url.get_file().get_filepath(), + self._conversion_file.get_filepath()]) + self._files_created.append(self._conversion_file) + + def _prepare_filedata(self): + if self.params.url.mimetype == "gif" and\ + self.params.breaktype not in ['mat', 'psd']: + self.params.url.get_file().choose_gif_frame(module=self) + if self.params.breakangle: + self._rotate() + self._enforce_jpg() + self._first_conversion() + self._file_data = self._conversion_file.get_raw_data() + if not self._file_data: + self.err_warn("Unable to get file data") + + def _add_false_data(self): + if self.params.breakmode == "subtle": + self._subtle_break() + elif self.params.breakmode == "extreme": + self._extreme_break() + f = open(self._conversion_file.get_filepath(), 'w') + f.write(self._file_data) + f.close() + + def _final_conversion(self): + self._call_cmd([ + BIN_CONVERT, + self._conversion_file.get_filepath(), + self.get_output_file().get_filepath()]) + + def psd_psbfilepath(num): + return os.path.join( + re.sub( + r'\.', "-%s." % num, self.get_output_file().get_filepath()) + ) + if str(self.params.breaktype) == 'psd': + self.get_output_file().set_filepath(psd_psbfilepath(1), module=self) + self._files_created.append(psd_psbfilepath(0)) + if str(self.params.breaktype) == 'psb': + self.get_output_file().set_filepath(psd_psbfilepath(0), module=self) + self._files_created.append(psd_psbfilepath(1)) + if self.params.breakangle: + self._rotate_back() + + def create(self): + self._prepare_filedata() + self._add_false_data() + self._final_conversion() + super(PbBreaker, self).create() + + +def get_class(): + return PbBreaker diff --git a/photoblaster/modules/pbgenerate.py b/photoblaster/modules/pbgenerate.py deleted file mode 100755 index 1dd264b..0000000 --- a/photoblaster/modules/pbgenerate.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/python2.7 -from photoblaster.config import BIN_CONVERT, OUTPUT_IMAGE_TYPES,\ - DEFAULT_FINALFORMAT -from photoblaster.modules import Pb -from photoblaster._file import File - -_GRAVITY_PARAMS = [ - "NorthWest", "North", "NorthEast", "West", - "Center", "East", "SouthWest", "South", "SouthEast" -] -_GRAVITY_DEFAULT = "Center" -_COMPOSE_PARAMS = [ - "Over", "ATop", "Dst_Over", "Dst_In", "Dst_Out", "Multiply", - "Screen", "Divide", "Plus", "Difference", "Exclusion", "Pin_Light", - "Lighten", "Darken", "Overlay", "Hard_Light", "Soft_Light", - "Linear_Dodge", "Linear_Burn", "Color_Dodge", "Color_Burn", "Pegtop_Light" -] -_DISPOSE_PARAMS = ["None", "Previous", "Background"] -_DISPOSE_DEFAULT = "None" -_HUE_DEFUALT = '100' - - -class PbGenerate(Pb): - example_params = { - """Example params. Used with the classmethod Pb.run_example""" - 'nearest': 'true', - 'compose': 'Soft_Light', - 'coalesce': 'true', - 'dispose': 'None', - 'gravity': 'Center', - 'width': '200', - 'black': 'black', - 'tile': 'true', - 'white': 'white', - 'contrast': '100', - 'hue': '90', - 'saturation': '100', - 'merge_early': 'true', - 'format': 'gif', - 'background': 'http://i.asdf.us/im/bc/new_1430440747.gif', - 'subtract': '#EE7AE9', - 'transparent': 'true', - 'name': 'yo', - 'url': 'http://asdf.us/im/new.gif', - 'flop': 'true', - 'flip': 'false', - 'callback': 'jsonp1430442384162', - 'fuzz': '5' - } - - def __init__(self, **kwargs): - super(PbGenerate, self).__init__(**kwargs) - """ - Used to assert the value-types of the incoming parameters. - Types are defined as in their individual params classes. - """ - _definitions = { - # IMAGES - 'url': {'type': 'img_url'}, - 'background': {'type': 'img_url'}, - - # BOOLS - 'coalesce': {'type': 'bool'}, - 'nearest': {'type': 'bool'}, - 'merge_early': {'type': 'bool'}, - 'flip': {'type': 'bool'}, - 'flop': {'type': 'bool'}, - 'tile': {'type': 'bool'}, - 'transparent': {'type': 'bool'}, - - # COLORS - 'black': {'type': 'color', 'default': 'black'}, - 'white': {'type': 'color', 'default': 'white'}, - 'subtract': {'type': 'color'}, - - # INTS - 'fuzz': {'type': 'int'}, - 'width': {'type': 'int'}, - 'height': {'type': 'int'}, - 'brightness': {'type': 'int'}, - 'contrast': {'type': 'int'}, - 'saturation': {'type': 'int'}, - 'rotate': {'type': 'int'}, - 'hue': {'type': 'int', 'default': _HUE_DEFUALT}, - - # ENUMS - 'compose': { - 'type': 'enum', - 'enum_values': _COMPOSE_PARAMS, - 'default': 'Atop'}, - 'gravity': { - 'type': 'enum', - 'enum_values': _GRAVITY_PARAMS, - 'default': _GRAVITY_DEFAULT - }, - 'dispose': { - 'type': 'enum', - 'enum_values': _DISPOSE_PARAMS, - 'default': 'None' - }, - 'format': { - 'type': 'enum', - 'enum_values': OUTPUT_IMAGE_TYPES, - 'default': DEFAULT_FINALFORMAT - }, - - # STRINGS - "username": {'type': "string"}, - "callback": {'type': "string"}, - } - - """ - Definitions and arguments are merged into attributes - of the params object""" - self.params.definitions_import( - _definitions, kwargs, classname=self.__class__.__name__ - ) - - """Used for the database tag column. Allows for tracking of the type - of overlay method used.""" - self.tag = self.__class__.__name__ - if self.params.background: - self.tag = "%s:%s" % (self.tag, str(self.params.compose)) - elif self.params.transparent: - self.tag = "%s:%s" % (self.tag, "transparent") - - self.set_output_file( - File.from_url( - self.params.url['url'], - username=self.params.username, - classname=self.__class__.__name__, - extension=self.params.format - ) - ) - - self._db_url_param = str(self.params.url['url']) - - def _composite(self): - """Imagemagick composite command""" - cmd = [ - BIN_CONVERT, self.params.background.get_file().get_filepath(), - "null:", self.get_output_file().get_filepath(), "-matte", - "-dispose", self.params.dispose, - "-gravity", self.params.gravity, - "-compose", self.params.compose, "-layers", "composite", - self.get_output_file().get_filepath() - ] - self._call_cmd(cmd) - - def _convert(self): - """Imagemagick convert command""" - cmd = [BIN_CONVERT, self.params.url.get_file().get_filepath()] - if self.params.rotate: - cmd += ["-rotate", self.params.rotate] - if self.params.flip: - cmd += ["-flip"] - if self.params.flop: - cmd += ["-flop"] - if self.params.transparent: - if self.params.fuzz: - cmd += ["-fuzz", "{}%".format(self.params.fuzz)] - cmd += ["-transparent", self.params.subtract] - if self.params.width or self.params.height: - if self.params.nearest and self.params.format == "gif": - cmd += [ - "-coalesce", "+map", - "-interpolate", "Nearest", - "-interpolative-resize" - ] - else: - cmd.append("-resize") - cmd += [ - "{}x{}".format( - self.params.width or "", self.params.height or "" - ) - ] - if self.params.black != "black" or self.params.white != 'white': - cmd += [ - "+level-colors", - "{},{}".format(self.params.black, self.params.white) - ] - if self.params.contrast: - cmd += ['-contrast-stretch', self.params.contrast] - if self.params.brightness or self.params.saturation or self.params.hue: - cmd += [ - "-modulate", "{},{},{}".format( - self.params.brightness or 100, - self.params.contrast or 100, - self.params.hue or 100 - ) - ] - cmd.append("-coalesce") # why? FIXME - cmd += [self.get_output_file().get_filepath()] - self._call_cmd(cmd) - - def create(self): - self._convert() - if self.params.background: - self._composite() - super(PbGenerate, self).create() diff --git a/photoblaster/modules/pbgenerate/__init__.py b/photoblaster/modules/pbgenerate/__init__.py new file mode 100755 index 0000000..360a3ea --- /dev/null +++ b/photoblaster/modules/pbgenerate/__init__.py @@ -0,0 +1,204 @@ +#!/usr/bin/python2.7 +from photoblaster.config import BIN_CONVERT, OUTPUT_IMAGE_TYPES,\ + DEFAULT_FINALFORMAT +from photoblaster.modules_src import ModuleBase +from photoblaster._file import File + +_GRAVITY_PARAMS = [ + "NorthWest", "North", "NorthEast", "West", + "Center", "East", "SouthWest", "South", "SouthEast" +] +_GRAVITY_DEFAULT = "Center" +_COMPOSE_PARAMS = [ + "Over", "ATop", "Dst_Over", "Dst_In", "Dst_Out", "Multiply", + "Screen", "Divide", "Plus", "Difference", "Exclusion", "Pin_Light", + "Lighten", "Darken", "Overlay", "Hard_Light", "Soft_Light", + "Linear_Dodge", "Linear_Burn", "Color_Dodge", "Color_Burn", "Pegtop_Light" +] +_DISPOSE_PARAMS = ["None", "Previous", "Background"] +_DISPOSE_DEFAULT = "None" +_HUE_DEFUALT = '100' + + +class PbGenerate(ModuleBase): + example_params = { + """Example params. Used with the classmethod Pb.run_example""" + 'nearest': 'true', + 'compose': 'Soft_Light', + 'coalesce': 'true', + 'dispose': 'None', + 'gravity': 'Center', + 'width': '200', + 'black': 'black', + 'tile': 'true', + 'white': 'white', + 'contrast': '100', + 'hue': '90', + 'saturation': '100', + 'merge_early': 'true', + 'format': 'gif', + 'background': 'http://i.asdf.us/im/bc/new_1430440747.gif', + 'subtract': '#EE7AE9', + 'transparent': 'true', + 'name': 'yo', + 'url': 'http://asdf.us/im/new.gif', + 'flop': 'true', + 'flip': 'false', + 'callback': 'jsonp1430442384162', + 'fuzz': '5' + } + + def __init__(self, **kwargs): + super(PbGenerate, self).__init__(**kwargs) + """ + Used to assert the value-types of the incoming parameters. + Types are defined as in their individual params classes. + """ + _definitions = { + # IMAGES + 'url': {'type': 'img_url'}, + 'background': {'type': 'img_url'}, + + # BOOLS + 'coalesce': {'type': 'bool'}, + 'nearest': {'type': 'bool'}, + 'merge_early': {'type': 'bool'}, + 'flip': {'type': 'bool'}, + 'flop': {'type': 'bool'}, + 'tile': {'type': 'bool'}, + 'transparent': {'type': 'bool'}, + + # COLORS + 'black': {'type': 'color', 'default': 'black'}, + 'white': {'type': 'color', 'default': 'white'}, + 'subtract': {'type': 'color'}, + + # INTS + 'fuzz': {'type': 'int'}, + 'width': {'type': 'int'}, + 'height': {'type': 'int'}, + 'brightness': {'type': 'int'}, + 'contrast': {'type': 'int'}, + 'saturation': {'type': 'int'}, + 'rotate': {'type': 'int'}, + 'hue': {'type': 'int', 'default': _HUE_DEFUALT}, + + # ENUMS + 'compose': { + 'type': 'enum', + 'enum_values': _COMPOSE_PARAMS, + 'default': 'Atop'}, + 'gravity': { + 'type': 'enum', + 'enum_values': _GRAVITY_PARAMS, + 'default': _GRAVITY_DEFAULT + }, + 'dispose': { + 'type': 'enum', + 'enum_values': _DISPOSE_PARAMS, + 'default': 'None' + }, + 'format': { + 'type': 'enum', + 'enum_values': OUTPUT_IMAGE_TYPES, + 'default': DEFAULT_FINALFORMAT + }, + + # STRINGS + "username": {'type': "string"}, + "callback": {'type': "string"}, + } + + """ + Definitions and arguments are merged into attributes + of the params object""" + self.params.definitions_import( + _definitions, kwargs, classname=self.__class__.__name__ + ) + + """Used for the database tag column. Allows for tracking of the type + of overlay method used.""" + self.tag = self.__class__.__name__ + if self.params.background: + self.tag = "%s:%s" % (self.tag, str(self.params.compose)) + elif self.params.transparent: + self.tag = "%s:%s" % (self.tag, "transparent") + + self.set_output_file( + File.from_url( + self.params.url['url'], + username=self.params.username, + classname=self.__class__.__name__, + extension=self.params.format + ) + ) + + self._db_url_param = str(self.params.url['url']) + + def _composite(self): + """Imagemagick composite command""" + cmd = [ + BIN_CONVERT, self.params.background.get_file().get_filepath(), + "null:", self.get_output_file().get_filepath(), "-matte", + "-dispose", self.params.dispose, + "-gravity", self.params.gravity, + "-compose", self.params.compose, "-layers", "composite", + self.get_output_file().get_filepath() + ] + self._call_cmd(cmd) + + def _convert(self): + """Imagemagick convert command""" + cmd = [BIN_CONVERT, self.params.url.get_file().get_filepath()] + if self.params.rotate: + cmd += ["-rotate", self.params.rotate] + if self.params.flip: + cmd += ["-flip"] + if self.params.flop: + cmd += ["-flop"] + if self.params.transparent: + if self.params.fuzz: + cmd += ["-fuzz", "{}%".format(self.params.fuzz)] + cmd += ["-transparent", self.params.subtract] + if self.params.width or self.params.height: + if self.params.nearest and self.params.format == "gif": + cmd += [ + "-coalesce", "+map", + "-interpolate", "Nearest", + "-interpolative-resize" + ] + else: + cmd.append("-resize") + cmd += [ + "{}x{}".format( + self.params.width or "", self.params.height or "" + ) + ] + if self.params.black != "black" or self.params.white != 'white': + cmd += [ + "+level-colors", + "{},{}".format(self.params.black, self.params.white) + ] + if self.params.contrast: + cmd += ['-contrast-stretch', self.params.contrast] + if self.params.brightness or self.params.saturation or self.params.hue: + cmd += [ + "-modulate", "{},{},{}".format( + self.params.brightness or 100, + self.params.contrast or 100, + self.params.hue or 100 + ) + ] + cmd.append("-coalesce") # why? FIXME + cmd += [self.get_output_file().get_filepath()] + self._call_cmd(cmd) + + def create(self): + self._convert() + if self.params.background: + self._composite() + super(PbGenerate, self).create() + + +def get_class(): + return PbGenerate diff --git a/photoblaster/modules/pbgradient.py b/photoblaster/modules/pbgradient.py deleted file mode 100755 index fe885b5..0000000 --- a/photoblaster/modules/pbgradient.py +++ /dev/null @@ -1,235 +0,0 @@ -"""Creates a gradient image and adds effects to it""" - -from photoblaster.config import DEFAULT_WIDTH, DEFAULT_HEIGHT,\ - DEFAULT_FINALFORMAT, \ - BIN_CONVERT, BEVELBORDER, OUTPUT_IMAGE_TYPES -from photoblaster.modules import Pb -from photoblaster._file import File - -_DEFAULT_COLOR_1 = "white" -_DEFAULT_COLOR_2 = "black" - -_DEFAULT_BEVEL_PERCENT = "12" - -_halftone_values = { - "checkeredfade": "h6x6a", - "etchedtransition": "o8x8", - "bendaydots": "h16x16o", - "smallerdots1": "h8x8o", - "smallerdots2": "c7x7w", - "flatstripes": "o2x2", -} - - -class PbGradient(Pb): - example_params = { - "width": "200", - "color1": "#ffdead", - "color2": "blue", - "stripes": "true", - "stripenumber": "20", - "gradienttype": "plasma", - "stripeintensity": "20", - "halftone": "checkeredfade", - "percentbeveled": "30", - "flip": "true", - "bevel": "flatinner", - "rotate": "20", - "height": "200", - "filetype": "jpg", - "username": "whatever" - } - - def __init__(self, **kwargs): - super(PbGradient, self).__init__(**kwargs) - _definitions = { - 'width': {'type': 'int', 'default': DEFAULT_WIDTH}, - 'height': {'type': 'int', 'default': DEFAULT_HEIGHT}, - 'color1': {'type': 'color', 'default': _DEFAULT_COLOR_1}, - 'color2': {'type': 'color', 'default': _DEFAULT_COLOR_2}, - 'stripes': {'type': 'bool'}, - 'stripenumber': {'type': 'int', 'default': 0}, - 'stripeintensity': {'type': 'int', 'default': 0}, - 'blurriness': {'type': 'int', 'default': 0}, - 'contrast': {'type': 'int', 'default': 100}, - 'brightness': {'type': 'int', 'default': 100}, - 'saturation': {'type': 'int', 'default': 100}, - 'hue': {'type': 'int', 'default': 100}, - 'halftone': {'type': 'enum', 'enum_values': [ - 'checkeredfade', 'etchedtransition', 'bendaydots', - 'smallerdots1', 'smallerdots2', 'flatstripes', - ]}, - 'bevel': {'type': 'enum', 'enum_values': [ - 'flatout', 'flatinner', 'evenlyframed', 'biginner', - 'bigouter', 'dramaticflatout', 'dramaticflatinner', - ]}, - 'percentbeveled': { - 'type': 'int', 'default': _DEFAULT_BEVEL_PERCENT}, - 'tilt': {'type': 'int'}, - 'rotate': {'type': 'int'}, - 'flip': {'type': 'bool'}, - 'flop': {'type': 'bool'}, - 'filetype': { - 'type': 'enum', - 'enum_values': OUTPUT_IMAGE_TYPES, - 'default': DEFAULT_FINALFORMAT - }, - 'gradienttype': {'type': 'enum', 'enum_values': [ - 'gradient', 'canvas', 'plasma', 'radial', 'colorspace', - 'mirrored', 'plasmawash', 'gradientwash', 'noise' - ], 'default': 'gradient'}, - 'username': {'type': 'string'} - } - self.params.definitions_import( - _definitions, kwargs, classname=self.__class__.__name__) - - namepart = "%s-%s" % ( - str(self.params.color1).replace( - '#', '').replace('(', '-').replace(')', '-'), - str(self.params.color2).replace( - '#', '').replace('(', '-').replace(')', '-') - ) - self.set_output_file( - File( - namepart=namepart, - extension=self.params.filetype, - classname=self.__class__.__name__, - username=self.params.username) - ) - - def _build_cmd(self): - cmd = [BIN_CONVERT] - cmd.extend([ - '-size', - "{}x{}".format(self.params.width, self.params.height) - ]) - - if self.params.rotate: - cmd.extend(["-rotate", str(self.params.rotate)]) - if self.params.tilt: - cmd.extend(["-distort", "SRT", str(self.params.tilt)]) - if self.params.flip == "true": - cmd.append("-flip") - if self.params.flop == "true": - cmd.append("-flop") - if self.params.contrast: - cmd.extend(["-contrast-stretch", str(self.params.contrast)]) - _gradients = { - "gradient": [ - "gradient:{}-{}".format( - self.params.color1, self.params.color2)], - "canvas": ["canvas:{}".format(self.params.color1)], - "radial": [ - "radial-gradient:{}-{}".format( - self.params.color1, self.params.color2) - ], - "plasma": [ - "plasma:{}-{}".format(self.params.color1, self.params.color2) - ], - "colorspace": [ - "-colorspace", - "Gray", - "plasma:{}-{}".format( - self.params.color1, - self.params.color2 - ) - ], - "mirrored": [ - "plasma:{}-{}".format( - self.params.color1, - self.params.color2 - ), - "(", "+clone", "-flop", ")", - "+append" - ], - "plasmawash": [ - "plasma:{}-{}".format( - self.params.color1, - self.params.color2 - ), - "-set", "colorspace", "HSB" - ], - "gradientwash": [ - "gradient:{}-{}".format( - self.params.color1, - self.params.color2 - ), - "-set", "colorspace", "HSB" - ], - "noise": ["xc:", "+noise", "Random", "-virtual-pixel", "tile"] - } - cmd += _gradients[str(self.params.gradienttype)] - - if self.params.blurriness: - cmd.extend( - ["-blur", "0x{}".format(self.params.blurriness), "-auto-level"]) - - if self.params.stripes and self.params.stripenumber: - cmd.extend(["-function", "Sinusoid"]) - if self.params.stripeintensity: - cmd.append("{},{}".format( - self.params.stripenumber, - self.params.stripeintensity - ) - ) - else: - cmd.append(str(self.params.stripenumber)) - if str(self.params.halftone) in _halftone_values: - cmd.extend([ - "-ordered-dither", - _halftone_values[str(self.params.halftone)] - ]) - cmd += [ - '-modulate', - "{},{},{}".format( - self.params.brightness or "100", - self.params.saturation or "100", - self.params.hue or "100" - ) - ] - cmd.append(self.get_output_file().get_filepath()) - self._call_cmd(cmd) - if self.params.bevel: - self._make_bevel() - - def _get_bevelvalue(self): - w, h = map(int, (self.params.width, self.params.height)) - if h >= w: - bevpercentval = str( - int(int(self.params.percentbeveled)*int(h))/500) - else: - bevpercentval = str( - int(int(self.params.percentbeveled)*int(w))/500) - return { - "flatout": ["-s", bevpercentval, "-m", "outer"], - "flatinner": ["-s", bevpercentval, "-m", "inner"], - "evenlyframed": ["-s", bevpercentval, "-m", "split"], - "biginner": [ - "-s", - bevpercentval, - "-m", - "outer", - "-c", - "50", "-b", "red", "-a", "25"], - "bigouter": [ - "-s", - bevpercentval, - "-m", - "split", "-c", "50", "-b", "red", "-a", "25"], - "dramaticflatout": [ - "-s", bevpercentval, "-m", "outer", "-a", "25", "-b", "blue"], - "dramaticflatinner": [ - "-s", bevpercentval, "-m", "outer", "-a", "25", "-b", "blue"], - }[str(self.params.bevel)] - - def _make_bevel(self): - cmd = [BEVELBORDER] - cmd += self._get_bevelvalue() - cmd += [ - self.get_output_file().get_filepath(), - self.get_output_file().get_filepath()] - self._call_cmd(cmd) - - def create(self): - self._build_cmd() - super(PbGradient, self).create() diff --git a/photoblaster/modules/pbgradient/__init__.py b/photoblaster/modules/pbgradient/__init__.py new file mode 100755 index 0000000..343c35a --- /dev/null +++ b/photoblaster/modules/pbgradient/__init__.py @@ -0,0 +1,239 @@ +"""Creates a gradient image and adds effects to it""" + +from photoblaster.config import DEFAULT_WIDTH, DEFAULT_HEIGHT,\ + DEFAULT_FINALFORMAT, \ + BIN_CONVERT, BEVELBORDER, OUTPUT_IMAGE_TYPES +from photoblaster.modules_src import ModuleBase +from photoblaster._file import File + +_DEFAULT_COLOR_1 = "white" +_DEFAULT_COLOR_2 = "black" + +_DEFAULT_BEVEL_PERCENT = "12" + +_halftone_values = { + "checkeredfade": "h6x6a", + "etchedtransition": "o8x8", + "bendaydots": "h16x16o", + "smallerdots1": "h8x8o", + "smallerdots2": "c7x7w", + "flatstripes": "o2x2", +} + + +class PbGradient(ModuleBase): + example_params = { + "width": "200", + "color1": "#ffdead", + "color2": "blue", + "stripes": "true", + "stripenumber": "20", + "gradienttype": "plasma", + "stripeintensity": "20", + "halftone": "checkeredfade", + "percentbeveled": "30", + "flip": "true", + "bevel": "flatinner", + "rotate": "20", + "height": "200", + "filetype": "jpg", + "username": "whatever" + } + + def __init__(self, **kwargs): + super(PbGradient, self).__init__(**kwargs) + _definitions = { + 'width': {'type': 'int', 'default': DEFAULT_WIDTH}, + 'height': {'type': 'int', 'default': DEFAULT_HEIGHT}, + 'color1': {'type': 'color', 'default': _DEFAULT_COLOR_1}, + 'color2': {'type': 'color', 'default': _DEFAULT_COLOR_2}, + 'stripes': {'type': 'bool'}, + 'stripenumber': {'type': 'int', 'default': 0}, + 'stripeintensity': {'type': 'int', 'default': 0}, + 'blurriness': {'type': 'int', 'default': 0}, + 'contrast': {'type': 'int', 'default': 100}, + 'brightness': {'type': 'int', 'default': 100}, + 'saturation': {'type': 'int', 'default': 100}, + 'hue': {'type': 'int', 'default': 100}, + 'halftone': {'type': 'enum', 'enum_values': [ + 'checkeredfade', 'etchedtransition', 'bendaydots', + 'smallerdots1', 'smallerdots2', 'flatstripes', + ]}, + 'bevel': {'type': 'enum', 'enum_values': [ + 'flatout', 'flatinner', 'evenlyframed', 'biginner', + 'bigouter', 'dramaticflatout', 'dramaticflatinner', + ]}, + 'percentbeveled': { + 'type': 'int', 'default': _DEFAULT_BEVEL_PERCENT}, + 'tilt': {'type': 'int'}, + 'rotate': {'type': 'int'}, + 'flip': {'type': 'bool'}, + 'flop': {'type': 'bool'}, + 'filetype': { + 'type': 'enum', + 'enum_values': OUTPUT_IMAGE_TYPES, + 'default': DEFAULT_FINALFORMAT + }, + 'gradienttype': {'type': 'enum', 'enum_values': [ + 'gradient', 'canvas', 'plasma', 'radial', 'colorspace', + 'mirrored', 'plasmawash', 'gradientwash', 'noise' + ], 'default': 'gradient'}, + 'username': {'type': 'string'} + } + self.params.definitions_import( + _definitions, kwargs, classname=self.__class__.__name__) + + namepart = "%s-%s" % ( + str(self.params.color1).replace( + '#', '').replace('(', '-').replace(')', '-'), + str(self.params.color2).replace( + '#', '').replace('(', '-').replace(')', '-') + ) + self.set_output_file( + File( + namepart=namepart, + extension=self.params.filetype, + classname=self.__class__.__name__, + username=self.params.username) + ) + + def _build_cmd(self): + cmd = [BIN_CONVERT] + cmd.extend([ + '-size', + "{}x{}".format(self.params.width, self.params.height) + ]) + + if self.params.rotate: + cmd.extend(["-rotate", str(self.params.rotate)]) + if self.params.tilt: + cmd.extend(["-distort", "SRT", str(self.params.tilt)]) + if self.params.flip == "true": + cmd.append("-flip") + if self.params.flop == "true": + cmd.append("-flop") + if self.params.contrast: + cmd.extend(["-contrast-stretch", str(self.params.contrast)]) + _gradients = { + "gradient": [ + "gradient:{}-{}".format( + self.params.color1, self.params.color2)], + "canvas": ["canvas:{}".format(self.params.color1)], + "radial": [ + "radial-gradient:{}-{}".format( + self.params.color1, self.params.color2) + ], + "plasma": [ + "plasma:{}-{}".format(self.params.color1, self.params.color2) + ], + "colorspace": [ + "-colorspace", + "Gray", + "plasma:{}-{}".format( + self.params.color1, + self.params.color2 + ) + ], + "mirrored": [ + "plasma:{}-{}".format( + self.params.color1, + self.params.color2 + ), + "(", "+clone", "-flop", ")", + "+append" + ], + "plasmawash": [ + "plasma:{}-{}".format( + self.params.color1, + self.params.color2 + ), + "-set", "colorspace", "HSB" + ], + "gradientwash": [ + "gradient:{}-{}".format( + self.params.color1, + self.params.color2 + ), + "-set", "colorspace", "HSB" + ], + "noise": ["xc:", "+noise", "Random", "-virtual-pixel", "tile"] + } + cmd += _gradients[str(self.params.gradienttype)] + + if self.params.blurriness: + cmd.extend( + ["-blur", "0x{}".format(self.params.blurriness), "-auto-level"]) + + if self.params.stripes and self.params.stripenumber: + cmd.extend(["-function", "Sinusoid"]) + if self.params.stripeintensity: + cmd.append("{},{}".format( + self.params.stripenumber, + self.params.stripeintensity + ) + ) + else: + cmd.append(str(self.params.stripenumber)) + if str(self.params.halftone) in _halftone_values: + cmd.extend([ + "-ordered-dither", + _halftone_values[str(self.params.halftone)] + ]) + cmd += [ + '-modulate', + "{},{},{}".format( + self.params.brightness or "100", + self.params.saturation or "100", + self.params.hue or "100" + ) + ] + cmd.append(self.get_output_file().get_filepath()) + self._call_cmd(cmd) + if self.params.bevel: + self._make_bevel() + + def _get_bevelvalue(self): + w, h = map(int, (self.params.width, self.params.height)) + if h >= w: + bevpercentval = str( + int(int(self.params.percentbeveled)*int(h))/500) + else: + bevpercentval = str( + int(int(self.params.percentbeveled)*int(w))/500) + return { + "flatout": ["-s", bevpercentval, "-m", "outer"], + "flatinner": ["-s", bevpercentval, "-m", "inner"], + "evenlyframed": ["-s", bevpercentval, "-m", "split"], + "biginner": [ + "-s", + bevpercentval, + "-m", + "outer", + "-c", + "50", "-b", "red", "-a", "25"], + "bigouter": [ + "-s", + bevpercentval, + "-m", + "split", "-c", "50", "-b", "red", "-a", "25"], + "dramaticflatout": [ + "-s", bevpercentval, "-m", "outer", "-a", "25", "-b", "blue"], + "dramaticflatinner": [ + "-s", bevpercentval, "-m", "outer", "-a", "25", "-b", "blue"], + }[str(self.params.bevel)] + + def _make_bevel(self): + cmd = [BEVELBORDER] + cmd += self._get_bevelvalue() + cmd += [ + self.get_output_file().get_filepath(), + self.get_output_file().get_filepath()] + self._call_cmd(cmd) + + def create(self): + self._build_cmd() + super(PbGradient, self).create() + + +def get_class(): + return PbGradient diff --git a/photoblaster/modules/pbgrid.py b/photoblaster/modules/pbgrid.py deleted file mode 100755 index c43c4b1..0000000 --- a/photoblaster/modules/pbgrid.py +++ /dev/null @@ -1,234 +0,0 @@ -from photoblaster.config import DEFAULT_FINALFORMAT, DEFAULT_HEIGHT,\ - DEFAULT_WIDTH, OUTPUT_IMAGE_TYPES,\ - THREEDROTATE, GRID, BIN_CONVERT, BIN_COMPOSITE -from photoblaster.modules import Pb -from photoblaster._file import File - -_DEFAULT_LINE_COLOR = "silver" - - -class PbGrid(Pb): - """ - Creates or overlays a grid on an image, and adds 3D perspective - """ - example_params = { - 'bgimage': - 'http://i.asdf.us/im/1a/imBreak_1424909483_xx_abridged___.gif', - 'planebgimage': 'http://i.imgur.com/FICZtph.png', - 'tilt': '30', - 'spacing': '30', - 'hlines': 'true', - 'roll': '30', - 'shadow': 'true', - 'username': 'someuser', - 'trim': 'true' - } - - def __init__(self, **kwargs): - super(PbGrid, self).__init__(**kwargs) - _definitions = { - 'width': {'type': 'int'}, - 'height': {'type': 'int'}, - 'linethickness': {'type': 'int', 'default': 1}, - 'opacity': {'type': 'float', "default": 1.0}, - 'linecolor': {'type': 'color', 'default': 'whitesmoke'}, - 'spacing': {'type': 'int', 'default': 10}, - 'vlines': {'type': 'bool'}, - 'hlines': {'type': 'bool'}, - 'shadow': {'type': 'bool'}, - 'bgimage': {'type': 'img_url'}, - 'bgcolor': {'type': 'color', 'default': 'transparent'}, - 'imageinstead': {'type': 'img_url'}, - 'planebgcolor': {'type': 'color', 'default': 'transparent'}, - 'planebgimage': {'type': 'img_url'}, - 'swing': {'type': 'int'}, - 'tilt': {'type': 'int'}, - 'roll': {'type': 'int'}, - 'zoom': {'type': 'float'}, - 'skycolor': {'type': 'color', 'default': 'transparent'}, - 'transition': { - 'type': 'enum', - 'enum_values': [ - 'background', 'dither', 'edge', 'mirror', 'random', 'tile' - ], - 'default': 'background' - }, - 'trim': {'type': 'bool'}, - 'finalformat': { - 'type': 'enum', - 'enum_values': OUTPUT_IMAGE_TYPES, - 'default': DEFAULT_FINALFORMAT - }, - 'username': {'type': 'string'}, - } - self.params.definitions_import( - _definitions, kwargs, classname=self.__class__.__name__ - ) - if self.params.imageinstead: - self.set_output_file( - File.from_url( - self.params.imageinstead['url'], - extension=self.params.finalformat, - classname=self.__class__.__name__, - username=self.params.username) - ) - elif self.params.planebgimage: - self.set_output_file( - File.from_url( - self.params.planebgimage['url'], - extension=self.params.finalformat, - classname=self.__class__.__name__, - username=self.params.username) - ) - else: - self.set_output_file( - File( - extension=self.params.finalformat, - classname=self.__class__.__name__, - username=self.params.username) - ) - - self._db_url_param = str( - filter( - lambda n: n, [ - self.params.imageinstead, - self.params.planebgimage, - self.params.bgimage, - "NULL" - ] - )[0] - ) - - #makes a canvas file...step 1 (if not bgimage) - def _make_canvas(self): - dimensions = "{}x{}".format( - self.params.width or DEFAULT_WIDTH, - self.params.height or DEFAULT_HEIGHT - ) - if self.params.bgimage: - return - bgcolor = "xc:{}".format(self.params.bgcolor or 'transparent') - cmd = [ - BIN_CONVERT, - "-size", dimensions, bgcolor, - self.get_output_file().get_filepath()] - self._call_cmd(cmd) - - #2nd step-- run grid - def _grid_command(self): - cmd = [GRID] - if self.params.spacing: - if self.params.vlines: - width = 2 * int(self.params.width or DEFAULT_WIDTH) - cmd += ["-s", "{},{}".format(self.params.spacing, width)] - elif self.params.hlines: - height = 2 * int(self.params.height or DEFAULT_HEIGHT) - cmd += ["-s", "{},{}".format(height, self.params.spacing)] - else: - cmd += ["-s", self.params.spacing] - cmd += ["-c", self.params.linecolor or _DEFAULT_LINE_COLOR] - if self.params.linethickness: - cmd += ['-t', self.params.linethickness] - if self.params.opacity: - cmd += ['-o', self.params.opacity] - cmd += [ - self.get_output_file().get_filepath(), - self.get_output_file().get_filepath()] - self._call_cmd(cmd) - - def _shadow_cmd(self): - """ - convert 1.png \ - \( +clone -background black -shadow 110x1+9+9 \) \ - +swap -background none -layers merge +repage 2.png - """ - cmd = [ - BIN_CONVERT, - self.get_output_file().get_filepath(), - "(", - "+clone", "-background", "black", "-shadow", "100x2+20+10", ")", - "+swap", "-background", "none", "-layers", "merge", "+repage", - self.get_output_file().get_filepath() - ] - self._call_cmd(cmd) - - def _threed_rotate_cmd(self): - #3rd step--run 3Drotate - cmd = [THREEDROTATE] - if self.params.swing: - cmd += ["pan={}".format(self.params.swing)] - if self.params.tilt: - cmd += ["tilt={}".format(self.params.tilt)] - if self.params.roll: - cmd += ["roll={}".format(self.params.roll)] - if self.params.zoom: - cmd += ["zoom={}".format(self.params.zoom)] - if cmd == [THREEDROTATE]: # if nothing has been added - return - if self.params.planebgcolor and not self.params.planebgimage: - cmd += ["bgcolor={}".format(self.params.planebgcolor)] - else: - cmd += ["bgcolor=none"] - cmd += ["skycolor={}".format(self.params.skycolor or 'none')] - if self.params.transition: - cmd += ["vp={}".format(self.params.transition)] - cmd += [ - self.get_output_file().get_filepath(), - self.get_output_file().get_filepath()] - self._call_cmd(cmd) - - def _trim_cmd(self): - cmd = [ - BIN_CONVERT, - self.get_output_file().get_filepath(), - "-trim", "+repage", self.get_output_file().get_filepath()] - self._call_cmd(cmd) - - def _prepare_gridimage(self, image): - if image['mimetype'] == 'gif': - image.get_file().choose_gif_frame(module=self) - if image['mimetype'] != 'png': - cmd = [ - BIN_CONVERT, - image.get_file().get_filepath(), - self.get_output_file().get_filepath()] - else: - cmd = [ - 'cp', image.get_file().get_filepath(), - self.output_file.get_filepath()] - self._call_cmd(cmd) - - def _overlay_planebgimage(self): - import sys - sys.stderr.write("should be overlaying here!\n\n\n") - cmd = [ - BIN_COMPOSITE, - "-compose", "Dst_Over", "-gravity", "center", - self.params.planebgimage["path"], - self.get_output_file().get_filepath(), - self.get_output_file().get_filepath() - ] - self._call_cmd(cmd) - - def create(self): - if self.params.bgimage: - self._prepare_gridimage(self.params.bgimage) - self._grid_command() - elif self.params.imageinstead: - self._prepare_gridimage(self.params.imageinstead) - else: - self._make_canvas() - self._grid_command() - if self.params.shadow: - self._shadow_cmd() - self._threed_rotate_cmd() - if self.params.planebgimage: - import sys - sys.stderr.write("what the fuck!!!") - self._overlay_planebgimage() - else: - import sys - sys.stderr.write("UMMMMMMMMMMMMMMMMMM\n") - if self.params.trim: - self._trim_cmd() - super(PbGrid, self).create() diff --git a/photoblaster/modules/pbgrid/__init__.py b/photoblaster/modules/pbgrid/__init__.py new file mode 100755 index 0000000..bff0ad6 --- /dev/null +++ b/photoblaster/modules/pbgrid/__init__.py @@ -0,0 +1,239 @@ +from photoblaster.config import DEFAULT_FINALFORMAT, DEFAULT_HEIGHT,\ + DEFAULT_WIDTH, OUTPUT_IMAGE_TYPES,\ + THREEDROTATE, GRID, BIN_CONVERT, BIN_COMPOSITE +from photoblaster.modules_src import ModuleBase +from photoblaster._file import File + +_DEFAULT_LINE_COLOR = "silver" + + +class PbGrid(ModuleBase): + """ + Creates or overlays a grid on an image, and adds 3D perspective + """ + example_params = { + 'bgimage': + 'http://i.asdf.us/im/1a/imBreak_1424909483_xx_abridged___.gif', + 'planebgimage': 'http://i.imgur.com/FICZtph.png', + 'tilt': '30', + 'spacing': '30', + 'hlines': 'true', + 'roll': '30', + 'shadow': 'true', + 'username': 'someuser', + 'trim': 'true' + } + + def __init__(self, **kwargs): + super(PbGrid, self).__init__(**kwargs) + _definitions = { + 'width': {'type': 'int'}, + 'height': {'type': 'int'}, + 'linethickness': {'type': 'int', 'default': 1}, + 'opacity': {'type': 'float', "default": 1.0}, + 'linecolor': {'type': 'color', 'default': 'whitesmoke'}, + 'spacing': {'type': 'int', 'default': 10}, + 'vlines': {'type': 'bool'}, + 'hlines': {'type': 'bool'}, + 'shadow': {'type': 'bool'}, + 'bgimage': {'type': 'img_url'}, + 'bgcolor': {'type': 'color', 'default': 'transparent'}, + 'imageinstead': {'type': 'img_url'}, + 'planebgcolor': {'type': 'color', 'default': 'transparent'}, + 'planebgimage': {'type': 'img_url'}, + 'swing': {'type': 'int'}, + 'tilt': {'type': 'int'}, + 'roll': {'type': 'int'}, + 'zoom': {'type': 'float'}, + 'skycolor': {'type': 'color', 'default': 'transparent'}, + 'transition': { + 'type': 'enum', + 'enum_values': [ + 'background', 'dither', 'edge', 'mirror', 'random', 'tile' + ], + 'default': 'background' + }, + 'trim': {'type': 'bool'}, + 'finalformat': { + 'type': 'enum', + 'enum_values': OUTPUT_IMAGE_TYPES, + 'default': DEFAULT_FINALFORMAT + }, + 'username': {'type': 'string'}, + } + self.params.definitions_import( + _definitions, kwargs, classname=self.__class__.__name__ + ) + if self.params.imageinstead: + self.set_output_file( + File.from_url( + self.params.imageinstead['url'], + extension=self.params.finalformat, + classname=self.__class__.__name__, + username=self.params.username) + ) + elif self.params.planebgimage: + self.set_output_file( + File.from_url( + self.params.planebgimage['url'], + extension=self.params.finalformat, + classname=self.__class__.__name__, + username=self.params.username) + ) + else: + self.set_output_file( + File( + extension=self.params.finalformat, + classname=self.__class__.__name__, + username=self.params.username) + ) + + self._db_url_param = str( + filter( + lambda n: n, [ + self.params.imageinstead, + self.params.planebgimage, + self.params.bgimage, + "NULL" + ] + )[0] + ) + + #makes a canvas file...step 1 (if not bgimage) + def _make_canvas(self): + dimensions = "{}x{}".format( + self.params.width or DEFAULT_WIDTH, + self.params.height or DEFAULT_HEIGHT + ) + if self.params.bgimage: + return + bgcolor = "xc:{}".format(self.params.bgcolor or 'transparent') + cmd = [ + BIN_CONVERT, + "-size", dimensions, bgcolor, + self.get_output_file().get_filepath()] + self._call_cmd(cmd) + + #2nd step-- run grid + def _grid_command(self): + cmd = [GRID] + if self.params.spacing: + if self.params.vlines: + width = 2 * int(self.params.width or DEFAULT_WIDTH) + cmd += ["-s", "{},{}".format(self.params.spacing, width)] + elif self.params.hlines: + height = 2 * int(self.params.height or DEFAULT_HEIGHT) + cmd += ["-s", "{},{}".format(height, self.params.spacing)] + else: + cmd += ["-s", self.params.spacing] + cmd += ["-c", self.params.linecolor or _DEFAULT_LINE_COLOR] + if self.params.linethickness: + cmd += ['-t', self.params.linethickness] + if self.params.opacity: + cmd += ['-o', self.params.opacity] + cmd += [ + self.get_output_file().get_filepath(), + self.get_output_file().get_filepath()] + self._call_cmd(cmd) + + def _shadow_cmd(self): + """ + convert 1.png \ + \( +clone -background black -shadow 110x1+9+9 \) \ + +swap -background none -layers merge +repage 2.png + """ + cmd = [ + BIN_CONVERT, + self.get_output_file().get_filepath(), + "(", + "+clone", "-background", "black", "-shadow", "100x2+20+10", ")", + "+swap", "-background", "none", "-layers", "merge", "+repage", + self.get_output_file().get_filepath() + ] + self._call_cmd(cmd) + + def _threed_rotate_cmd(self): + #3rd step--run 3Drotate + cmd = [THREEDROTATE] + if self.params.swing: + cmd += ["pan={}".format(self.params.swing)] + if self.params.tilt: + cmd += ["tilt={}".format(self.params.tilt)] + if self.params.roll: + cmd += ["roll={}".format(self.params.roll)] + if self.params.zoom: + cmd += ["zoom={}".format(self.params.zoom)] + if cmd == [THREEDROTATE]: # if nothing has been added + return + if self.params.planebgcolor and not self.params.planebgimage: + cmd += ["bgcolor={}".format(self.params.planebgcolor)] + else: + cmd += ["bgcolor=none"] + cmd += ["skycolor={}".format(self.params.skycolor or 'none')] + if self.params.transition: + cmd += ["vp={}".format(self.params.transition)] + cmd += [ + self.get_output_file().get_filepath(), + self.get_output_file().get_filepath()] + self._call_cmd(cmd) + + def _trim_cmd(self): + cmd = [ + BIN_CONVERT, + self.get_output_file().get_filepath(), + "-trim", "+repage", self.get_output_file().get_filepath()] + self._call_cmd(cmd) + + def _prepare_gridimage(self, image): + if image['mimetype'] == 'gif': + image.get_file().choose_gif_frame(module=self) + if image['mimetype'] != 'png': + cmd = [ + BIN_CONVERT, + image.get_file().get_filepath(), + self.get_output_file().get_filepath()] + else: + cmd = [ + 'cp', image.get_file().get_filepath(), + self.output_file.get_filepath()] + self._call_cmd(cmd) + + def _overlay_planebgimage(self): + import sys + sys.stderr.write("should be overlaying here!\n\n\n") + cmd = [ + BIN_COMPOSITE, + "-compose", "Dst_Over", "-gravity", "center", + self.params.planebgimage["path"], + self.get_output_file().get_filepath(), + self.get_output_file().get_filepath() + ] + self._call_cmd(cmd) + + def create(self): + if self.params.bgimage: + self._prepare_gridimage(self.params.bgimage) + self._grid_command() + elif self.params.imageinstead: + self._prepare_gridimage(self.params.imageinstead) + else: + self._make_canvas() + self._grid_command() + if self.params.shadow: + self._shadow_cmd() + self._threed_rotate_cmd() + if self.params.planebgimage: + import sys + sys.stderr.write("what the fuck!!!") + self._overlay_planebgimage() + else: + import sys + sys.stderr.write("UMMMMMMMMMMMMMMMMMM\n") + if self.params.trim: + self._trim_cmd() + super(PbGrid, self).create() + + +def get_class(): + return PbGrid + diff --git a/photoblaster/modules/pblandscape/__init__.py b/photoblaster/modules/pblandscape/__init__.py index 3044b3b..651f2e9 100755 --- a/photoblaster/modules/pblandscape/__init__.py +++ b/photoblaster/modules/pblandscape/__init__.py @@ -1,10 +1,10 @@ import base64 -from photoblaster.modules import Pb +from photoblaster.modules_src import ModuleBase import urlparse import re from photoblaster._file import File -class PbLandscape(Pb): +class PbLandscape(ModuleBase): try: example_params = { 'imgdata': open( diff --git a/photoblaster/modules/pbpattern.py b/photoblaster/modules/pbpattern.py deleted file mode 100755 index 1e80647..0000000 --- a/photoblaster/modules/pbpattern.py +++ /dev/null @@ -1,115 +0,0 @@ -from photoblaster.config import BIN_CONVERT, BIN_COMPOSITE -from photoblaster.modules import Pb -from PIL import Image -from photoblaster._file import File - -_FUSE_MODE = "Pin_Light" - - -class PbPattern(Pb): - example_params = { - "pattern_data": '{"matrix":[["0","0","0","0","0","1","0","0","0","0"],["0","0","0","0","1","1","1","0","0","0"],["0","0","1","1","1","0","1","0","0","0"],["0","1","1","0","0","0","0","0","0","0"],["0","1","0","0","1","0","0","0","0","0"],["0","1","0","0","1","0","0","0","1","0"],["0","1","0","0","1","1","0","0","1","0"],["0","1","0","0","0","1","1","1","1","0"],["0","1","1","1","1","0","0","0","0","0"],["0","0","0","0","1","0","0","0","0","0"]],"width":"10","height":"10"}', - #"username": "garfield", - #"pattern_url": "http://asdf.us/impattern/patterns/1.png", - "image_url": "http://i.asdf.us/im/be/PinkHijab_1425078647_reye.gif", - } - - def __init__(self, **kwargs): - super(PbPattern, self).__init__(**kwargs) - _definitions = { - 'image_url': {'type': 'img_url'}, - 'pattern_url': {'type': 'img_url'}, - 'pattern_data': {'type': 'json'}, - 'username': {'type': 'string'}, - } - self.params.definitions_import( - _definitions, kwargs, classname=self.__class__.__name__) - self.set_output_file( - File.from_url( - self.params.image_url['url'], - extension=self.params.image_url['mimetype'], - classname=self.__class__.__name__, - username=self.params.username - ) - ) - self.pattern_file = None - if self.params.pattern_data: - self.pattern_file = File( - namepart="pattern", - username=self.params.username, - classname=self.__class__.__name__, - extension="png" - ) - self._from_pattern_data() - elif not self.params.pattern_url: - self.err_warn( - "pattern must be supplied as json array or as a png url") - else: - self.pattern_file = self.params.pattern_url.get_file() - - self._db_url_param = str(self.params.image_url.url) - - def _from_pattern_data(self): - def boolToColor(boolean): - if boolean: - return (0, 0, 0, 255) - else: - return (255, 255, 255, 255) - specs = self.params.pattern_data.value - if int(specs['width']) > 100 or int(specs['height']) > 100: - self.err_warn("height and width need to be less than 100 px") - img = Image.new('RGBA', (int(specs['width']), int(specs['height']))) - pixels = img.load() - for i in range(0, len(specs['matrix'])): - for j in range(0, len(specs['matrix'][i])): - pixels[j, i] = boolToColor(int(specs['matrix'][i][j])) - img.save(self.pattern_file.get_filepath(), "PNG") - - #first step - def _make_canvas(self): - _width, _height = self.params.image_url.get_file().get_dimensions() - cmd = [ - BIN_CONVERT, "-size", _width + "x" + _height, - "canvas:transparent", - self.get_output_file().get_filepath()] - self._call_cmd(cmd) - - #second step use the Canvas as a background - def _make_mask(self): - #tile the pattern pattern on the canvas - cmd = [ - BIN_COMPOSITE, "-tile", self.pattern_file.get_filepath(), - self.get_output_file().get_filepath(), - self.get_output_file().get_filepath()] - self._call_cmd(cmd) - #fuse the tiled file to create a mask - #convert thebg.gif -compose Dst_In null: \ - #thefile.gif -matte -layers composite new.gif - cmd = [ - BIN_CONVERT, - self.get_output_file().get_filepath(), - "-compose", "Dst_In", "null:", - self.params.image_url.get_file().get_filepath(), - "-matte", "-layers", "composite", - self.get_output_file().get_filepath() - - ] - self._call_cmd(cmd) - - #third step - def _fuse_mask(self, fuse_mode=_FUSE_MODE): - cmd = [ - BIN_CONVERT, "-dispose", "2", - self.get_output_file().get_filepath(), - "null:", - self.params.image_url.get_file().get_filepath(), - "-matte", "-compose", fuse_mode, "-layers", "composite", - self.get_output_file().get_filepath() - ] - self._call_cmd(cmd) - - def create(self): - self._make_canvas() - self._make_mask() - self._fuse_mask() - super(PbPattern, self).create() diff --git a/photoblaster/modules/pbpattern/__init__.py b/photoblaster/modules/pbpattern/__init__.py new file mode 100755 index 0000000..355b30f --- /dev/null +++ b/photoblaster/modules/pbpattern/__init__.py @@ -0,0 +1,119 @@ +from photoblaster.config import BIN_CONVERT, BIN_COMPOSITE +from photoblaster.modules_src import ModuleBase +from PIL import Image +from photoblaster._file import File + +_FUSE_MODE = "Pin_Light" + + +class PbPattern(ModuleBase): + example_params = { + "pattern_data": '{"matrix":[["0","0","0","0","0","1","0","0","0","0"],["0","0","0","0","1","1","1","0","0","0"],["0","0","1","1","1","0","1","0","0","0"],["0","1","1","0","0","0","0","0","0","0"],["0","1","0","0","1","0","0","0","0","0"],["0","1","0","0","1","0","0","0","1","0"],["0","1","0","0","1","1","0","0","1","0"],["0","1","0","0","0","1","1","1","1","0"],["0","1","1","1","1","0","0","0","0","0"],["0","0","0","0","1","0","0","0","0","0"]],"width":"10","height":"10"}', + #"username": "garfield", + #"pattern_url": "http://asdf.us/impattern/patterns/1.png", + "image_url": "http://i.asdf.us/im/be/PinkHijab_1425078647_reye.gif", + } + + def __init__(self, **kwargs): + super(PbPattern, self).__init__(**kwargs) + _definitions = { + 'image_url': {'type': 'img_url'}, + 'pattern_url': {'type': 'img_url'}, + 'pattern_data': {'type': 'json'}, + 'username': {'type': 'string'}, + } + self.params.definitions_import( + _definitions, kwargs, classname=self.__class__.__name__) + self.set_output_file( + File.from_url( + self.params.image_url['url'], + extension=self.params.image_url['mimetype'], + classname=self.__class__.__name__, + username=self.params.username + ) + ) + self.pattern_file = None + if self.params.pattern_data: + self.pattern_file = File( + namepart="pattern", + username=self.params.username, + classname=self.__class__.__name__, + extension="png" + ) + self._from_pattern_data() + elif not self.params.pattern_url: + self.err_warn( + "pattern must be supplied as json array or as a png url") + else: + self.pattern_file = self.params.pattern_url.get_file() + + self._db_url_param = str(self.params.image_url.url) + + def _from_pattern_data(self): + def boolToColor(boolean): + if boolean: + return (0, 0, 0, 255) + else: + return (255, 255, 255, 255) + specs = self.params.pattern_data.value + if int(specs['width']) > 100 or int(specs['height']) > 100: + self.err_warn("height and width need to be less than 100 px") + img = Image.new('RGBA', (int(specs['width']), int(specs['height']))) + pixels = img.load() + for i in range(0, len(specs['matrix'])): + for j in range(0, len(specs['matrix'][i])): + pixels[j, i] = boolToColor(int(specs['matrix'][i][j])) + img.save(self.pattern_file.get_filepath(), "PNG") + + #first step + def _make_canvas(self): + _width, _height = self.params.image_url.get_file().get_dimensions() + cmd = [ + BIN_CONVERT, "-size", _width + "x" + _height, + "canvas:transparent", + self.get_output_file().get_filepath()] + self._call_cmd(cmd) + + #second step use the Canvas as a background + def _make_mask(self): + #tile the pattern pattern on the canvas + cmd = [ + BIN_COMPOSITE, "-tile", self.pattern_file.get_filepath(), + self.get_output_file().get_filepath(), + self.get_output_file().get_filepath()] + self._call_cmd(cmd) + #fuse the tiled file to create a mask + #convert thebg.gif -compose Dst_In null: \ + #thefile.gif -matte -layers composite new.gif + cmd = [ + BIN_CONVERT, + self.get_output_file().get_filepath(), + "-compose", "Dst_In", "null:", + self.params.image_url.get_file().get_filepath(), + "-matte", "-layers", "composite", + self.get_output_file().get_filepath() + + ] + self._call_cmd(cmd) + + #third step + def _fuse_mask(self, fuse_mode=_FUSE_MODE): + cmd = [ + BIN_CONVERT, "-dispose", "2", + self.get_output_file().get_filepath(), + "null:", + self.params.image_url.get_file().get_filepath(), + "-matte", "-compose", fuse_mode, "-layers", "composite", + self.get_output_file().get_filepath() + ] + self._call_cmd(cmd) + + def create(self): + self._make_canvas() + self._make_mask() + self._fuse_mask() + super(PbPattern, self).create() + + +def get_class(): + return PbPattern diff --git a/pluginloader.py b/pluginloader.py new file mode 100644 index 0000000..1f568d4 --- /dev/null +++ b/pluginloader.py @@ -0,0 +1,19 @@ +import imp +import os + +PluginFolder = "./photoblaster/modules_src" +MainModule = "__init__" + +def getPlugins(): + plugins = [] + possibleplugins = os.listdir(PluginFolder) + for i in possibleplugins: + location = os.path.join(PluginFolder, i) + if not os.path.isdir(location) or not MainModule + ".py" in os.listdir(location): + continue + info = imp.find_module(MainModule, [location]) + plugins.append({"name": i, "info": info}) + return plugins + +def loadPlugin(plugin): + return imp.load_module(MainModule, *plugin["info"]) diff --git a/run_module_examples.py b/run_module_examples.py index 8a22c10..c6559c8 100644 --- a/run_module_examples.py +++ b/run_module_examples.py @@ -1,9 +1,13 @@ #!/usr/bin/python2.7 """calls the example_run method on all modules""" -from photoblaster.modules import Pb -for cls in Pb.__subclasses__(): - print cls.__name__ - if cls.__name__ == "PbGradient": +import pluginbase +from photoblaster.modules import Modules + + +modules = Modules() +for module_name in modules.list_modules(): + if module_name == 'pbgradient': + cls = modules.get_module(module_name) instance = cls.example_run() instance.get_output_file().s3move() print instance.get_output_file().as_dict() diff --git a/share/install/requirements.txt b/share/install/requirements.txt index 85daaed..4de82f8 100644 --- a/share/install/requirements.txt +++ b/share/install/requirements.txt @@ -2,3 +2,4 @@ Flask-SQLAlchemy Flask SQLAlchemy +pluginbase -- cgit v1.2.3-70-g09d2