diff --git a/build/shared/tools/MovieMaker/src/processing/app/tools/MovieMaker.java b/build/shared/tools/MovieMaker/src/processing/app/tools/MovieMaker.java index a64a6d35d..23c10a51d 100644 --- a/build/shared/tools/MovieMaker/src/processing/app/tools/MovieMaker.java +++ b/build/shared/tools/MovieMaker/src/processing/app/tools/MovieMaker.java @@ -35,11 +35,11 @@ import ch.randelshofer.media.quicktime.QuickTimeWriter; * Hacked from Werner Randelshofer's QuickTimeWriter demo. The original version * can be found here. *

- * A more up-to-date version of the project seems to be + * A more up-to-date version of the project is * here. - * If someone would like to help us update the encoder, that'd be great. + * Problem is, it's too big, so we don't want to merge it into our code. *

- * Broken out as a separate project because the license (CC) probably isn't + * Broken out as a separate project because the license (CC) probably isn't * compatible with the rest of Processing and we don't want any confusion. *

* Added JAI ImageIO to support lots of other image file formats [131008]. @@ -52,7 +52,7 @@ import ch.randelshofer.media.quicktime.QuickTimeWriter; *

  • The dialog box is super ugly. It's a hacked up version of the previous * interface, but I'm too scared to pull that GUI layout code apart. *
  • The 'None' compressor seems to have bugs, so just disabled it instead. - *
  • The 'pass through' option seems to be broken, so it's been removed. + *
  • The 'pass through' option seems to be broken, so it's been removed. * In its place is an option to use the same width/height as the originals. *
  • When this new 'pass through' is set, there's some nastiness with how * the 'final' width/height variables are passed to the movie maker. @@ -228,10 +228,10 @@ public class MovieMaker extends JFrame implements Tool { chooseImageFolderButton.setText(Language.text("movie_maker.choose_button")); //chooseImageFolderButton.addActionListener(formListener); chooseImageFolderButton.addActionListener(new ActionListener() { - + @Override public void actionPerformed(ActionEvent e) { - Chooser.selectFolder(MovieMaker.this, + Chooser.selectFolder(MovieMaker.this, Language.text("movie_maker.select_image_folder"), new File(imageFolderField.getText()), new Chooser.Callback() { @@ -249,10 +249,10 @@ public class MovieMaker extends JFrame implements Tool { chooseSoundFileButton.setText(Language.text("movie_maker.choose_button")); //chooseSoundFileButton.addActionListener(formListener); chooseSoundFileButton.addActionListener(new ActionListener() { - + @Override public void actionPerformed(ActionEvent e) { - Chooser.selectInput(MovieMaker.this, + Chooser.selectInput(MovieMaker.this, Language.text("movie_maker.select_sound_file"), new File(soundFileField.getText()), new Chooser.Callback() { @@ -269,14 +269,14 @@ public class MovieMaker extends JFrame implements Tool { createMovieButton.setText(Language.text("movie_maker.create_movie_button")); // createMovieButton.addActionListener(formListener); createMovieButton.addActionListener(new ActionListener() { - + @Override public void actionPerformed(ActionEvent e) { String lastPath = prefs.get("movie.outputFile", null); File lastFile = lastPath == null ? null : new File(lastPath); - Chooser.selectOutput(MovieMaker.this, + Chooser.selectOutput(MovieMaker.this, Language.text("movie_maker.save_dialog_prompt"), - lastFile, + lastFile, new Chooser.Callback() { @Override void select(File file) { @@ -295,7 +295,7 @@ public class MovieMaker extends JFrame implements Tool { // public void run() { // createMovie(target); // } -// +// // }); } } @@ -616,16 +616,23 @@ public class MovieMaker extends JFrame implements Tool { Arrays.sort(imgFiles); } - // Check on first image, if we can actually do pass through + // Get the width and height if we're preserving size. + // Nullify bad Files so we don't get errors twice. if (originalSize) { - BufferedImage temp = readImage(imgFiles[0]); - if (temp == null) { - return new RuntimeException(Language.interpolate("movie_maker.error.cant_read", imgFiles[0].getAbsolutePath())); - } - width = temp.getWidth(); - height = temp.getHeight(); - if (width <= 0 || height <= 0) { - return new RuntimeException(Language.interpolate("movie_maker.error.cant_read_maybe_bad", imgFiles[0].getName())); + for (int i = 0; true; ++i) { + // No images at all? No video then. + if (i >= imgFiles.length) { + throw new RuntimeException(Language.text("movie_maker.error.no_images_found")); + } + + BufferedImage temp = readImage(imgFiles[i]); + if (temp == null) { + imgFiles[i] = null; + } else { + width = temp.getWidth(); + height = temp.getHeight(); + break; + } } } @@ -642,7 +649,7 @@ public class MovieMaker extends JFrame implements Tool { writeAudioOnly(movieFile, soundFile, streaming); } return null; - + } catch (Throwable t) { return t; } @@ -673,50 +680,89 @@ public class MovieMaker extends JFrame implements Tool { }//GEN-LAST:event_createMovie - /** + /** * Read an image from a file. ImageIcon doesn't don't do well with some file * types, so we use ImageIO. ImageIO doesn't handle TGA files created by * Processing, so this calls our own loadImageTGA(). + *
    Prints errors itself. + * @return null on error; image only if okay. */ - private BufferedImage readImage(File file) throws IOException { - // Make sure that we're using a ClassLoader that's aware of the ImageIO jar - //Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - //BufferedImage image = ImageIO.read(file); - // rewritten to switch back to the default loader - Thread current = Thread.currentThread(); - ClassLoader origLoader = Thread.currentThread().getContextClassLoader(); - current.setContextClassLoader(getClass().getClassLoader()); - BufferedImage image = ImageIO.read(file); - current.setContextClassLoader(origLoader); + private BufferedImage readImage(File file) { + try { + // Make sure that we're using a ClassLoader that's aware of the ImageIO jar + //Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + //BufferedImage image = ImageIO.read(file); + // rewritten to switch back to the default loader + Thread current = Thread.currentThread(); + ClassLoader origLoader = Thread.currentThread().getContextClassLoader(); + current.setContextClassLoader(getClass().getClassLoader()); - /* - String[] loadImageFormats = ImageIO.getReaderFormatNames(); - if (loadImageFormats != null) { - for (String format : loadImageFormats) { - System.out.println(format); + BufferedImage image; + try { + image = ImageIO.read(file); + } catch (IOException e) { + System.err.println(Language.interpolate("movie_maker.error.cant_read", + file.getAbsolutePath())); + return null; } - } - */ - if (image == null) { - String path = file.getAbsolutePath(); - String pathLower = path.toLowerCase(); - // Might be an incompatible TGA or TIFF created by Processing - if (pathLower.endsWith(".tga")) { - return loadImageTGA(file); - - } else if (pathLower.endsWith(".tif") || pathLower.endsWith(".tiff")) { - throw new IOException(Language.text("movie_maker.error.avoid_tiff")); + current.setContextClassLoader(origLoader); + + /* + String[] loadImageFormats = ImageIO.getReaderFormatNames(); + if (loadImageFormats != null) { + for (String format : loadImageFormats) { + System.out.println(format); + } } + */ + + if (image == null) { + String path = file.getAbsolutePath(); + String pathLower = path.toLowerCase(); + // Might be an incompatible TGA or TIFF created by Processing + if (pathLower.endsWith(".tga")) { + try { + return loadImageTGA(file); + } catch (IOException e) { + System.err.println(Language.interpolate("movie_maker.error.cant_read", + file.getAbsolutePath())); + return null; + } + + } else if (pathLower.endsWith(".tif") || pathLower.endsWith(".tiff")) { + System.err.println(Language.interpolate("movie_maker.error.cant_read", + file.getAbsolutePath()) + " " + + Language.text("movie_maker.error.avoid_tiff")); + return null; + + } else { + System.err.println(Language.interpolate("movie_maker.error.cant_read", + file.getAbsolutePath())); + return null; + } + + } else { + if (image.getWidth() <= 0 || image.getHeight() <= 0) { + System.err.println(Language.interpolate("movie_maker.error.cant_read_maybe_bad", + file.getAbsolutePath())); + return null; + } + } + return image; + + // Catch-all is sometimes needed. + } catch (RuntimeException e) { + System.err.println(Language.interpolate("movie_maker.error.cant_read", file.getAbsolutePath())); + return null; } - return image; } - + /** variable frame rate. */ private void writeVideoOnlyVFR(File movieFile, File[] imgFiles, int width, int height, double fps, QuickTimeWriter.VideoFormat videoFormat, /*boolean passThrough,*/ String streaming) throws IOException { File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp"); - ProgressMonitor p = new ProgressMonitor(MovieMaker.this, + ProgressMonitor p = new ProgressMonitor(MovieMaker.this, Language.interpolate("movie_maker.progress.creating_file_name", movieFile.getName()), Language.text("movie_maker.progress.creating_output_file"), 0, imgFiles.length); @@ -746,6 +792,8 @@ public class MovieMaker extends JFrame implements Tool { int prevImgDuration = 0; for (int i = 0; i < imgFiles.length && !p.isCanceled(); i++) { File f = imgFiles[i]; + if (f == null) continue; + p.setNote(Language.interpolate("movie_maker.progress.processing", f.getName())); p.setProgress(i); @@ -755,6 +803,8 @@ public class MovieMaker extends JFrame implements Tool { } else { //BufferedImage fImg = ImageIO.read(f); BufferedImage fImg = readImage(f); + if (fImg == null) continue; + g.drawImage(fImg, 0, 0, width, height, null); if (i != 0 && Arrays.equals(data, prevData)) { prevImgDuration += duration; @@ -871,6 +921,7 @@ public class MovieMaker extends JFrame implements Tool { private void writeVideoAndAudio(File movieFile, File[] imgFiles, File audioFile, int width, int height, double fps, QuickTimeWriter.VideoFormat videoFormat, /*boolean passThrough,*/ String streaming) throws IOException { + File tmpFile = streaming.equals("none") ? movieFile : new File(movieFile.getPath() + ".tmp"); ProgressMonitor p = new ProgressMonitor(MovieMaker.this, Language.interpolate("movie_maker.progress.creating_file_name", movieFile.getName()), @@ -949,24 +1000,25 @@ public class MovieMaker extends JFrame implements Tool { } // Advance video to movie time - while (imgIndex < imgFiles.length && qtOut.getTrackDuration(1) < movieTime) { + for (; imgIndex < imgFiles.length && qtOut.getTrackDuration(1) < movieTime; ++imgIndex) { // catch up with video time p.setProgress(imgIndex); - p.setNote(Language.interpolate("movie_maker.progress.processing", imgFiles[imgIndex].getName())); + File f = imgFiles[imgIndex]; + if (f == null) continue; + + p.setNote(Language.interpolate("movie_maker.progress.processing", f.getName())); //if (passThrough) { if (false) { - qtOut.writeSample(1, imgFiles[imgIndex], vsDuration); + qtOut.writeSample(1, f, vsDuration); } else { //BufferedImage fImg = ImageIO.read(imgFiles[imgIndex]); - BufferedImage fImg = readImage(imgFiles[imgIndex]); - if (fImg == null) { - continue; - } + BufferedImage fImg = readImage(f); + if (fImg == null) continue; + g.drawImage(fImg, 0, 0, width, height, null); fImg.flush(); qtOut.writeFrame(1, imgBuffer, vsDuration); } - ++imgIndex; } } if (streaming.equals("fastStart")) { @@ -999,14 +1051,14 @@ public class MovieMaker extends JFrame implements Tool { } } - + /** - * Targa image loader for RLE-compressed TGA files. - * Code taken from PApplet, any changes here should lead to updates there. + * Targa image loader for RLE-compressed TGA files. + * Code taken from PApplet, any changes here should lead to updates there. */ static private BufferedImage loadImageTGA(File file) throws IOException { InputStream is = new FileInputStream(file); - + try { byte header[] = new byte[18]; int offset = 0; @@ -1168,7 +1220,7 @@ public class MovieMaker extends JFrame implements Tool { break; case ARGB: for (int i = 0; i < num; i++) { - pixels[index++] = is.read() | + pixels[index++] = is.read() | (is.read() << 8) | (is.read() << 16) | (is.read() << 24); } break; @@ -1193,13 +1245,13 @@ public class MovieMaker extends JFrame implements Tool { WritableRaster wr = image.getRaster(); wr.setDataElements(0, 0, w, h, pixels); return image; - + } finally { is.close(); } } - - + + /** * @param args the command line arguments */ @@ -1214,7 +1266,7 @@ public class MovieMaker extends JFrame implements Tool { } }); } - + private JLabel aboutLabel; private JButton chooseImageFolderButton;