/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
/*
* The org.opensourcephysics.media.core package defines the Open Source Physics
* media framework for working with video and other media.
*
* Copyright (c) 2014 Douglas Brown and Wolfgang Christian.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* For additional information and documentation on Open Source Physics,
* please see <http://www.opensourcephysics.org/>.
*/
package org.opensourcephysics.media.core;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Collection;
import java.util.HashMap;
import javax.swing.event.SwingPropertyChangeSupport;
import org.opensourcephysics.controls.OSPLog;
import org.opensourcephysics.display.DrawingPanel;
import org.opensourcephysics.display.Interactive;
/**
* This provides basic implementations of all Video methods. Subclasses should
* provide a raw image for display--see ImageVideo or GifVideo for an example.
*
* @author Douglas Brown
* @version 1.0
*/
public class VideoAdapter implements Video {
// instance fields
protected Image rawImage; // raw image from video source
protected Dimension size; // image pixel dimensions
protected BufferedImage bufferedImage; // offscreen buffered image copy
protected BufferedImage filteredImage; // filtered image
protected int frameCount;
protected int frameNumber;
protected int startFrameNumber;
protected int endFrameNumber;
protected double rate = 1;
protected boolean playing = false;
protected boolean looping = false;
protected double minX, maxX, minY, maxY;
protected boolean mouseEnabled = false;
protected boolean visible = true;
protected boolean isMeasured = false;
protected boolean isValidMeasure = false;
protected boolean widthDominates = true;
protected boolean isValidImage = false;
protected boolean isValidFilteredImage = false;
protected ImageCoordSystem coords;
protected DoubleArray aspects;
protected PropertyChangeSupport support;
protected HashMap<String, Object> properties = new HashMap<String, Object>();
protected FilterStack filterStack = new FilterStack();
protected Raster clearRaster;
/**
* Protected constructor creates an empty VideoAdapter
*/
protected VideoAdapter() {
initialize();
}
/**
* Draws the video image on the panel.
*
* @param panel the drawing panel requesting the drawing
* @param g the graphics context on which to draw
*/
public void draw(DrawingPanel panel, Graphics g) {
if(!visible) {
return;
}
Graphics2D g2 = (Graphics2D) g;
if(((panel instanceof VideoPanel)&&((VideoPanel) panel).isDrawingInImageSpace())||isMeasured) {
AffineTransform gat = g2.getTransform(); // save graphics transform
g2.transform(panel.getPixelTransform()); // world to screen
if(panel instanceof VideoPanel) {
VideoPanel vidPanel = (VideoPanel) panel;
if(!vidPanel.isDrawingInImageSpace()) {
// use video panel's coords for vid to world transform
ImageCoordSystem coords = vidPanel.getCoords();
g2.transform(coords.getToWorldTransform(frameNumber));
}
}
else { // not a video panel, so draw in world space
// use this video's coords for vid to world transform
g2.transform(coords.getToWorldTransform(frameNumber));
}
// draw the video or filtered image
if(filterStack.isEmpty()||!filterStack.isEnabled()) {
g2.drawImage(rawImage, 0, 0, panel);
} else {
g2.drawImage(getImage(), 0, 0, panel);
}
g2.setTransform(gat); // restore transform
}
else { // center image in panel if not measured
double centerX = (panel.getXMax()+panel.getXMin())/2;
double centerY = (panel.getYMax()+panel.getYMin())/2;
int xoffset = panel.xToPix(centerX)-size.width/2;
int yoffset = panel.yToPix(centerY)-size.height/2;
// draw the video or filtered image
if(filterStack.isEmpty()||!filterStack.isEnabled()) {
g2.drawImage(rawImage, xoffset, yoffset, panel);
} else {
g2.drawImage(getImage(), xoffset, yoffset, panel);
}
}
}
/**
* Shows or hides the video.
*
* @param visible <code>true</code> to show the video
*/
public void setVisible(boolean visible) {
this.visible = visible;
firePropertyChange("videoVisible", null, new Boolean(visible)); //$NON-NLS-1$
}
/**
* Gets the visibility of the video.
*
* @return <code>true</code> if the video is visible
*/
public boolean isVisible() {
return visible;
}
/**
* Gets the minimum x needed to draw this object.
*
* @return minimum x
*/
public double getXMin() {
if(!isValidMeasure) {
findMinMaxValues();
}
return minX;
}
/**
* Gets the maximum x needed to draw this object.
*
* @return maximum x
*/
public double getXMax() {
if(!isValidMeasure) {
findMinMaxValues();
}
return maxX;
}
/**
* Gets the minimum y needed to draw this object.
*
* @return minimum y
*/
public double getYMin() {
if(!isValidMeasure) {
findMinMaxValues();
}
return minY;
}
/**
* Gets the maximum y needed to draw this object.
*
* @return maximum y
*/
public double getYMax() {
if(!isValidMeasure) {
findMinMaxValues();
}
return maxY;
}
/**
* Reports whether information is available to set min/max values.
*
* @return <code>true</code> if min/max values are valid
*/
public boolean isMeasured() {
return isMeasured;
}
/**
* Gets the current video image after applying enabled filters.
*
* @return the current video image with filters applied
*/
public BufferedImage getImage() {
if(!isValidImage) { // bufferedImage needs refreshing
isValidImage = true;
Graphics g = bufferedImage.createGraphics();
bufferedImage.setData(clearRaster);
g.drawImage(rawImage, 0, 0, null);
}
if(filterStack.isEmpty()||!filterStack.isEnabled()) {
return bufferedImage;
} else if(!isValidFilteredImage) { // filteredImage needs refreshing
isValidFilteredImage = true;
filteredImage = filterStack.getFilteredImage(bufferedImage);
}
return filteredImage;
}
/**
* Returns this video if enabled.
*
* @param panel the drawing panel
* @param xpix the x coordinate in pixels
* @param ypix the y coordinate in pixels
* @return this if enabled, otherwise null
*/
public Interactive findInteractive(DrawingPanel panel, int xpix, int ypix) {
if(!mouseEnabled) {
return null;
}
return this;
}
/**
* Sets whether this responds to mouse hits.
*
* @param enabled <code>true</code> if this responds to mouse hits.
*/
public void setEnabled(boolean enabled) {
mouseEnabled = enabled;
}
/**
* Gets whether this responds to mouse hits.
*
* @return <code>true</code> if this responds to mouse hits.
*/
public boolean isEnabled() {
return mouseEnabled;
}
/**
* Sets x position of upper left corner of the specified video frame
* in world units.
*
* @param n the video frame number
* @param x the world x position
*/
public void setFrameX(int n, double x) {
setFrameXY(n, x, coords.imageToWorldY(n, 0, 0));
}
/**
* Sets x position of upper left corner of all video frames
* in world units.
*
* @param x the world x position
*/
public void setX(double x) {
for(int n = 0; n<frameCount; n++) {
setFrameX(n, x);
}
}
/**
* Sets y position of upper left corner of the specified video frame
* in world units.
*
* @param n the video frame number
* @param y the world y position
*/
public void setFrameY(int n, double y) {
setFrameXY(n, coords.imageToWorldX(n, 0, 0), y);
}
/**
* Sets y position of upper left corner of all video frames
* in world units.
*
* @param y the world y position
*/
public void setY(double y) {
for(int n = 0; n<frameCount; n++) {
setFrameY(n, y);
}
}
/**
* Gets x position of upper left corner of the current video frame
* in world units.
*
* @return the world x position
*/
public double getX() {
return coords.imageToWorldX(frameNumber, 0, 0);
}
/**
* Gets y position of upper left corner of the current video frame
* in world units.
*
* @return the world y position
*/
public double getY() {
return coords.imageToWorldY(frameNumber, 0, 0);
}
/**
* Sets the x and y position of the UL corner of the specified video
* frame in world units.
*
* @param n the video frame number
* @param x the world x position
* @param y the world y position
*/
public void setFrameXY(int n, double x, double y) {
double sin = coords.getSine(n);
double cos = coords.getCosine(n);
double tx = coords.getScaleX(n)*(y*sin-x*cos);
double ty = coords.getScaleY(n)*(y*cos+x*sin);
coords.setOriginXY(n, tx, ty);
}
/**
* Sets the x and y position of the UL corner of all video frames
* in world units.
*
* @param x the world x position
* @param y the world y position
*/
public void setXY(double x, double y) {
for(int n = 0; n<frameCount; n++) {
setFrameXY(n, x, y);
}
}
/**
* Sets the relative aspect of the specified video frame. Relative
* aspect is the ratio of the world aspect to the pixel aspect of
* the image. The pixel aspect is the ratio of image width to height
* in pixels, and world aspect is the ratio of world width to height
* in world units. For example, a 320 x 240 pixel movie has a pixel
* aspect of 1.33. If relative aspect is 2, then the world aspect
* will be 2.67. So if the video's width is 16 wu, its height will
* be 6 wu. Or if its height is 10 wu, its width will be 26.67 wu.
*
* @param n the video frame number
* @param relativeAspect the desired relative aspect
*/
public void setFrameRelativeAspect(int n, double relativeAspect) {
if((relativeAspect<0.001)||(relativeAspect>1000)) {
return;
}
aspects.set(n, Math.abs(relativeAspect));
if(isMeasured) {
if(widthDominates) {
setFrameWidth(n, size.width/coords.getScaleX(n));
} else {
setFrameHeight(n, size.height/coords.getScaleY(n));
}
}
}
/**
* Sets the relative aspect of all video frames. Relative
* aspect is the ratio of the world aspect to the pixel aspect of
* the image. The pixel aspect is the ratio of image width to height
* in pixels, and world aspect is the ratio of world width to height
* in world units. For example, a 320 x 240 pixel movie has a pixel
* aspect of 1.33. If relative aspect is 2, then the world aspect
* will be 2.67. So if the video's width is 16 wu, its height will
* be 6 wu. Or if its height is 10 wu, its width will be 26.67 wu.
*
* @param relativeAspect the desired relative aspect
*/
public void setRelativeAspect(double relativeAspect) {
for(int n = 0; n<frameCount; n++) {
setFrameRelativeAspect(n, relativeAspect);
}
}
/**
* Gets the relative aspect of the current video frame.
*
* @return the relative aspect of the current image.
*/
public double getRelativeAspect() {
return aspects.get(frameNumber);
}
/**
* Sets the width of the specified video frame in world units. Also sets
* the height using the relative aspect.
*
* @param n the video frame number
* @param width the width in world units
* @see #setRelativeAspect
*/
public void setFrameWidth(int n, double width) {
if(width==0) {
return;
}
width = Math.abs(width);
// save x and y since setting width invalidates them
double x = coords.imageToWorldX(n, 0, 0);
double y = coords.imageToWorldY(n, 0, 0);
double scaleX = size.width/width;
coords.setScaleX(n, scaleX);
coords.setScaleY(n, scaleX*aspects.get(n));
widthDominates = true;
// restore x and y to their correct values
setFrameXY(n, x, y);
}
/**
* Sets the width of all video frames in world units. Also sets
* the heights using the relative aspect.
*
* @param width the width in world units
* @see #setRelativeAspect
*/
public void setWidth(double width) {
for(int n = 0; n<frameCount; n++) {
setFrameWidth(n, width);
}
}
/**
* Gets the current width of the video frame.
*
* @return the width of the video image
*/
public double getWidth() {
return size.width/coords.getScaleX(frameNumber);
}
/**
* Sets the height of the specified video frame in world units. Also sets
* the width using the relative aspect.
*
* @param n the video frame number
* @param height the height in world units
* @see #setRelativeAspect
*/
public void setFrameHeight(int n, double height) {
if(height==0) {
return;
}
height = Math.abs(height);
// save x and y since setting width invalidates them
double x = coords.imageToWorldX(n, 0, 0);
double y = coords.imageToWorldY(n, 0, 0);
double scaleY = size.height/height;
coords.setScaleY(n, scaleY);
coords.setScaleX(n, scaleY/aspects.get(n));
widthDominates = false;
// restore x and y to their correct values
setFrameXY(n, x, y);
}
/**
* Sets the height of all video frames in world units. Also sets
* the widths using the relative aspect.
*
* @param height the height in world units
* @see #setRelativeAspect
*/
public void setHeight(double height) {
for(int n = 0; n<frameCount; n++) {
setFrameHeight(n, height);
}
}
/**
* Gets the current height of the video frame.
*
* @return the height of the video image
*/
public double getHeight() {
return size.height/coords.getScaleY(frameNumber);
}
/**
* Sets the angle in radians of the specified video frame measured ccw
* from the world x-axis. This results in a rotation only.
*
* @param n the video frame number
* @param theta the angle in radians
*/
public void setFrameAngle(int n, double theta) {
// save x and y since setting angle invalidates them
double x = coords.imageToWorldX(n, 0, 0);
double y = coords.imageToWorldY(n, 0, 0);
double cos = Math.cos(theta);
double sin = Math.sin(theta);
coords.setCosineSine(n, cos, -sin);
setFrameXY(n, x, y); // restore x and y to their correct values
}
/**
* Sets the angle in radians of all video frames measured ccw
* from the world x-axis. This results in a rotation only.
*
* @param theta the angle in radians
*/
public void setAngle(double theta) {
for(int n = 0; n<frameCount; n++) {
setFrameAngle(n, theta);
}
}
/**
* Gets the angle in radians of the curent video frame measured ccw
* from the world x-axis.
*
* @return the angle in radians
*/
public double getAngle() {
return -coords.getAngle(frameNumber);
}
/**
* Steps the video forward one frame.
*/
public void step() {
stop();
setFrameNumber(frameNumber+1);
}
/**
* Steps the video back one frame.
*/
public void back() {
stop();
setFrameNumber(frameNumber-1);
}
/**
* Gets the total number of video frames.
*
* @return the number of video frames
*/
public int getFrameCount() {
return frameCount;
}
/**
* Gets the current video frame number.
*
* @return the current frame number
*/
public int getFrameNumber() {
return frameNumber;
}
/**
* Sets the video frame number.
*
* @param n the desired frame number
*/
public void setFrameNumber(int n) {
if(n==frameNumber) {
return;
}
n = Math.min(n, endFrameNumber);
n = Math.max(n, startFrameNumber);
firePropertyChange("nextframe", null, n); //$NON-NLS-1$
frameNumber = n;
}
/**
* Gets the start frame number.
*
* @return the start frame number
* @see #getEndFrameNumber
*/
public int getStartFrameNumber() {
return startFrameNumber;
}
/**
* Sets the start frame number.
*
* @param n the desired start frame number
* @see #setEndFrameNumber
*/
public void setStartFrameNumber(int n) {
if(n==startFrameNumber) {
return;
}
n = Math.max(0, n);
startFrameNumber = Math.min(endFrameNumber, n);
firePropertyChange("startframe", null, new Integer(startFrameNumber)); //$NON-NLS-1$
}
/**
* Gets the end frame number.
*
* @return the end frame number
* @see #getStartFrameNumber
*/
public int getEndFrameNumber() {
return endFrameNumber;
}
/**
* Sets the end frame number.
*
* @param n the desired end frame number,
* @see #setStartFrameNumber
*/
public void setEndFrameNumber(int n) {
if(n==endFrameNumber) {
return;
}
if(frameCount>1) {
n = Math.min(frameCount-1, n);
}
endFrameNumber = Math.max(startFrameNumber, n);
firePropertyChange("endframe", null, new Integer(endFrameNumber)); //$NON-NLS-1$
}
/**
* Gets the start time of the specified frame in milliseconds.
*
* @param n the frame number
* @return the start time of the frame in milliseconds, or -1 if not known
*/
public double getFrameTime(int n) {
return -1;
}
/**
* Gets the duration of the specified frame in milliseconds.
*
* @param n the frame number
* @return the duration of the frame in milliseconds
*/
public double getFrameDuration(int n) {
if(frameCount==1) {
return getDuration();
}
if(n==frameCount-1) {
return getDuration()-getFrameTime(n);
}
return getFrameTime(n+1)-getFrameTime(n);
}
/**
* Plays the video at the current rate.
*/
public void play() {
playing = true;
}
/**
* Stops the video.
*/
public void stop() {
playing = false;
}
/**
* Stops the video and resets it to the start time.
*/
public void reset() {
stop();
setFrameNumber(startFrameNumber);
}
/**
* Gets the current video time in milliseconds.
*
* @return the current time in milliseconds, or -1 if not known
*/
public double getTime() {
return -1;
}
/**
* Sets the video time in milliseconds.
*
* @param millis the desired time in milliseconds
*/
public void setTime(double millis) {
/** implemented by subclasses */
}
/**
* Gets the start time in milliseconds.
*
* @return the start time in milliseconds, or -1 if not known
*/
public double getStartTime() {
return -1;
}
/**
* Sets the start time in milliseconds. NOTE: the actual start time
* is normally set to the beginning of a frame.
*
* @param millis the desired start time in milliseconds
*/
public void setStartTime(double millis) {
/** implemented by subclasses */
}
/**
* Gets the end time in milliseconds.
*
* @return the end time in milliseconds, or -1 if not known
*/
public double getEndTime() {
return -1;
}
/**
* Sets the end time in milliseconds. NOTE: the actual end time
* is set to the end of a frame.
*
* @param millis the desired end time in milliseconds
*/
public void setEndTime(double millis) {
/** implemented by subclasses */
}
/**
* Gets the duration of the video.
*
* @return the duration of the video in milliseconds, or -1 if not known
*/
public double getDuration() {
return -1;
}
/**
* Sets the frame number to the start frame.
*/
public void goToStart() {
setFrameNumber(startFrameNumber);
}
/**
* Sets the frame number to the end frame.
*/
public void goToEnd() {
setFrameNumber(endFrameNumber);
}
/**
* Starts and stops the video.
*
* @param playing <code>true</code> starts the video, and
* <code>false</code> stops it
*/
public void setPlaying(boolean playing) {
if(playing) {
play();
} else {
stop();
}
}
/**
* Gets the playing state of this video.
*
* @return <code>true</code> if the video is playing
*/
public boolean isPlaying() {
return playing;
}
/**
* Sets the looping state of this video.
* If true, the video restarts when reaching the end.
*
* @param loops <code>true</code> if the video loops
*/
public void setLooping(boolean loops) {
if(looping==loops) {
return;
}
looping = loops;
firePropertyChange("looping", null, new Boolean(looping)); //$NON-NLS-1$
}
/**
* Gets the looping state of the video.
* If true, the video restarts when reaching the end.
*
* @return <code>true</code> if the video loops
*/
public boolean isLooping() {
return looping;
}
/**
* Sets the relative play rate. Relative play rate is the ratio
* of a video's play rate to its preferred ("normal") play rate.
*
* @param rate the relative play rate.
*/
public void setRate(double rate) {
rate = Math.abs(rate);
if((rate==this.rate)||(rate==0)) {
return;
}
this.rate = rate;
}
/**
* Gets the relative play rate. Relative play rate is the ratio
* of a video's play rate to its preferred ("normal") play rate.
*
* @return the relative play rate.
*/
public double getRate() {
return rate;
}
/**
* Sets the image coordinate system used to convert from
* imagespace to worldspace.
*
* @param coords the image coordinate system
*/
public void setCoords(ImageCoordSystem coords) {
if(coords==this.coords) {
return;
}
this.coords.removePropertyChangeListener(this);
coords.addPropertyChangeListener(this);
this.coords = coords;
isMeasured = true;
isValidMeasure = false;
firePropertyChange("coords", null, coords); //$NON-NLS-1$
}
/**
* Gets the image coordinate system.
*
* @return the image coordinate system
*/
public ImageCoordSystem getCoords() {
return coords;
}
/**
* Sets the filter stack.
*
* @param stack the new filter stack
*/
public void setFilterStack(FilterStack stack) {
filterStack.removePropertyChangeListener(this);
filterStack = stack;
filterStack.addPropertyChangeListener(this);
}
/**
* Gets the filter stack.
*
* @return the filter stack
*/
public FilterStack getFilterStack() {
return filterStack;
}
/**
* Sets a user property of the video.
*
* @param name the name of the property
* @param value the value of the property
*/
public void setProperty(String name, Object value) {
if(name.equals("measure")) { //$NON-NLS-1$
isValidMeasure = false;
} else {
properties.put(name, value);
}
}
/**
* Gets a user property of the video. May return null.
*
* @param name the name of the property
* @return the value of the property
*/
public Object getProperty(String name) {
return properties.get(name);
}
/**
* Gets a collection of user property names for the video.
*
* @return a collection of property names
*/
public Collection<String> getPropertyNames() {
return properties.keySet();
}
/**
* Adds a PropertyChangeListener to this video.
*
* @param listener the object requesting property change notification
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
/**
* Adds a PropertyChangeListener to this video.
*
* @param property the name of the property of interest to the listener
* @param listener the object requesting property change notification
*/
public void addPropertyChangeListener(String property, PropertyChangeListener listener) {
support.addPropertyChangeListener(property, listener);
}
/**
* Removes a PropertyChangeListener from this video.
*
* @param listener the listener requesting removal
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
/**
* Removes a PropertyChangeListener for a specified property.
*
* @param property the name of the property
* @param listener the listener to remove
*/
public void removePropertyChangeListener(String property, PropertyChangeListener listener) {
support.removePropertyChangeListener(property, listener);
}
/**
* Disposes of this video.
*/
public void dispose() {
if (coords!=null)
coords.removePropertyChangeListener(this);
getFilterStack().setInspectorsVisible(false);
}
/**
* Responds to property change events. VideoAdapter listens for the following
* events: "transform" from ImageCoordSystem and "image" from
* FilterStack.
*
* @param e the property change event
*/
public void propertyChange(PropertyChangeEvent e) {
if(e.getSource()==coords) { // "transform"
isMeasured = true;
isValidMeasure = false;
} else if(e.getSource()==filterStack) { // "image"
isValidFilteredImage = false;
support.firePropertyChange(e);
}
}
//____________________________ protected methods ____________________________
/**
* Sends a PropertyChangeEvent to registered listeners. No event is sent
* if oldVal and newVal are equal, unless they are both null.
*
* @param property the name of the property that has changed
* @param oldVal the value of the property before the change (may be null)
* @param newVal the value of the property after the change (may be null)
*/
protected void firePropertyChange(String property, Object oldVal, Object newVal) {
support.firePropertyChange(property, oldVal, newVal);
}
@Override
protected void finalize() {
OSPLog.finer(getClass().getSimpleName()+" resources released by garbage collector"); //$NON-NLS-1$
}
//_______________________________ protected methods _________________________
/**
* Initialize this video.
*/
protected void initialize() {
support = new SwingPropertyChangeSupport(this);
filterStack.addPropertyChangeListener(this);
}
/**
* Refreshes the BufferedImage based on current size.
* Creates a new image if needed.
*/
protected void refreshBufferedImage() {
if((bufferedImage==null)||(bufferedImage.getWidth()!=size.width)||(bufferedImage.getHeight()!=size.height)) {
bufferedImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
int clear = new Color(0, 0, 0, 0).getRGB();
int[] rgb = new int[size.width*size.height];
for(int i = 0; i<rgb.length; i++) {
rgb[i] = clear;
}
bufferedImage.setRGB(0, 0, size.width, size.height, rgb, 0, size.width);
clearRaster = bufferedImage.getData();
isValidImage = false;
}
}
/**
* Finds the min and max values of x and y.
*/
protected void findMinMaxValues() {
VideoClip clip = (VideoClip) getProperty("videoclip"); //$NON-NLS-1$
// check all four corner positions of every frame in the current clip
Point2D corner = new Point2D.Double(0, 0); // top left
int start = 0;
if(clip!=null) {
start = clip.getStartFrameNumber();
}
AffineTransform at = coords.getToWorldTransform(start);
at.transform(corner, corner);
maxX = minX = corner.getX();
maxY = minY = corner.getY();
int stepCount = frameCount;
if(clip!=null) {
stepCount = clip.getStepCount();
}
for(int n = 0; n<stepCount; n++) {
if(clip==null) {
at = coords.getToWorldTransform(n);
} else {
at = coords.getToWorldTransform(clip.stepToFrame(n));
}
for(int i = 0; i<4; i++) {
switch(i) {
case 0 :
corner.setLocation(0, 0);
break;
case 1 :
corner.setLocation(size.width, 0);
break;
case 2 :
corner.setLocation(0, size.height);
break;
case 3 :
corner.setLocation(size.width, size.height);
}
at.transform(corner, corner);
minX = Math.min(corner.getX(), minX);
maxX = Math.max(corner.getX(), maxX);
minY = Math.min(corner.getY(), minY);
maxY = Math.max(corner.getY(), maxY);
}
}
isValidMeasure = true;
}
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/