NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

icongenerator.py (9322B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\iconrendering2\icongenerator.py
      2 from __future__ import print_function
      3 import os
      4 import osutils
      5 import tempfile
      6 import subprocess
      7 import shutil
      8 import imgdiff
      9 import blue
     10 from iconrendering2.factory.factory import IconRendererFactory
     11 from iconrendering2.const import IconStyle, InputType, Language, DEFAULT_ICON_DIFF_TOLERANCE, ALL_METAGROUPS
     12 try:
     13     from platformtools.compatibility.exposure import perforceutils
     14     try:
     15         perforceutils.get_connection()
     16         p4enabled = True
     17     except perforceutils.P4Exception:
     18         p4enabled = False
     19 
     20 except ImportError:
     21     perforceutils = None
     22     p4enabled = False
     23 
     24 class Options(object):
     25     NONE = 0
     26     USE_TMP_FOLDER = 1
     27     CHECKOUT = 2
     28     CLEAN_TMP_FOLDER = 4
     29     VIEW_OUTPUT = 8
     30     ONLY_USE_EXISTING_BASE_ICONS = 16
     31     DONT_RENDER_METAGROUP_OVERLAYS = 32
     32 
     33 
     34 class _JobInfo(object):
     35 
     36     def __init__(self, options, outputFolder, tmpOutputFolder, renderers, errors):
     37         self.options = options
     38         self.outputFolder = outputFolder
     39         self.tmpOutputFolder = tmpOutputFolder
     40         self.renderers = renderers
     41         self.errors = errors
     42 
     43 
     44 class IconGenerator(object):
     45 
     46     def __init__(self, resourceMapper = None, sofFactory = None, changelist = None, language = Language.ENGLISH, preRenderCallback = None, postRenderCallback = None, options = Options.NONE):
     47         self._resourceMapper = resourceMapper
     48         self._sofFactory = sofFactory
     49         self._changelist = changelist
     50         self._language = language
     51         self._preRenderCallback = preRenderCallback
     52         self._postRenderCallback = postRenderCallback
     53         self._factory = IconRendererFactory(self._resourceMapper, self._sofFactory)
     54         self._options = options
     55         self._options &= ~Options.VIEW_OUTPUT
     56         self._viewOutput = options & Options.VIEW_OUTPUT
     57         self._openedFolders = []
     58         self._scheduledJobs = {}
     59 
     60     def Generate(self, inputType, input, outputFolder = None, metadata = None, desiredStyles = IconStyle.ALL, desiredMetagroups = ALL_METAGROUPS, desiredSizes = None):
     61         errors = []
     62         try:
     63             job = self._CreateJob(inputType, input, outputFolder, metadata, desiredStyles, desiredMetagroups, desiredSizes)
     64             if job:
     65                 errors.extend(job.errors)
     66                 errors.extend(self._Run(input, [job]))
     67         except Exception:
     68             import traceback
     69             errors.append(''.join(traceback.format_exc()))
     70 
     71         return errors
     72 
     73     def Schedule(self, inputType, input, outputFolder = None, metadata = None, desiredStyles = IconStyle.ALL, desiredMetagroups = ALL_METAGROUPS, desiredSizes = None):
     74         job = self._CreateJob(inputType, input, outputFolder, metadata, desiredStyles, desiredMetagroups, desiredSizes)
     75         if job:
     76             self._Schedule(input, job)
     77 
     78     def Unschedule(self, input):
     79         if input in self._scheduledJobs:
     80             self._scheduledJobs.pop(input)
     81 
     82     def RenderScheduled(self):
     83         errors = []
     84         for input, jobs in self._scheduledJobs.iteritems():
     85             for job in jobs:
     86                 errors.extend(job.errors)
     87 
     88             errors.extend(self._Run(input, jobs))
     89 
     90         self._scheduledJobs = {}
     91         return errors
     92 
     93     def CancelScheduled(self):
     94         self._scheduledJobs = {}
     95 
     96     def _Schedule(self, input, job):
     97         if input not in self._scheduledJobs:
     98             self._scheduledJobs[input] = []
     99         self._scheduledJobs[input].append(job)
    100 
    101     def _Run(self, input, jobs):
    102         outputFolders = set()
    103         errors = []
    104 
    105         def _callback(outputList):
    106             if self._postRenderCallback:
    107                 self._postRenderCallback(input, outputList)
    108 
    109         for j in jobs:
    110             outputInfos = []
    111             for r in j.renderers:
    112                 renderErrors = r.Render()
    113                 outputInfos.extend(r.GetOutputList())
    114                 for e in renderErrors:
    115                     errors.append((input, e[0], e[1]))
    116 
    117             outputFiles = [ info.outputPath for info in outputInfos ]
    118             for path in outputFiles:
    119                 print('Generated icon: {}'.format(path))
    120 
    121             if _callback:
    122                 _callback(outputInfos)
    123             if j.options & Options.USE_TMP_FOLDER:
    124                 errors.extend(_CopyToFinalFolder(j.outputFolder, outputFiles, self._changelist if p4enabled else None))
    125             _CleanTmpFolder(j.tmpOutputFolder, j.options)
    126             outputFolders.add(j.outputFolder)
    127 
    128         if self._viewOutput:
    129             for output in outputFolders:
    130                 if output not in self._openedFolders:
    131                     self._openedFolders.append(output)
    132                     resolvedPath = blue.paths.ResolvePath(output)
    133                     subprocess.Popen('explorer %s' % resolvedPath.replace('/', '\\'))
    134 
    135         return errors
    136 
    137     def _CreateJob(self, inputType, input, outputFolder, metadata, desiredStyles, desiredMetagroups, desiredSizes = None):
    138         if not desiredSizes:
    139             desiredSizes = [64, 128, 512]
    140         if metadata and metadata.get('skip', False):
    141             print('Skipping icons, as indicated in the metadata.')
    142             return None
    143         if not self._resourceMapper and inputType in [InputType.RED_FILE, InputType.GRAPHIC_ID]:
    144             print("No Resource Mapper defined, can't generate icons for %s (%s)" % (input, inputType))
    145             return None
    146         if not self._sofFactory and inputType in [InputType.RED_FILE, InputType.GRAPHIC_ID, InputType.DNA]:
    147             print("No SOF Factory defined, can't generate icons for %s (%s)" % (input, inputType))
    148             return None
    149         options = _ReviewOptions(self._options)
    150         outputFolder, tmpOutputFolder = _GetCorrectFolders(outputFolder, options)
    151         onlyUseExistingBaseIcons = Options.ONLY_USE_EXISTING_BASE_ICONS & self._options
    152         renderMetagroupOverlays = not Options.DONT_RENDER_METAGROUP_OVERLAYS & self._options
    153         renderers, errors = self._factory.CreateRenderers(inputType, input, tmpOutputFolder, desiredStyles, desiredMetagroups, desiredSizes, onlyUseExistingBaseIcons, renderMetagroupOverlays, metadata, self._language)
    154         if self._preRenderCallback:
    155             outputInfos = []
    156             for r in renderers:
    157                 outputInfos.extend(r.GetOutputList())
    158 
    159             self._preRenderCallback(input, outputInfos)
    160         return _JobInfo(options, outputFolder, tmpOutputFolder, renderers, errors)
    161 
    162 
    163 def CompareIcons(iconA, iconB, tolerance = DEFAULT_ICON_DIFF_TOLERANCE):
    164     diffResult = imgdiff.ImgDiff(iconA, iconB, normal=False, alpha=False)
    165     error = diffResult['Color']['MeanAbsoluteError']
    166     return error <= tolerance
    167 
    168 
    169 def _ReviewOptions(options):
    170     if options & Options.CHECKOUT:
    171         options = options | Options.USE_TMP_FOLDER
    172     return options
    173 
    174 
    175 def _GetCorrectFolders(outputFolder, options):
    176     if options & Options.USE_TMP_FOLDER:
    177         tmpOutputFolder = os.path.join(tempfile.mkdtemp('Icons'))
    178         if outputFolder is None:
    179             outputFolder = tmpOutputFolder
    180     else:
    181         if outputFolder is None:
    182             print()
    183             return (None, None, ["No output folder or temp folder defined, can't generate icons."])
    184         tmpOutputFolder = outputFolder
    185     return (outputFolder, tmpOutputFolder)
    186 
    187 
    188 def _ProcessCopyErrors(failed):
    189     errors = []
    190     for src, dst, excstring in failed:
    191         if os.path.exists(dst):
    192             errors.append('Failed to copy new icon to target directory:\n    icon: {}\n    target: {}\n{}'.format(src, dst, excstring))
    193         else:
    194             errors.append('Failed to overwrite existing icon with new generated version:\n    new icon: {}\n    old icon: {}\n{}'.format(src, dst, excstring))
    195 
    196     return errors
    197 
    198 
    199 def _CreateGenerationFailedErrors(copyOps, outputFiles):
    200     if len(copyOps) != len(outputFiles):
    201         return [ "Failed to finalize icon, icon wasn't generated: {}".format(x) for x in outputFiles if not os.path.exists(x) ]
    202     else:
    203         return []
    204 
    205 
    206 def _CopyToFinalFolder(outputFolder, outputFiles, changelist):
    207     resolvedOutputFolder = blue.paths.ResolvePath(outputFolder)
    208     copyOps = [ (os.path.abspath(x), os.path.abspath(os.path.join(resolvedOutputFolder, os.path.basename(x)))) for x in outputFiles if os.path.exists(x) ]
    209     errors = _CreateGenerationFailedErrors(copyOps, outputFiles)
    210 
    211     def copyFiles():
    212         failed = []
    213         for src, dst in copyOps:
    214             if dst != src and os.path.exists(src):
    215                 try:
    216                     if not os.path.exists(dst):
    217                         open(dst, 'a').close()
    218                     shutil.copy(src, dst)
    219                 except (IOError, OSError):
    220                     import traceback
    221                     failed += (src, dst, ''.join(traceback.format_exc()))
    222 
    223         return failed
    224 
    225     if changelist is not None and perforceutils is not None:
    226         with perforceutils.checkout([ dst for _, dst in copyOps ], changelist):
    227             failed = copyFiles()
    228     else:
    229         failed = copyFiles()
    230     errors.extend(_ProcessCopyErrors(failed))
    231     return errors
    232 
    233 
    234 def _CleanTmpFolder(tmpOutputFolder, options):
    235     if options & Options.CLEAN_TMP_FOLDER and options & Options.USE_TMP_FOLDER:
    236         osutils.SafeRmTree(tmpOutputFolder)