/* * @(#)SubpictureFrame * * Copyright (c) 2005-2009 by dvb.matt, All Rights Reserved. * * This file is part of ProjectX, a free Java based demux utility. * By the authors, ProjectX is intended for educational purposes only, * as a non-commercial test project. * * * This program 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package net.sourceforge.dvb.projectx.gui; import java.awt.Font; import java.awt.Image; import java.awt.Color; import java.awt.Graphics; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList; import javax.swing.event.*; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JMenuBar; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.JSlider; import javax.swing.JLabel; import javax.swing.JCheckBoxMenuItem; import net.sourceforge.dvb.projectx.gui.CommonGui; import net.sourceforge.dvb.projectx.common.Resource; import net.sourceforge.dvb.projectx.common.Common; import net.sourceforge.dvb.projectx.xinput.XInputFile; import net.sourceforge.dvb.projectx.parser.CommonParsing; public class SubpictureFrame extends JFrame { String title = Resource.getString("subpicture.title"); String info = ""; private Picture picture; private JSlider slider; private ArrayList picture_indices = null; private int picture_index = 0; private byte[] picture_data = null; private int[] color_table = null; private int PreviewFlags = 0; private int horizontal_offset = 0; private int vertical_offset = 0; /** * */ public SubpictureFrame() { addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { close(); } }); buildMenu(); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(buildSizePanel(), BorderLayout.NORTH); panel.add(picture = new Picture(), BorderLayout.CENTER); panel.add(buildSliderPanel(), BorderLayout.SOUTH); getContentPane().add("Center", panel); setTitle(title); //setBounds(200, 0, 726, 726); setBounds(200, 0, 726, 750); setResizable(false); UIManager.addPropertyChangeListener(new UISwitchListener(getRootPane())); } /** * */ public void close() { resetPreview(); dispose(); } /** * */ protected void buildMenu() { JMenuBar menuBar = new JMenuBar(); menuBar.add(buildFileMenu()); setJMenuBar(menuBar); } /** * */ protected JMenu buildFileMenu() { JMenu fileMenu = new JMenu(); CommonGui.localize(fileMenu, "Common.File"); final JCheckBoxMenuItem background = new JCheckBoxMenuItem("use Preview Picture as Background"); background.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK)); background.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PreviewFlags = background.getState() ? PreviewFlags | 1 : PreviewFlags & ~1; getPictureData(slider.getValue()); } }); fileMenu.add(background); final JCheckBoxMenuItem letterbox = new JCheckBoxMenuItem("use Letterbox"); letterbox.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK)); letterbox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PreviewFlags = letterbox.getState() ? PreviewFlags | 2 : PreviewFlags & ~2; getPictureData(slider.getValue()); } }); fileMenu.add(letterbox); final JCheckBoxMenuItem areabox = new JCheckBoxMenuItem("don't show Multiple Areas Boundaries"); areabox.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK)); areabox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PreviewFlags = areabox.getState() ? PreviewFlags | 4 : PreviewFlags & ~4; getPictureData(slider.getValue()); } }); fileMenu.add(areabox); final JCheckBoxMenuItem applyarea = new JCheckBoxMenuItem("don't apply Multiple Areas Replacements"); applyarea.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.CTRL_MASK)); applyarea.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PreviewFlags = applyarea.getState() ? PreviewFlags | 8 : PreviewFlags & ~8; getPictureData(slider.getValue()); } }); fileMenu.add(applyarea); final JCheckBoxMenuItem allopaque = new JCheckBoxMenuItem("paint all as Opaque"); allopaque.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK)); allopaque.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PreviewFlags = allopaque.getState() ? PreviewFlags | 0x10 : PreviewFlags & ~0x10; getPictureData(slider.getValue()); } }); fileMenu.add(allopaque); fileMenu.addSeparator(); JMenuItem close = new JMenuItem(); CommonGui.localize(close, "Common.Close"); close.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.ALT_MASK)); close.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { close(); } }); fileMenu.add(close); return fileMenu; } /** * */ protected JPanel buildSizePanel() { JPanel panel = new JPanel(); //panel.setLayout(new BoxLayout()); final JSlider slider_h = new JSlider(); slider_h.setMajorTickSpacing(32); slider_h.setSnapToTicks(true); slider_h.setMaximum(1920); slider_h.setValue(0); slider_h.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { horizontal_offset = slider_h.getValue(); repaintSubpicture(); } }); panel.add(new JLabel("Preview X Offset: ")); panel.add(slider_h); final JSlider slider_v = new JSlider(); slider_v.setMajorTickSpacing(16); slider_v.setSnapToTicks(true); slider_v.setMaximum(1088); slider_v.setValue(0); slider_v.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { vertical_offset = slider_v.getValue(); repaintSubpicture(); } }); panel.add(new JLabel("Preview Y Offset: ")); panel.add(slider_v); return panel; } /** * */ protected JPanel buildSliderPanel() { JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); slider = new JSlider(); slider.setMajorTickSpacing(1); slider.setPaintTicks(true); slider.setSnapToTicks(true); slider.setMaximum(1); slider.setValue(0); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { getPictureData(slider.getValue()); } }); panel.add(slider); return panel; } /** * */ public void setFrameTitle(String newtitle) { setTitle(title + " " + newtitle); info = newtitle; } /** * */ public void loadPreview(XInputFile xif) { scanIFO(xif.toString()); if (scanData(xif)) show(); } /** * */ public void resetPreview() { picture_indices = null; picture_index = 0; picture_data = null; color_table = null; info = ""; slider.setMaximum(1); slider.setValue(0); } /** * read colors from ifo (pjx auto generated) */ private void scanIFO(String ifoname) { try { String nifoname = ifoname + ".IFO"; File f = new File(nifoname); if (!f.exists()) { f = new File(ifoname.substring(0, ifoname.lastIndexOf(".")) + ".IFO"); if (!f.exists()) { color_table = null; return; } } XInputFile xif = new XInputFile(f); byte[] data = new byte[64]; color_table = new int[16]; xif.randomAccessSingleRead(data, 0x10B4); //read 16x 4bytes from pos 0x10B4 for (int i = 0, j = color_table.length; i < j; i++) color_table[i] = YUVtoRGB(CommonParsing.getIntValue(data, i * 4, 4, !CommonParsing.BYTEREORDERING)); } catch (Exception e) { color_table = null; Common.setExceptionMessage(e); } } /** * convert colors from ifo (pjx auto generated) */ private int YUVtoRGB(int values) { int Y = 0xFF & values>>16; int Cr = 0xFF & values>>8; int Cb = 0xFF & values; if (Y == 0) return 0; int R = (int)((float)Y +1.402f * (Cr-128)); int G = (int)((float)Y -0.34414 * (Cb-128) -0.71414 * (Cr-128)); int B = (int)((float)Y +1.722 * (Cb-128)); R = R < 0 ? 0 : (R > 0xFF ? 0xFF : R); G = G < 0 ? 0 : (G > 0xFF ? 0xFF : G); B = B < 0 ? 0 : (B > 0xFF ? 0xFF : B); int T = 0xFF; return (T<<24 | R<<16 | G<<8 | B); } /** * */ private boolean scanData(XInputFile xif) { boolean b = false; picture_indices = new ArrayList(); try { if (!xif.exists()) return b; picture_data = new byte[(int) xif.length()]; info = xif.toString(); xif.randomAccessSingleRead(picture_data, 0); // read all long pts = 0; for (int i = 0; i < picture_data.length; i++) { if (picture_data[i] != 0x53 || picture_data[i + 1] != 0x50) // header continue; pts = CommonParsing.readPTS(picture_data, i + 2, 8, CommonParsing.BYTEREORDERING, false); picture_indices.add(new Long[] { new Long(i), new Long(CommonParsing.readPTS(picture_data, i + 2, 8, CommonParsing.BYTEREORDERING, false) / 90), null } ); i += 8; } slider.setMaximum(picture_indices.size() - 1); } catch (Exception e) { slider.setMaximum(1); info = "file read error!"; //Common.setExceptionMessage(e); return b; } slider.setValue(0); return !b; } /** * */ private void getPictureData(int index) { picture_index = index; if (picture_index < 0 || picture_indices == null || picture_index >= picture_indices.size()) return; Long[] values = (Long[]) picture_indices.get(picture_index); int pos = values[0].intValue(); //start pos int length = picture_data.length - pos; if (picture_index + 1 < picture_indices.size()) { Long[] nvalues = (Long[]) picture_indices.get(picture_index + 1); int npos = nvalues[0].intValue(); //start pos length = npos - pos; } Common.getSubpictureClass().setColorTable(color_table); byte[] array = new byte[length]; System.arraycopy(picture_data, pos, array, 0, length); PreviewFlags &= 0xFF; //remove subpic preview resolution PreviewFlags |= (0xFFF & picture.getWidth()) << 20; //add horizontal resolution PreviewFlags |= (0xFFF & picture.getHeight()) << 8; //add vertical resolution int duration = Common.getSubpictureClass().decode_picture(array, 10, true, new String[2], (PreviewFlags & 1) == 1 ? CommonGui.getPicturePanel().getPreviewImage() : null, PreviewFlags); values[2] = new Long(duration / 90); repaintSubpicture(); } /** * */ public void repaintSubpicture() { picture.repaint(); } /** * */ public class Picture extends JPanel { private Font font; private int w = 720; private int h = 576; /** * */ public Picture() { font = new Font("Tahoma", Font.PLAIN, 14); setBackground(Color.gray); setPreferredSize(new Dimension(w, 704)); setMinimumSize(new Dimension(w, 704)); setMaximumSize(new Dimension(w, 704)); } /** * */ public void paint(Graphics g) { paintPicture(g); paintInfoBackground(g); paintInfoField(g); paintPreviewInfo(g); } /** * */ private void paintPicture(Graphics g) { Image image = Common.getSubpictureClass().getImage(); // link to image, original size if (image != null) g.drawImage(image, -horizontal_offset, -vertical_offset, this); //move to fit into preview // g.drawImage(image, 0, 0, this); // original } /** * */ private void paintInfoBackground(Graphics g) { g.setColor(Color.black); g.fillRect(0, h, w, 140); //divide g.setColor(Color.white); g.fillRect(0, h, w, 2); } /** * */ private void paintInfoField(Graphics g) { g.setColor(Color.white); g.setFont(font); g.drawString(info, 4, 608); } /** * */ private void paintPreviewInfo(Graphics g) { if (picture_indices == null) return; if (picture_indices.size() == 0) return; Long[] values = (Long[]) picture_indices.get(picture_index); String str1 = "Pos: " + values[0] + " / Picture: " + (picture_index + 1) + " of " + picture_indices.size() + " / " + Common.getSubpictureClass().isForced_Msg(1); g.setColor(Color.white); g.setFont(font); g.drawString(str1, 4, 626); String str2 = "PTS In " + Common.formatTime_1(values[1].longValue()) + " Duration " + Common.formatTime_1(values[2].longValue()) + " PTS Out " + Common.formatTime_1(values[1].longValue() + values[2].longValue()); if (picture_index + 1 < picture_indices.size()) { Long[] nvalues = (Long[]) picture_indices.get(picture_index + 1); long diff = nvalues[1].longValue() - (values[1].longValue() + values[2].longValue()); str2 += " Next In " + Common.formatTime_1(nvalues[1].longValue()); if (diff < 0) str2 += " Overlap " + Common.formatTime_1(Math.abs(diff)); else str2 += " Gap " + Common.formatTime_1(diff); } else str2 += " Next File End "; g.drawString(str2, 4, 644); paintColorIndex(g); } /** * */ private void paintColorIndex(Graphics g) { g.setFont(font); int[] colors = Common.getSubpictureClass().getColorTable(0); for (int i = 0, x = 4; i < 16; i++, x += 44) { g.setColor(new Color(colors[i])); g.fillRect(x + 20, 580, 12, 12); g.setColor(Color.white); g.drawString(Integer.toHexString(i).toUpperCase(), x, 592); g.drawRect(x + 20, 580, 12, 12); } } } }