You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
codeine/src/app/captureFrame.cpp

297 lines
7.9 KiB

// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information
#include "../debug.h"
#include <tdefiledialog.h>
#include <kpreviewwidgetbase.h>
#include <kpushbutton.h>
#include <kstatusbar.h>
#include <kstdguiitem.h>
#include "mainWindow.h"
#include "../mxcl.library.h"
#include <tqdialog.h>
#include <tqhbox.h>
#include <tqlabel.h>
#include <tqimage.h>
#include <tqlayout.h>
#include <tqpainter.h>
#include <tqstringlist.h>
#include "theStream.h"
#include "xineEngine.h"
#include <xine.h>
namespace Codeine {
class FrameCapturePreview : public KPreviewWidgetBase
{
TQImage m_frame;
virtual void showPreview( const KURL& ) {}
virtual void clearPreview() {}
virtual void paintEvent( TQPaintEvent* )
{
TQPainter painter( this );
const uint h = int( double(m_frame.height()) / m_frame.width() * (width()-5) );
const uint y = (height() - h) / 2;
painter.drawImage( TQRect( 5, y, width(), h ), m_frame );
const TQString text = TQString("%1x%2").arg( m_frame.width() ).arg( m_frame.height() );
const uint x = (width() - fontMetrics().width( text ))/2;
painter.drawText( x, y + h + fontMetrics().height() + 5, text );
}
public:
FrameCapturePreview( const TQImage& frame, TQWidget *parent )
: KPreviewWidgetBase( parent )
, m_frame( frame )
{
setMinimumWidth( 200 );
}
};
class FrameCaptureDialog : public TQDialog
{
const TQImage m_frame;
const TQString m_time;
const TQString m_title;
void message( const TQString &text ) { ((MainWindow*)parentWidget())->statusBar()->message( text, 4000 ); }
public:
FrameCaptureDialog( const TQImage &frame, const TQString &time, MainWindow *parent )
: TQDialog( parent, 0, false /*modal*/, TQt::WDestructiveClose )
, m_frame( frame )
, m_time( time )
, m_title( TheStream::prettyTitle() )
{
(new TQVBoxLayout( this ))->setAutoAdd( true );
(new TQLabel( this ))->setPixmap( frame );
TQHBox *box = new TQHBox( this );
KPushButton *o = new KPushButton( KStdGuiItem::save(), box );
connect( o, SIGNAL(clicked()), SLOT(accept()) );
o = new KPushButton( KStdGuiItem::cancel(), box );
o->setText( i18n("Discard") );
connect( o, SIGNAL(clicked()), SLOT(reject()) );
setCaption( i18n("Capture - %1").arg( time ) );
setFixedSize( sizeHint() );
show();
//TODO don't activate
//TODO move to the parent's side - not centrally aligned
}
~FrameCaptureDialog()
{
delete [] m_frame.bits();
}
virtual void accept()
{
KFileDialog dialog( ":frame_capture", i18n("*.png|PNG Format\n*.jpeg|JPEG Format"), this, 0, false );
dialog.setOperationMode( KFileDialog::Saving );
dialog.setCaption( i18n("Save Frame") );
dialog.setSelection( m_title + " - " + m_time + ".png" );
dialog.setPreviewWidget( new FrameCapturePreview( m_frame, &dialog ) );
if( dialog.exec() == Accepted ) {
const TQString fileName = dialog.selectedFile();
if( fileName.isEmpty() )
return;
const TQString type = dialog.currentFilter().remove( 0, 2 ).upper();
if( m_frame.save( fileName, type.utf8() ) )
message( i18n("%1 saved successfully").arg( fileName ) );
else
message( i18n("Sorry, could not save %1").arg( fileName ) );
}
deleteLater();
}
};
void
MainWindow::captureFrame()
{
new FrameCaptureDialog( videoWindow()->captureFrame(), m_timeLabel->text(), this );
}
/************************************************************
* Helpers to convert yuy and yv12 frames to rgb *
* code from gxine modified for 32bit output *
* Copyright (C) 2000-2003 the xine project *
************************************************************/
static void
yuy2Toyv12( uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int w, int h )
{
const int w2 = w / 2;
for( int j, i = 0; i < h; i += 2 ) {
for( j = 0; j < w2; j++ )
{
// packed YUV 422 is: Y[i] U[i] Y[i+1] V[i]
*(y++) = *(input++);
*(u++) = *(input++);
*(y++) = *(input++);
*(v++) = *(input++);
}
// down sampling
for( j = 0; j < w2; j++ ) {
// skip every second line for U and V
*(y++) = *(input++);
input++;
*(y++) = *(input++);
input++;
}
}
}
static uchar*
yv12ToRgb( uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, const int w, const int h )
{
/// Create rgb data from yv12
#define clip_8_bit(val) \
{ \
if( val < 0 ) \
val = 0; \
else if( val > 255 ) \
val = 255; \
}
int y, u, v;
int r, g, b;
int sub_i_uv;
int sub_j_uv;
const int uv_width = w / 2;
const int uv_height = h / 2;
uchar * const rgb = new uchar[(w * h * 4)]; //qt needs a 32bit align
if( !rgb )
return 0;
for( int i = 0; i < h; ++i ) {
// calculate u & v rows
sub_i_uv = ((i * uv_height) / h);
for( int j = 0; j < w; ++j ) {
// calculate u & v columns
sub_j_uv = (j * uv_width) / w;
/***************************************************
*
* Colour conversion from
* http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30
*
* Thanks to Billy Biggs <vektor@dumbterm.net>
* for the pointer and the following conversion.
*
* R' = [ 1.1644 0 1.5960 ] ([ Y' ] [ 16 ])
* G' = [ 1.1644 -0.3918 -0.8130 ] * ([ Cb ] - [ 128 ])
* B' = [ 1.1644 2.0172 0 ] ([ Cr ] [ 128 ])
*
* Where in xine the above values are represented as
*
* Y' == image->y
* Cb == image->u
* Cr == image->v
*
***************************************************/
y = src_y[(i * w) + j] - 16;
u = src_u[(sub_i_uv * uv_width) + sub_j_uv] - 128;
v = src_v[(sub_i_uv * uv_width) + sub_j_uv] - 128;
r = (int)((1.1644 * (double)y) + (1.5960 * (double)v));
g = (int)((1.1644 * (double)y) - (0.3918 * (double)u) - (0.8130 * (double)v));
b = (int)((1.1644 * (double)y) + (2.0172 * (double)u));
clip_8_bit( r );
clip_8_bit( g );
clip_8_bit( b );
rgb[(i * w + j) * 4 + 0] = b;
rgb[(i * w + j) * 4 + 1] = g;
rgb[(i * w + j) * 4 + 2] = r;
rgb[(i * w + j) * 4 + 3] = 0;
}
}
return rgb;
}
/************************************************************/
TQImage
VideoWindow::captureFrame() const
{
DEBUG_BLOCK
int ratio, format, w, h;
if( !xine_get_current_frame( *engine(), &w, &h, &ratio, &format, NULL ) )
return TQImage();
uint8_t *yuv = new uint8_t[((w+8) * (h+1) * 2)];
if( yuv == 0 ) {
Debug::error() << "Not enough memory to make screenframe!\n";
return TQImage(); }
xine_get_current_frame( *engine(), &w, &h, &ratio, &format, yuv );
// convert to yv12 if necessary
uint8_t *y = 0, *u = 0, *v = 0;
switch( format )
{
case XINE_IMGFMT_YUY2: {
uint8_t *yuy2 = yuv;
yuv = new uint8_t[(w * h * 2)];
if( yuv == 0 ) {
Debug::error() << "Not enough memory to make screenframe!\n";
delete [] yuy2;
return TQImage(); }
y = yuv;
u = yuv + w * h;
v = yuv + w * h * 5 / 4;
yuy2Toyv12( y, u, v, yuy2, w, h );
delete [] yuy2;
} break;
case XINE_IMGFMT_YV12:
y = yuv;
u = yuv + w * h;
v = yuv + w * h * 5 / 4;
break;
default:
Debug::warning() << "Format " << format << " not supported!\n";
delete [] yuv;
return TQImage();
}
// convert to rgb
uchar *rgb = yv12ToRgb( y, u, v, w, h );
TQImage frame( rgb, w, h, 32, 0, 0, TQImage::IgnoreEndian );
delete [] yuv;
return frame;
}
}