diff --git a/CHANGELOG.md b/CHANGELOG.md index c1b44b8d3..6e9708740 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. ### Added - "Starting Flash content debugger" in status bar when debugging starts - Simple editor - edit parameters of items inside buttons -- Simple editor - add/remove frames in buttons +- Simple editor - add/remove frames in buttons, button timeline header ### Fixed - Resize export dialogs labels to match localized strings diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 58566c2b3..dd76bc7e3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -746,7 +746,7 @@ public class Timeline { for (int d = 0; d <= maxDepth; d++) { for (int f = frames.size() - 1; f >= 0; f--) { if (frames.get(f).layers.get(d) != null) { - depthMaxFrame.put(d, f); + depthMaxFrame.put(d, f); break; } } @@ -766,6 +766,8 @@ public class Timeline { } } } + } else { + depthMaxFrameButtons.putAll(depthMaxFrame); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONRECORD.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONRECORD.java index 01512dcee..5cb92c3b4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONRECORD.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONRECORD.java @@ -161,7 +161,7 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh public BUTTONRECORD(BUTTONRECORD source) { this.buttonHasBlendMode = source.buttonHasBlendMode; this.buttonHasFilterList = source.buttonHasFilterList; - this.buttonStateHitTest =source. buttonStateHitTest; + this.buttonStateHitTest = source.buttonStateHitTest; this.buttonStateDown = source.buttonStateDown; this.buttonStateOver = source.buttonStateOver; this.buttonStateUp = source.buttonStateUp; @@ -175,8 +175,6 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh this.tag = source.tag; this.modified = source.modified; } - - /** * Constructor. @@ -349,5 +347,5 @@ public class BUTTONRECORD implements Serializable, TreeItem, HasSwfAndTag, HasCh return false; } return true; - } + } } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java index 1a110aae0..64b7d7f85 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineBodyPanel.java @@ -31,7 +31,6 @@ import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.types.BUTTONRECORD; -import com.jpexs.helpers.Helper; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; @@ -52,7 +51,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -76,7 +74,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe private Timeline timeline; - public static final int FRAME_WIDTH = 8; + private static final int FRAME_WIDTH = 8; + + private static final int BUTTON_FRAME_WIDTH = 40; + + private static final int MARKER_SIZE = 4; public static final int FRAME_HEIGHT = 18; @@ -223,6 +225,20 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe }); } + public static int getFrameWidthForTimeline(Timeline timeline) { + if (timeline == null) { + return FRAME_WIDTH; + } + if (timeline.timelined instanceof ButtonTag) { + return BUTTON_FRAME_WIDTH; + } + return FRAME_WIDTH; + } + + public int getFrameWidth() { + return getFrameWidthForTimeline(timeline); + } + @Override protected void paintComponent(Graphics g1) { @@ -232,18 +248,20 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setColor(getBackgroundColor()); - g.fillRect(0, 0, getWidth(), getHeight()); - + g.fillRect(0, 0, getWidth(), getHeight()); + if (timeline == null) { return; } - + + int frameWidth = getFrameWidth(); + Set emptyFrames = new LinkedHashSet<>(); if (timeline.timelined instanceof ButtonTag) { emptyFrames = ((ButtonTag) timeline.timelined).getEmptyFrames(); + frameWidth = BUTTON_FRAME_WIDTH; } Rectangle clip = g.getClipBounds(); - int frameWidth = FRAME_WIDTH; int frameHeight = FRAME_HEIGHT; int start_f = clip.x / frameWidth; int start_d = clip.y / frameHeight; @@ -285,14 +303,14 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe Frame fr = timeline.getFrame(f); if (fr != null && !fr.actions.isEmpty()) { if (firstAction) { - drawBlock(g, getEmptyFrameColor(), 0, 0, f, BlockType.EMPTY); + drawBlock(g, getEmptyFrameColor(), 0, 0, f, BlockType.EMPTY, frameWidth); } int f2 = f + 1; while (f2 <= max_f && timeline.getFrame(f2) != null && timeline.getFrame(f2).actions.isEmpty()) { f2++; } - drawBlock(g, getEmptyFrameColor(), 0, f, f2 - f, BlockType.EMPTY); + drawBlock(g, getEmptyFrameColor(), 0, f, f2 - f, BlockType.EMPTY, frameWidth); g.setColor(A_COLOR); g.setFont(getFont().deriveFont(FONT_SIZE)); g.drawString("a", f * frameWidth + frameWidth / 2 - awidth / 2, frameHeight / 2); @@ -322,11 +340,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe for (int f = start_f2; f <= end_f2; f++) { DepthState fl = timeline.getFrame(f).layers.get(d); - + if (emptyFrames.contains(f)) { fl = null; } - + boolean motionTween = fl == null ? false : fl.motionTween; DepthState flNext = f < max_f ? timeline.getFrame(f + 1).layers.get(d) : null; @@ -372,7 +390,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe blockType = shapeTween ? BlockType.SHAPE_TWEEN : motionTween ? BlockType.MOTION_TWEEN : BlockType.NORMAL; } - drawBlock(g, backColor, d, draw_f, num_frames, blockType); + drawBlock(g, backColor, d, draw_f, num_frames, blockType, frameWidth); } } @@ -383,8 +401,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } } - private void drawBlock(Graphics2D g, Color backColor, int depth, int frame, int num_frames, BlockType blockType) { - int frameWidth = FRAME_WIDTH; + private void drawBlock(Graphics2D g, Color backColor, int depth, int frame, int num_frames, BlockType blockType, int frameWidth) { int frameHeight = FRAME_HEIGHT; for (int n = frame; n < frame + num_frames; n++) { @@ -429,9 +446,9 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe g.setColor(KEY_COLOR); } if (blockType == BlockType.EMPTY) { - g.drawOval(frame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2); + g.drawOval(frame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight * 3 / 4 - MARKER_SIZE / 2, MARKER_SIZE, MARKER_SIZE); } else { - g.fillOval(frame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2); + g.fillOval(frame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight * 3 / 4 - MARKER_SIZE / 2, MARKER_SIZE, MARKER_SIZE); } if (num_frames > 1) { @@ -442,7 +459,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe g.setColor(KEY_COLOR); } if (isTween) { - g.fillOval(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2); + g.fillOval(endFrame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight * 3 / 4 - MARKER_SIZE / 2, MARKER_SIZE, MARKER_SIZE); } else { if (cursor != null && cursor.contains(new Point(endFrame, depth))) { @@ -450,13 +467,13 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } else { g.setColor(STOP_COLOR); } - g.fillRect(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight / 2 - 2, frameWidth / 2, frameHeight / 2); + g.fillRect(endFrame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight / 2 - 2, MARKER_SIZE, frameHeight / 2); if (cursor != null && cursor.contains(new Point(endFrame, depth))) { g.setBackground(getSelectedColorText()); } else { g.setColor(STOP_BORDER_COLOR); } - g.drawRect(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight / 2 - 2, frameWidth / 2, frameHeight / 2); + g.drawRect(endFrame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight / 2 - 2, MARKER_SIZE, frameHeight / 2); } for (int n = frame + 1; n < frame + num_frames; n++) { @@ -580,7 +597,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe return; } Point p = e.getPoint(); - p.x = p.x / FRAME_WIDTH; + p.x = p.x / getFrameWidth(); p.y = p.y / FRAME_HEIGHT; /*if (p.x >= timeline.getFrameCount()) { p.x = timeline.getFrameCount() - 1; @@ -624,7 +641,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe DepthState ds = fr == null ? null : fr.layers.get(depth); Set emptyFrames = new LinkedHashSet<>(); if (timeline.timelined instanceof ButtonTag) { - emptyFrames = ((ButtonTag) timeline.timelined).getEmptyFrames(); + emptyFrames = ((ButtonTag) timeline.timelined).getEmptyFrames(); } boolean thisEmpty = emptyFrames.contains(frame) || ds == null || ds.getCharacter() == null; boolean previousEmpty = true; @@ -724,7 +741,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe if (rec != null) { rec.setFrame(nf, false); } - for(int i = nf + 1; i < button.getFrameCount(); i++) { + for (int i = nf + 1; i < button.getFrameCount(); i++) { rec = button.getButtonRecordAt(i, nd, false); if (rec != null) { rec.setFrame(i - 1, true); @@ -817,7 +834,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } } } - + timelined.resetTimeline(); Point firstCursor = orderedCursor.iterator().next(); @@ -874,22 +891,22 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe continue; } ds.key = true; - + if (timelined instanceof ButtonTag) { ButtonTag button = (ButtonTag) timelined; BUTTONRECORD rec = button.getButtonRecordAt(nf, nd, false); if (rec != null) { BUTTONRECORD rec2 = new BUTTONRECORD(rec.getSwf(), rec.getTag()); rec2.fromPlaceObject(rec.toPlaceObject()); - - for(int i = nf; i < button.getFrameCount(); i++) { + + for (int i = nf; i < button.getFrameCount(); i++) { rec2.setFrame(i, rec.hasFrame(i)); rec.setFrame(i, false); } button.getRecords().add(rec2); } continue; - } + } PlaceObjectTypeTag place; try { place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag(); @@ -977,14 +994,14 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } } if (timelined instanceof ButtonTag) { - ButtonTag button = (ButtonTag) timelined; + ButtonTag button = (ButtonTag) timelined; BUTTONRECORD rec = button.getButtonRecordAt(nonEmptyFrame, fdepth, false); BUTTONRECORD rec2 = new BUTTONRECORD(rec.getSwf(), rec.getTag()); rec2.fromPlaceObject(rec.toPlaceObject()); rec2.setFrame(fframe, true); button.getRecords().add(rec2); - - } else { + + } else { PlaceObjectTypeTag place; try { place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag(); @@ -1042,13 +1059,12 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe int nf = p.x; int nd = p.y; - if (nf >= timelined.getFrameCount()) { - if (timelined instanceof ButtonTag) { + if (timelined instanceof ButtonTag) { continue; - } - + } + ReadOnlyTagList tags = timelined.getTags(); for (int i = tags.size() - 1; i >= 0; i--) { Tag t = tags.get(i); @@ -1094,7 +1110,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe boolean somethingAfter = false; Set emptyFrames = new LinkedHashSet<>(); - if (timelined instanceof ButtonTag) { + if (timelined instanceof ButtonTag) { emptyFrames = ((ButtonTag) timelined).getEmptyFrames(); } for (int f = nf + 1; f < timeline.getFrameCount(); f++) { @@ -1112,8 +1128,8 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe if (!empty || somethingAfter) { int moveFrameCount = somethingAfter ? 1 : nf - f; for (int mf = 0; mf < moveFrameCount; mf++) { - - if (timelined instanceof ButtonTag) { + + if (timelined instanceof ButtonTag) { ButtonTag button = (ButtonTag) timelined; if (!somethingAfter) { BUTTONRECORD rec = button.getButtonRecordAt(f, nd, true); @@ -1129,7 +1145,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } button.packRecords(); continue; - } + } int pos = timelined.indexOfTag(timeline.getFrame(f).showFrameTag); ReadOnlyTagList tags = timelined.getTags(); @@ -1293,9 +1309,9 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe public Rectangle getFrameBounds(int frame, int depth) { Rectangle rect = new Rectangle(); - rect.width = FRAME_WIDTH; + rect.width = getFrameWidth(); rect.height = FRAME_HEIGHT; - rect.x = frame * FRAME_WIDTH; + rect.x = frame * getFrameWidth(); rect.y = depth * FRAME_HEIGHT; return rect; } @@ -1303,7 +1319,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe public void refresh() { int frameCount = timeline == null ? 0 : timeline.getFrameCount(); int maxDepth = timeline == null ? 0 : timeline.getMaxDepth(); - Dimension dim = new Dimension(FRAME_WIDTH * frameCount + 1, FRAME_HEIGHT * (maxDepth + 1 /*actions*/ + 1 /*one additional*/)); + Dimension dim = new Dimension(getFrameWidth() * frameCount + 1, FRAME_HEIGHT * (maxDepth + 1 /*actions*/ + 1 /*one additional*/)); setSize(dim); setPreferredSize(dim); } diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java b/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java index a0c3f2506..7681c0d77 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelineTimePanel.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.easygui; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; import com.jpexs.decompiler.flash.timeline.Timeline; import java.awt.Color; import java.awt.Dimension; @@ -46,6 +47,8 @@ public class TimelineTimePanel extends JPanel implements MouseListener { private int selectedFrame = -1; private int leftPos = 0; + + private Timeline timeline; private final List listeners = new ArrayList<>(); @@ -62,6 +65,7 @@ public class TimelineTimePanel extends JPanel implements MouseListener { setFont(getFont().deriveFont(TimelineDepthPanel.FONT_SIZE)); int maxDepthW = getFontMetrics(getFont()).stringWidth(maxDepthStr); leftPos = maxDepthW + 2 * TimelineDepthPanel.PADDING; + this.timeline = timeline; } @@ -89,29 +93,53 @@ public class TimelineTimePanel extends JPanel implements MouseListener { @Override protected void paintComponent(Graphics g) { Rectangle clip = g.getClipBounds(); - int start_f = (scrollOffset + clip.x) / TimelineBodyPanel.FRAME_WIDTH; - int end_f = (scrollOffset + clip.x + clip.width) / TimelineBodyPanel.FRAME_WIDTH; + int frameWidth = TimelineBodyPanel.getFrameWidthForTimeline(timeline); + int start_f = (scrollOffset + clip.x) / frameWidth; + int end_f = (scrollOffset + clip.x + clip.width) / frameWidth; g.setColor(TimelineBodyPanel.getBackgroundColor()); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(borderColor); - int xofs = leftPos - TimelineBodyPanel.FRAME_WIDTH - scrollOffset % TimelineBodyPanel.FRAME_WIDTH; + int xofs = -scrollOffset % frameWidth; for (int f = 0; f <= end_f; f++) { - g.drawLine(xofs + f * TimelineBodyPanel.FRAME_WIDTH + 1, TimelineBodyPanel.FRAME_HEIGHT - 1, xofs + f * TimelineBodyPanel.FRAME_WIDTH + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength); + g.drawLine(xofs + f * frameWidth + 1, TimelineBodyPanel.FRAME_HEIGHT - 1, xofs + f * frameWidth+ 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength); } g.setFont(g.getFont().deriveFont(fontSize)); for (int f = 0; f <= end_f; f++) { int cur_f = start_f + f; if (selectedFrame == cur_f) { g.setColor(TimelineBodyPanel.SELECTED_COLOR); - g.fillRect(xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + 1, 0, TimelineBodyPanel.FRAME_WIDTH, TimelineBodyPanel.FRAME_HEIGHT - 1); + g.fillRect(xofs + f * frameWidth + 1, 0, frameWidth, TimelineBodyPanel.FRAME_HEIGHT - 1); g.setColor(TimelineBodyPanel.SELECTED_BORDER_COLOR); - g.drawRect(xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + 1, 0, TimelineBodyPanel.FRAME_WIDTH, TimelineBodyPanel.FRAME_HEIGHT - 1); + g.drawRect(xofs + f * frameWidth + 1, 0, frameWidth, TimelineBodyPanel.FRAME_HEIGHT - 1); } g.setColor(fontColor); - if ((cur_f + 1) % 5 == 0 || cur_f == 0) { - String timeStr = Integer.toString(cur_f + 1); - int w = g.getFontMetrics().stringWidth(timeStr); - g.drawString(timeStr, xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + TimelineBodyPanel.FRAME_WIDTH / 2 - w / 2 + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength - lineTextSpace); + + if (timeline != null && timeline.timelined instanceof ButtonTag) { + if (f < 5) { + String timeStr = ""; + switch (f) { + case ButtonTag.FRAME_UP: + timeStr = "Up"; + break; + case ButtonTag.FRAME_OVER: + timeStr = "Over"; + break; + case ButtonTag.FRAME_DOWN: + timeStr = "Down"; + break; + case ButtonTag.FRAME_HITTEST: + timeStr = "Hit"; + break; + } + int w = g.getFontMetrics().stringWidth(timeStr); + g.drawString(timeStr, xofs + f * frameWidth + frameWidth / 2 - w / 2 + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength - lineTextSpace); + } + } else { + if ((cur_f + 1) % 5 == 0 || cur_f == 0) { + String timeStr = Integer.toString(cur_f + 1); + int w = g.getFontMetrics().stringWidth(timeStr); + g.drawString(timeStr, xofs + f * frameWidth + frameWidth / 2 - w / 2 + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength - lineTextSpace); + } } } } @@ -122,7 +150,7 @@ public class TimelineTimePanel extends JPanel implements MouseListener { @Override public void mousePressed(MouseEvent e) { - int frame = (scrollOffset + e.getX()) / TimelineBodyPanel.FRAME_WIDTH; + int frame = (scrollOffset + e.getX()) / TimelineBodyPanel.getFrameWidthForTimeline(timeline); frameSelect(frame); for (FrameSelectionListener l : listeners) { l.frameSelected(frame, new ArrayList<>()); diff --git a/src/com/jpexs/decompiler/flash/easygui/TimelinedTagListDoableOperation.java b/src/com/jpexs/decompiler/flash/easygui/TimelinedTagListDoableOperation.java index b2234b846..b872e5bc6 100644 --- a/src/com/jpexs/decompiler/flash/easygui/TimelinedTagListDoableOperation.java +++ b/src/com/jpexs/decompiler/flash/easygui/TimelinedTagListDoableOperation.java @@ -43,14 +43,14 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation this.swfPanel = swfPanel; this.timelined = timelined; this.fframe = swfPanel.getFrame(); - this.fdepths = swfPanel.getDepths(); + this.fdepths = swfPanel.getDepths(); } @Override public void doOperation() { swfPanel.setTimelined(timelined); swfPanel.setFrame(fframe, fdepths); - + saveTagList(); wasModified = timelined.isModified(); timelined.setModified(true); @@ -59,7 +59,7 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation protected void saveTagList() { if (timelined instanceof ButtonTag) { List recordsCopy = new ArrayList<>(); - for (BUTTONRECORD rec :((ButtonTag) timelined).getRecords()) { + for (BUTTONRECORD rec : ((ButtonTag) timelined).getRecords()) { recordsCopy.add(new BUTTONRECORD(rec)); } buttonRecords = recordsCopy; @@ -87,7 +87,7 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation timelined.addTag(tags.get(i)); } timelined.resetTimeline(); - timelined.setFrameCount(timelined.getTimeline().getFrameCount()); + timelined.setFrameCount(timelined.getTimeline().getFrameCount()); } }