Ejemplo n.º 1
0
    def str_to_xsheet(self, ani_data):
        """
        Update FrameList from animation data.
    
        """

        data = json.loads(ani_data)

        # first check if it's in the legacy non-descriptive JSON or new XDNA format
        if type(data) is dict and data['XDNA']:
            print 'Loading using new file format'
            x = self.xdna

            # right now we only have one animatable layer, hence the first item
            raster_frames = data['xsheet']['raster_frame_lists'][0]

            self.frames = FrameList(len(raster_frames), self.opacities)
            self.framerate = data['xsheet']['framerate']
            self.cleared = True

            for i, d in enumerate(raster_frames):
                if d['idx'] is not None:
                    #cel = self.doc.layers[d['idx']] - this line exceptions with some files if the first frame is a key
                    
                    # cels appear to be numbered from 
                    # 0 in memory, but from 1 on disk...
                    cel = self.doc.layers[d['idx']-1] 
                else:
                    cel = None
                self.frames[i].is_key = d['is_key']
                self.frames[i].description = d['description']
                self.frames[i].cel = cel
                logger.info("loading cel #" + str(i))

        else:
            # load in legacy style
            print 'Loading using old format'
            self.using_legacy = True
            self.frames = FrameList(len(data), self.opacities)
            self.cleared = True
            for i, d in enumerate(data):
                is_key, description, layer_idx = d
                if layer_idx is not None:
                    cel = self.doc.layers[layer_idx]
                else:
                    cel = None
                self.frames[i].is_key = is_key
                self.frames[i].description = description
                self.frames[i].cel = cel
Ejemplo n.º 2
0
    def str_to_xsheet(self, ani_data):
        """
        Update FrameList from animation data.
    
        """

        data = json.loads(ani_data)

        # first check if it's in the legacy non-descriptive JSON or new XDNA format
        if type(data) is dict and data['XDNA']:
            print 'Loading using new file format'
            x = self.xdna

            # right now we only have one animatable layer, hence the first item
            raster_frames = data['xsheet']['raster_frame_lists'][0]

            self.frames = FrameList(len(raster_frames), self.opacities)
            self.framerate = data['xsheet']['framerate']
            self.cleared = True

            for i, d in enumerate(raster_frames):
                if d['idx'] is not None:
                    cel = self.doc.layers[d['idx']]
                else:
                    cel = None
                self.frames[i].is_key = d['is_key']
                self.frames[i].description = d['description']
                self.frames[i].cel = cel

        else:
            # load in legacy style
            print 'Loading using old format'
            self.using_legacy = True
            self.frames = FrameList(len(data), self.opacities)
            self.cleared = True
            for i, d in enumerate(data):
                is_key, description, layer_idx = d
                if layer_idx is not None:
                    cel = self.doc.layers[layer_idx]
                else:
                    cel = None
                self.frames[i].is_key = is_key
                self.frames[i].description = description
                self.frames[i].cel = cel
Ejemplo n.º 3
0
 def clear_xsheet(self, init=False):
     self.frames = FrameList(24, self.opacities)
     self.cleared = True
Ejemplo n.º 4
0
class Animation(object):
    
    opacities = {
    'cel':   0.5,
    'key':        0.4,
    'inbetweens': 0.2,
    'other keys': 0.3,
    'other':      0,
    }
    
    def __init__(self, doc):
        self.doc = doc
        self.frames = None
        self.framerate = 24.0
        self.cleared = False
        self.using_legacy = False
        self.xdna = XDNA()

        # For reproduction, "play", "pause", "stop":
        self.player_state = None

        # For cut/copy/paste operations:
        self.edit_operation = None
        self.edit_frame = None

    def clear_xsheet(self, init=False):
        self.frames = FrameList(24, self.opacities)
        self.cleared = True
    
    def legacy_xsheet_as_str(self):
        """
        Return animation X-Sheet as data in json format.

        """
        data = []
        for f in self.frames:
            if f.cel is not None:
                layer_idx = self.doc.layers.index(f.cel)
            else:
                layer_idx = None
            data.append((f.is_key, f.description, layer_idx))
        str_data = json.dumps(data, sort_keys=True, indent=4)
        return str_data

    def xsheet_as_str(self):
        """
        Return animation X-Sheet as data in XDNA format.

        """
        x = self.xdna

        data = {
            'metadata': x.application_signature,
            'XDNA': x.xdna_signature,
            'xsheet': {
                'framerate': self.framerate,
                'raster_frame_lists': [[]]
            }
        }

        for f in self.frames:
            if f.cel is not None:
                layer_idx = self.doc.layers.index(f.cel)
            else:
                layer_idx = None
            data['xsheet']['raster_frame_lists'][0].append({
                'idx': layer_idx,
                'is_key': f.is_key,
                'description': f.description
            })

        str_data = json.dumps(data, sort_keys=True, indent=4)
        return str_data

    def _write_xsheet(self, xsheetfile):
        """
        Save FrameList to file.
        
        """
        str_data = self.xsheet_as_str()
        xsheetfile.write(str_data)

    def str_to_xsheet(self, ani_data):
        """
        Update FrameList from animation data.
    
        """

        data = json.loads(ani_data)

        # first check if it's in the legacy non-descriptive JSON or new XDNA format
        if type(data) is dict and data['XDNA']:
            print 'Loading using new file format'
            x = self.xdna

            # right now we only have one animatable layer, hence the first item
            raster_frames = data['xsheet']['raster_frame_lists'][0]

            self.frames = FrameList(len(raster_frames), self.opacities)
            self.framerate = data['xsheet']['framerate']
            self.cleared = True

            for i, d in enumerate(raster_frames):
                if d['idx'] is not None:
                    cel = self.doc.layers[d['idx']]
                else:
                    cel = None
                self.frames[i].is_key = d['is_key']
                self.frames[i].description = d['description']
                self.frames[i].cel = cel

        else:
            # load in legacy style
            print 'Loading using old format'
            self.using_legacy = True
            self.frames = FrameList(len(data), self.opacities)
            self.cleared = True
            for i, d in enumerate(data):
                is_key, description, layer_idx = d
                if layer_idx is not None:
                    cel = self.doc.layers[layer_idx]
                else:
                    cel = None
                self.frames[i].is_key = is_key
                self.frames[i].description = description
                self.frames[i].cel = cel

    def _read_xsheet(self, xsheetfile):
        """
        Update FrameList from file.
    
        """
        ani_data = xsheetfile.read()
        self.str_to_xsheet(ani_data)
    
    def save_xsheet(self, filename):
        root, ext = os.path.splitext(filename)
        xsheet_fn = root + '.xsheet'
        xsheetfile = open(xsheet_fn, 'w')
        self._write_xsheet(xsheetfile)
    
    def load_xsheet(self, filename):
        root, ext = os.path.splitext(filename)
        xsheet_fn = root + '.xsheet'
        try:
            xsheetfile = open(xsheet_fn, 'r')
        except IOError:
            self.clear_xsheet()
        else:
            self._read_xsheet(xsheetfile)
    
    def save_png(self, filename, **kwargs):
        prefix, ext = os.path.splitext(filename)
        # if we have a number already, strip it
        l = prefix.rsplit('-', 1)
        if l[-1].isdigit():
            prefix = l[0]
        doc_bbox = self.doc.get_effective_bbox()
        for i in range(len(self.frames)):
            filename = '%s-%03d%s' % (prefix, i+1, ext)
            cel = self.frames.cel_at(i)
            cel._surface.save(filename, *doc_bbox, **kwargs)

    def save_avi(self, filename, vid_width=800, vid_fps=24, **kwargs):
        """
        Save video file with codec mpeg4.

        Requires command tools imagemagick and ffmpeg .

        """
        tempdir = tempfile.mkdtemp()
        jpgs_tempdir = os.path.join(tempdir, 'jpgs')
        os.mkdir(jpgs_tempdir)
        base_filename = os.path.basename(filename)
        prefix, ext = os.path.splitext(base_filename)
        out_filename = os.path.join(os.path.dirname(filename), prefix + '.avi')

        pngs_filename = os.path.join(tempdir, 'tempani.png')
        self.save_png(pngs_filename)

        # convert pngs to jpegs with imagemagick command:
        pngs_list = glob.glob(tempdir + os.path.sep + '*png')
        pngs_list.sort()
        for png_file in pngs_list:
            f_basename = os.path.basename(png_file)
            name, ext = os.path.splitext(f_basename)
            jpg_file = os.path.join(jpgs_tempdir, name + '.jpg')
            print "converting %s to %s..." % (png_file, jpg_file)
            call(["convert",
                  "-resize", str(vid_width),
                  "-quality", "100",
                  "-background", "white",
                  "-flatten",
                  png_file, jpg_file])

        # convert the previous jpgs to video with ffmpeg command:
        jpgs = jpgs_tempdir + os.path.sep + 'tempani-%03d.jpg'
        call(["ffmpeg",
              "-r", str(vid_fps),
              "-b", "1800",
              "-y", "-i",
              jpgs, out_filename])

    def _notify_canvas_observers(self, affected_layer):
        bbox = affected_layer._surface.get_bbox()
        for f in self.doc.canvas_observers:
            f(*bbox)

    def hide_all_frames(self):
        cels = []
        for cel in self.frames.get_all_cels():
            cel.visible = False
            self._notify_canvas_observers(cel)

    def change_visible_frame(self, prev_idx, cur_idx):
        prev_cel = self.frames.cel_at(prev_idx)
        cur_cel = self.frames.cel_at(cur_idx)
        if prev_cel == cur_cel:
            return
        if prev_cel != None:
            prev_cel.visible = False
            self._notify_canvas_observers(prev_cel)
        if cur_cel == None:
            return
        cur_cel.opacity = 1
        cur_cel.visible = True
        self._notify_canvas_observers(cur_cel)

    def update_opacities(self):
        opacities, visible = self.frames.get_opacities()

        for cel, opa in opacities.items():
            cel.opacity = opa
            self._notify_canvas_observers(cel)

        for cel, vis in visible.items():
            cel.visible = vis
            self._notify_canvas_observers(cel)

    def select_without_undo(self, idx):
        """Like the command but without undo/redo."""
        self.frames.select(idx)
        self.update_opacities()

    def play_animation(self):
        self.player_state = "play"
        self.doc.call_doc_observers()

    def pause_animation(self):
        self.player_state = "pause"

    def playpause_animation(self):
        if self.player_state != "play":
            self.player_state = "play"
        else:
            self.player_state = "pause"
        self.doc.call_doc_observers()

    def stop_animation(self):
        self.player_state = "stop"

    def player_next(self, use_lightbox=False):
        prev_idx = self.frames.idx
        if self.frames.has_next():
            self.frames.goto_next()
        else:
            self.frames.select(0)
        if use_lightbox:
            self.update_opacities()
        else:
            self.change_visible_frame(prev_idx, self.frames.idx)

    def toggle_key(self):
        frame = self.frames.get_selected()
        self.doc.do(anicommand.ToggleKey(self.doc, frame))

    def toggle_skip_visible(self):
        frame = self.frames.get_selected()
        self.doc.do(anicommand.ToggleSkipVisible(self.doc, frame))

    def previous_frame(self, with_cel=False):
        self.frames.goto_previous(with_cel)
        self.update_opacities()
        self.doc.call_doc_observers()

    def next_frame(self, with_cel=False):
        self.frames.goto_next(with_cel)
        self.update_opacities()
        self.doc.call_doc_observers()

    def previous_keyframe(self):
        self.frames.goto_previous_key()
        self.update_opacities()
        self.doc.call_doc_observers()

    def next_keyframe(self):
        self.frames.goto_next_key()
        self.update_opacities()
        self.doc.call_doc_observers()
    
    def change_description(self, new_description):
        frame = self.frames.get_selected()
        self.doc.do(anicommand.ChangeDescription(self.doc, frame, new_description))
    
    def add_cel(self):
        frame = self.frames.get_selected()
        if frame.cel is not None:
            return
        self.doc.do(anicommand.AddCel(self.doc, frame))

    def remove_cel(self):
        frame = self.frames.get_selected()
        if frame.cel is None:
            return
        self.doc.do(anicommand.RemoveCel(self.doc, frame))

    def select_frame(self, idx):
        self.doc.do(anicommand.SelectFrame(self.doc, idx))

    def change_opacityfactor(self, opacityfactor):
        self.frames.set_opacityfactor(opacityfactor)
        self.update_opacities()

    def toggle_opacity(self, attr, is_active):
        self.frames.setup_active_cels({attr: is_active})
        self.update_opacities()
    
    def toggle_nextprev(self, nextprev, is_active):
        self.frames.setup_nextprev({nextprev: is_active})
        self.update_opacities()
    
    def insert_frames(self, ammount=1):
        self.doc.do(anicommand.InsertFrames(self.doc, ammount))

    def remove_frames(self, ammount=1):
        self.doc.do(anicommand.RemoveFrames(self.doc, ammount))

    def can_cutcopy(self):
        frame = self.frames.get_selected()
        return frame.cel is not None

    def cutcopy_cel(self, edit_operation):
        frame = self.frames.get_selected()
        self.doc.ani.edit_operation = edit_operation
        self.doc.ani.edit_frame = frame
        self.doc.call_doc_observers()

    def can_paste(self):
        frame = self.frames.get_selected()
        return self.edit_frame is not None and \
            self.edit_frame != frame and \
            frame.cel == None

    def paste_cel(self):
        frame = self.frames.get_selected()
        self.doc.do(anicommand.PasteCel(self.doc, frame))
Ejemplo n.º 5
0
 def clear_xsheet(self, init=False):
     self.frames = FrameList(24, self.opacities)
     self.cleared = True
Ejemplo n.º 6
0
class Animation(object):
    
    opacities = {
    'cel':   0.5,
    'key':        0.4,
    'inbetweens': 0.2,
    'other keys': 0.3,
    'other':      0,
    }
    
    def __init__(self, doc):
        self.doc = doc
        self.frames = None
        self.framerate = 24.0
        self.cleared = False
        self.using_legacy = False
        self.xdna = XDNA()

        # For reproduction, "play", "pause", "stop":
        self.player_state = None

        # For cut/copy/paste operations:
        self.edit_operation = None
        self.edit_frame = None

    def clear_xsheet(self, init=False):
        self.frames = FrameList(24, self.opacities)
        self.cleared = True
    
    def legacy_xsheet_as_str(self):
        """
        Return animation X-Sheet as data in json format.

        """
        data = []
        for f in self.frames:
            if f.cel is not None:
                layer_idx = self.doc.layers.index(f.cel)
            else:
                layer_idx = None
            data.append((f.is_key, f.description, layer_idx))
        str_data = json.dumps(data, sort_keys=True, indent=4)
        return str_data

    def xsheet_as_str(self):
        """
        Return animation X-Sheet as data in XDNA format.

        """
        x = self.xdna

        data = {
            'metadata': x.application_signature,
            'XDNA': x.xdna_signature,
            'xsheet': {
                'framerate': self.framerate,
                'raster_frame_lists': [[]]
            }
        }

        for f in self.frames:
            if f.cel is not None:
                layer_idx = self.doc.layers.index(f.cel)
            else:
                layer_idx = None
            data['xsheet']['raster_frame_lists'][0].append({
                'idx': layer_idx,
                'is_key': f.is_key,
                'description': f.description
            })

        str_data = json.dumps(data, sort_keys=True, indent=4)
        return str_data

    def _write_xsheet(self, xsheetfile):
        """
        Save FrameList to file.
        
        """
        str_data = self.xsheet_as_str()
        xsheetfile.write(str_data)

    def str_to_xsheet(self, ani_data):
        """
        Update FrameList from animation data.
    
        """

        data = json.loads(ani_data)

        # first check if it's in the legacy non-descriptive JSON or new XDNA format
        if type(data) is dict and data['XDNA']:
            print 'Loading using new file format'
            x = self.xdna

            # right now we only have one animatable layer, hence the first item
            raster_frames = data['xsheet']['raster_frame_lists'][0]

            self.frames = FrameList(len(raster_frames), self.opacities)
            self.framerate = data['xsheet']['framerate']
            self.cleared = True

            for i, d in enumerate(raster_frames):
                if d['idx'] is not None:
                    #cel = self.doc.layers[d['idx']] - this line exceptions with some files if the first frame is a key
                    
                    # cels appear to be numbered from 
                    # 0 in memory, but from 1 on disk...
                    cel = self.doc.layers[d['idx']-1] 
                else:
                    cel = None
                self.frames[i].is_key = d['is_key']
                self.frames[i].description = d['description']
                self.frames[i].cel = cel
                logger.info("loading cel #" + str(i))

        else:
            # load in legacy style
            print 'Loading using old format'
            self.using_legacy = True
            self.frames = FrameList(len(data), self.opacities)
            self.cleared = True
            for i, d in enumerate(data):
                is_key, description, layer_idx = d
                if layer_idx is not None:
                    cel = self.doc.layers[layer_idx]
                else:
                    cel = None
                self.frames[i].is_key = is_key
                self.frames[i].description = description
                self.frames[i].cel = cel

    def _read_xsheet(self, xsheetfile):
        """
        Update FrameList from file.
    
        """
        ani_data = xsheetfile.read()
        self.str_to_xsheet(ani_data)
    
    def save_xsheet(self, filename):
        root, ext = os.path.splitext(filename)
        xsheet_fn = root + '.xsheet'
        xsheetfile = open(xsheet_fn, 'w')
        self._write_xsheet(xsheetfile)
    
    def load_xsheet(self, filename):
        root, ext = os.path.splitext(filename)
        xsheet_fn = root + '.xsheet'
        try:
            xsheetfile = open(xsheet_fn, 'r')
        except IOError:
            self.clear_xsheet()
        else:
            self._read_xsheet(xsheetfile)
    
    def save_png(self, filename, **kwargs):
        prefix, ext = os.path.splitext(filename)
        # if we have a number already, strip it
        l = prefix.rsplit('-', 1)
        if l[-1].isdigit():
            prefix = l[0]
        doc_bbox = self.doc.get_effective_bbox()
        for i in range(len(self.frames)):
            filename = '%s-%03d%s' % (prefix, i+1, ext)
            cel = self.frames.cel_at(i)
            cel.save_as_png(filename, *doc_bbox, **kwargs)

    def save_avi(self, filename, vid_width=800, vid_fps=24, **kwargs):
        """
        Save video file with codec mpeg4.

        Requires command tools imagemagick and ffmpeg .

        """
        tempdir = tempfile.mkdtemp()
        jpgs_tempdir = os.path.join(tempdir, 'jpgs')
        os.mkdir(jpgs_tempdir)
        base_filename = os.path.basename(filename)
        prefix, ext = os.path.splitext(base_filename)
        out_filename = os.path.join(os.path.dirname(filename), prefix + '.avi')

        pngs_filename = os.path.join(tempdir, 'tempani.png')
        self.save_png(pngs_filename)

        # convert pngs to jpegs with imagemagick command:
        pngs_list = glob.glob(tempdir + os.path.sep + '*png')
        pngs_list.sort()
        for png_file in pngs_list:
            f_basename = os.path.basename(png_file)
            name, ext = os.path.splitext(f_basename)
            jpg_file = os.path.join(jpgs_tempdir, name + '.jpg')
            print "converting %s to %s..." % (png_file, jpg_file)
            call(["convert",
                  "-resize", str(vid_width),
                  "-quality", "100",
                  "-background", "white",
                  "-flatten",
                  png_file, jpg_file])

        # convert the previous jpgs to video with ffmpeg command:
        jpgs = jpgs_tempdir + os.path.sep + 'tempani-%03d.jpg'
        call(["ffmpeg",
              "-r", str(vid_fps),
              "-b", "1800",
              "-y", "-i",
              jpgs, out_filename])

    def _notify_canvas_observers(self, affected_layer):
        bbox = affected_layer._surface.get_bbox()
        for f in self.doc.canvas_observers:
            f(*bbox)

    def hide_all_frames(self):
        cels = []
        for cel in self.frames.get_all_cels():
            cel.visible = False
            self._notify_canvas_observers(cel)

    def change_visible_frame(self, prev_idx, cur_idx):
        prev_cel = self.frames.cel_at(prev_idx)
        cur_cel = self.frames.cel_at(cur_idx)
        if prev_cel == cur_cel:
            return
        if prev_cel != None:
            prev_cel.visible = False
            self._notify_canvas_observers(prev_cel)
        if cur_cel == None:
            return
        cur_cel.opacity = 1
        cur_cel.visible = True
        self._notify_canvas_observers(cur_cel)

    def update_opacities(self):
        opacities, visible = self.frames.get_opacities()

        for cel, opa in opacities.items():
            if cel is None:
                continue
            cel.opacity = opa
            self._notify_canvas_observers(cel)

        for cel, vis in visible.items():
            if cel is None:
                continue
            cel.visible = vis
            self._notify_canvas_observers(cel)

    def select_without_undo(self, idx):
        """Like the command but without undo/redo."""
        self.frames.select(idx)
        self.update_opacities()

    def play_animation(self):
        self.player_state = "play"
        self.doc.call_doc_observers()

    def pause_animation(self):
        self.player_state = "pause"

    def playpause_animation(self):
        if self.player_state != "play":
            self.player_state = "play"
        else:
            self.player_state = "pause"
        self.doc.call_doc_observers()

    def stop_animation(self):
        self.player_state = "stop"

    def player_next(self, use_lightbox=False):
        prev_idx = self.frames.idx
        if self.frames.has_next():
            self.frames.goto_next()
        else:
            self.frames.select(0)
        if use_lightbox:
            self.update_opacities()
        else:
            self.change_visible_frame(prev_idx, self.frames.idx)

    def toggle_key(self):
        frame = self.frames.get_selected()
        self.doc.do(anicommand.ToggleKey(self.doc, frame))

    def toggle_skip_visible(self):
        frame = self.frames.get_selected()
        self.doc.do(anicommand.ToggleSkipVisible(self.doc, frame))

    def previous_frame(self, with_cel=False):
        self.frames.goto_previous(with_cel)
        self.update_opacities()
        self.doc.call_doc_observers()

    def next_frame(self, with_cel=False):
        self.frames.goto_next(with_cel)
        self.update_opacities()
        self.doc.call_doc_observers()

    def previous_keyframe(self):
        self.frames.goto_previous_key()
        self.update_opacities()
        self.doc.call_doc_observers()

    def next_keyframe(self):
        self.frames.goto_next_key()
        self.update_opacities()
        self.doc.call_doc_observers()
    
    def change_description(self, new_description):
        frame = self.frames.get_selected()
        self.doc.do(anicommand.ChangeDescription(self.doc, frame, new_description))
    
    def add_cel(self):
        frame = self.frames.get_selected()
        if frame.cel is not None:
            return
        self.doc.do(anicommand.AddCel(self.doc, frame))

    def remove_cel(self):
        frame = self.frames.get_selected()
        if frame.cel is None:
            return
        self.doc.do(anicommand.RemoveCel(self.doc, frame))

    def select_frame(self, idx):
        self.doc.do(anicommand.SelectFrame(self.doc, idx))

    def change_opacityfactor(self, opacityfactor):
        self.frames.set_opacityfactor(opacityfactor)
        self.update_opacities()

    def toggle_opacity(self, attr, is_active):
        self.frames.setup_active_cels({attr: is_active})
        self.update_opacities()
    
    def toggle_nextprev(self, nextprev, is_active):
        self.frames.setup_nextprev({nextprev: is_active})
        self.update_opacities()
    
    def insert_frames(self, ammount=1):
        self.doc.do(anicommand.InsertFrames(self.doc, ammount))

    def remove_frames(self, ammount=1):
        self.doc.do(anicommand.RemoveFrames(self.doc, ammount))

    def can_cutcopy(self):
        frame = self.frames.get_selected()
        return frame.cel is not None

    def cutcopy_cel(self, edit_operation):
        frame = self.frames.get_selected()
        self.doc.ani.edit_operation = edit_operation
        self.doc.ani.edit_frame = frame
        self.doc.call_doc_observers()

    def can_paste(self):
        frame = self.frames.get_selected()
        return self.edit_frame is not None and \
            self.edit_frame != frame and \
            frame.cel == None

    def paste_cel(self):
        frame = self.frames.get_selected()
        self.doc.do(anicommand.PasteCel(self.doc, frame))