2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * osd.cpp: Shows some text in a pretty way independent to the WM
8 * begin: Fre Sep 26 2003
9 * copyright: (C) 2004 Christian Muehlhaeuser <chris@chris.de>
10 * (C) 2004-2006 Seb Ruiz <me@sebruiz.net>
11 * (C) 2004, 2005 Max Howell
12 * (C) 2005 Gábor Lehel <illissius@gmail.com>
18 #include "amarokconfig.h"
19 #include "collectiondb.h" //for albumCover location
21 #include "enginecontroller.h"
22 #include "playlist.h" //if osdUsePlaylistColumns()
23 #include "playlistitem.h" //ditto
24 #include "podcastbundle.h"
26 #include "StarManager.h"
28 #include <KApplication>
29 #include <KPixmapEffect>
30 #include <KStandardDirs> //locate
33 #include <QDesktopWidget>
40 namespace ShadowEngine
42 QImage
makeShadow( const QPixmap
&textPixmap
, const QColor
&bgColor
);
46 #define MOODBAR_HEIGHT 20
49 OSDWidget::OSDWidget( QWidget
*parent
, const char *name
)
50 : QWidget( parent
, Qt::Window
| Qt::X11BypassWindowManagerHint
| Qt::WindowStaysOnTopHint
51 | Qt::FramelessWindowHint
| Qt::CustomizeWindowHint
)
53 , m_timer( new QTimer( this ) )
54 , m_alignment( Middle
)
57 , m_drawShadow( false )
61 setObjectName( name
);
62 setFocusPolicy( Qt::NoFocus
);
65 connect( m_timer
, SIGNAL(timeout()), SLOT(hide()) );
66 connect( CollectionDB::instance(), SIGNAL( ratingChanged( const QString
&, int ) ),
67 this, SLOT( ratingChanged( const QString
&, int ) ) );
69 //or crashes, KWindowSystem bug I think, crashes in QWidget::icon()
70 kapp
->setTopWidget( this );
74 OSDWidget::show( const QString
&text
, QImage newImage
)
76 if ( !newImage
.isNull() )
79 int w
= m_scaledCover
.width();
80 int h
= m_scaledCover
.height();
81 m_scaledCover
= m_cover
.scaled( w
, h
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
88 OSDWidget::ratingChanged( const QString
& path
, int rating
)
90 Meta::TrackPtr track
= EngineController::instance()->currentTrack();
93 if( track
->playableUrl().isLocalFile() && track
->playableUrl().path() == path
)
94 ratingChanged( rating
);
98 OSDWidget::ratingChanged( const short rating
)
100 m_text
= '\n' + i18n( "Rating changed" );
101 setRating( rating
); //Checks isEnabled() before doing anything
108 OSDWidget::volChanged( unsigned char volume
)
113 m_newvolume
= volume
;
114 m_text
= m_newvolume
? i18n("Volume: %1%", m_newvolume
) : i18n("Mute");
121 OSDWidget::show() //virtual
123 if ( !isEnabled() || m_text
.isEmpty() )
126 const uint M
= fontMetrics().width( 'x' );
128 const QRect oldGeometry
= QRect( pos(), size() );
129 const QRect newGeometry
= determineMetrics( M
);
131 if( newGeometry
.width() > 0 && newGeometry
.height() > 0 )
134 m_size
= newGeometry
.size();
135 //render( M, newGeometry.size() );
136 setGeometry( newGeometry
);
138 // bitBlt( this, 0, 0, &m_buffer );
140 if( m_duration
) //duration 0 -> stay forever
141 m_timer
->start( m_duration
, true ); //calls hide()
144 warning() << "Attempted to make an invalid sized OSD\n";
150 OSDWidget::determineMetrics( const uint M
)
152 // sometimes we only have a tiddly cover
153 const QSize minImageSize
= m_cover
.size().boundedTo( QSize(100,100) );
155 // determine a sensible maximum size, don't cover the whole desktop or cross the screen
156 const QSize
margin( (M
+ MARGIN
) * 2, (M
+ MARGIN
) * 2 ); //margins
157 const QSize image
= m_cover
.isNull() ? QSize( 0, 0 ) : minImageSize
;
158 const QSize max
= QApplication::desktop()->screen( m_screen
)->size() - margin
;
160 // If we don't do that, the boundingRect() might not be suitable for drawText() (Qt issue N67674)
161 m_text
.replace( QRegExp(" +\n"), "\n" );
162 // remove consecutive line breaks
163 m_text
.replace( QRegExp("\n+"), "\n" );
165 // The osd cannot be larger than the screen
166 QRect rect
= fontMetrics().boundingRect( 0, 0, max
.width() - image
.width(), max
.height(),
167 Qt::AlignCenter
| Qt::WordBreak
, m_text
);
171 static const QString tmp
= QString ("******").insert( 3,
172 ( i18n("Volume: 100%").length() >= i18n("Mute").length() )?
173 i18n("Volume: 100%") : i18n("Mute") );
175 QRect tmpRect
= fontMetrics().boundingRect( 0, 0,
176 max
.width() - image
.width(), max
.height() - fontMetrics().height(),
177 Qt::AlignCenter
| Qt::WordBreak
, tmp
);
178 tmpRect
.setHeight( tmpRect
.height() + fontMetrics().height() / 2 );
185 QPixmap
* star
= StarManager::instance()->getStar( 1 );
186 if( rect
.width() < star
->width() * 5 )
187 rect
.setWidth( star
->width() * 5 ); //changes right edge position
188 rect
.setHeight( rect
.height() + star
->height() + M
); //changes bottom edge pos
192 rect
.setHeight( rect
.height() + MOODBAR_HEIGHT
+ M
);
194 if( !m_cover
.isNull() )
196 const int availableWidth
= max
.width() - rect
.width() - M
; //WILL be >= (minImageSize.width() - M)
198 m_scaledCover
= m_cover
.scaled(
199 qMin( availableWidth
, m_cover
.width() ),
200 qMin( rect
.height(), m_cover
.height() ),
201 Qt::KeepAspectRatio
, Qt::SmoothTransformation
); //this will force us to be with our bounds
204 if( m_drawShadow
&& !m_scaledCover
.hasAlpha() &&
205 ( m_scaledCover
.width() > 22 || m_scaledCover
.height() > 22 ) )
206 shadowWidth
= static_cast<uint
>( m_scaledCover
.width() / 100.0 * 6.0 );
208 const int widthIncludingImage
= rect
.width()
209 + m_scaledCover
.width()
211 + M
; //margin between text + image
213 rect
.setWidth( widthIncludingImage
);
216 // expand in all directions by M
217 rect
.adjust( -M
, -M
, M
, M
);
219 const QSize newSize
= rect
.size();
220 const QRect screen
= QApplication::desktop()->screenGeometry( m_screen
);
221 QPoint
newPos( MARGIN
, m_y
);
223 switch( m_alignment
)
229 newPos
.rx() = screen
.width() - MARGIN
- newSize
.width();
233 newPos
.ry() = (screen
.height() - newSize
.height()) / 2;
238 newPos
.rx() = (screen
.width() - newSize
.width()) / 2;
242 //ensure we don't dip below the screen
243 if ( newPos
.y() + newSize
.height() > screen
.height() - MARGIN
)
244 newPos
.ry() = screen
.height() - MARGIN
- newSize
.height();
246 // correct for screen position
247 newPos
+= screen
.topLeft();
249 return QRect( newPos
, rect
.size() );
253 OSDWidget::paintEvent( QPaintEvent
* )
255 /// render with margin/spacing @param M and @param size
261 QRect
rect( point
, size
);
262 rect
.adjust( 0, 0, -1, -1 );
265 const uint xround
= (M
* 200) / size
.width();
266 const uint yround
= (M
* 200) / size
.height();
272 mask
.fill( Qt::color0
);
275 p
.setBrush( Qt::color1
);
276 p
.drawRoundRect( rect
, xround
, yround
);
283 foregroundColor().getHsv( &h
, &s
, &v
);
284 shadowColor
= v
> 128 ? Qt::black
: Qt::white
;
287 int align
= Qt::AlignCenter
| Qt::WordBreak
;
291 p
.fillRect( rect
, backgroundColor() );
293 p
.setPen( backgroundColor().dark() );
294 p
.drawRoundRect( rect
, xround
, yround
);
296 rect
.adjust( M
, M
, -M
, -M
);
298 if( !m_cover
.isNull() )
301 r
.setTop( (size
.height() - m_scaledCover
.height()) / 2 );
302 r
.setSize( m_scaledCover
.size() );
304 if( !m_scaledCover
.hasAlpha() && m_drawShadow
&&
305 ( m_scaledCover
.width() > 22 || m_scaledCover
.height() > 22 ) ) {
306 // don't draw a shadow for eg, the Amarok icon
308 const uint shadowSize
= static_cast<uint
>( m_scaledCover
.width() / 100.0 * 6.0 );
310 const QString folder
= Amarok::saveLocation( "covershadow-cache/" );
311 const QString file
= QString( "shadow_albumcover%1x%2.png" ).arg( m_scaledCover
.width() + shadowSize
)
312 .arg( m_scaledCover
.height() + shadowSize
);
313 if ( QFile::exists( folder
+ file
) )
314 shadow
.load( folder
+ file
);
316 shadow
.load( KStandardDirs::locate( "data", "amarok/images/shadow_albumcover.png" ) );
317 shadow
= shadow
.scaled( m_scaledCover
.width() + shadowSize
, m_scaledCover
.height() + shadowSize
,
318 Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
319 shadow
.save( folder
+ file
, "PNG" );
323 target
.convertFromImage( shadow
); //FIXME slow
324 copyBlt( &target
, 0, 0, &m_scaledCover
);
325 m_scaledCover
= target
;
326 r
.setTop( (size
.height() - m_scaledCover
.height()) / 2 );
327 r
.setSize( m_scaledCover
.size() );
330 p
.drawPixmap( r
.topLeft(), m_scaledCover
);
332 rect
.rLeft() += m_scaledCover
.width() + M
;
335 QPixmap
* star
= StarManager::instance()->getStar( m_rating
/2 );
336 int graphicsHeight
= 0;
341 = m_moodbarBundle
.moodbar().draw( rect
.width(), MOODBAR_HEIGHT
);
343 r
.setTop( rect
.bottom() - moodbar
.height()
344 - (m_rating
? star
->height() + M
: 0) );
345 graphicsHeight
+= moodbar
.height() + M
;
347 p
.drawPixmap( r
.left(), r
.top(), moodbar
);
348 m_moodbarBundle
= MetaBundle();
356 r
.setLeft(( rect
.left() + rect
.width() / 2 ) - star
->width() * m_rating
/ 4 );
357 r
.setTop( rect
.bottom() - star
->height() );
358 graphicsHeight
+= star
->height() + M
;
360 bool half
= m_rating
%2;
364 QPixmap
* halfStar
= StarManager::instance()->getHalfStar( m_rating
/2 + 1 );
365 p
.drawPixmap( r
.left() + star
->width() * ( m_rating
/ 2 ), r
.top(), *halfStar
);
366 star
= StarManager::instance()->getStar( m_rating
/2 + 1 );
369 for( int i
= 0; i
< m_rating
/2; i
++ )
371 p
.drawPixmap( r
.left() + i
* star
->width(), r
.top(), *star
);
377 rect
.setBottom( rect
.bottom() - graphicsHeight
);
379 // Draw "shadow" text effect (black outline)
382 QPixmap
pixmap( rect
.size() + QSize(10,10) );
383 pixmap
.fill( Qt::black
);
385 QPainter
p2( &pixmap
);
386 p2
.setFont( font() );
387 p2
.setPen( Qt::white
);
388 p2
.setBrush( Qt::white
);
389 p2
.drawText( QRect(QPoint(5,5), rect
.size()), align
, m_text
);
392 p
.drawImage( rect
.topLeft() - QPoint(5,5), ShadowEngine::makeShadow( pixmap
, shadowColor
) );
395 p
.setPen( foregroundColor() );
397 p
.drawText( rect
, align
, m_text
);
401 OSDWidget::event( QEvent
*e
)
405 case QEvent::ApplicationPaletteChange
:
406 if( !AmarokConfig::osdUseCustomColors() )
407 unsetColors(); //use new palette's colours
411 return QWidget::event( e
);
416 OSDWidget::mousePressEvent( QMouseEvent
* )
422 OSDWidget::unsetColors()
424 const QColorGroup c
= QApplication::palette().active();
426 setPaletteForegroundColor( c
.highlightedText() );
427 setPaletteBackgroundColor( c
.highlight() );
431 OSDWidget::setScreen( int screen
)
434 const int n
= QApplication::desktop()->numScreens();
435 m_screen
= (screen
>= n
) ? n
-1 : screen
;
440 OSDWidget::useMoodbar( void )
442 return (m_moodbarBundle
.moodbar().state() == Moodbar::Loaded
&&
443 AmarokConfig::showMoodbar() );
446 ////// OSDPreviewWidget below /////////////////////
449 #include <kiconloader.h>
454 QImage
icon() { return QImage( KIconLoader().iconPath( "amarok", -K3Icon::SizeHuge
) ); }
457 OSDPreviewWidget::OSDPreviewWidget( QWidget
*parent
)
458 : OSDWidget( parent
, "osdpreview" )
459 , m_dragging( false )
461 m_text
= i18n( "OSD Preview - drag to reposition" );
463 m_alignment
= Center
;
464 m_cover
= Amarok::icon();
467 void OSDPreviewWidget::mousePressEvent( QMouseEvent
*event
)
469 m_dragOffset
= event
->pos();
471 if( event
->button() == Qt::LeftButton
&& !m_dragging
) {
472 grabMouse( Qt::SizeAllCursor
);
478 void OSDPreviewWidget::mouseReleaseEvent( QMouseEvent
* /*event*/ )
485 // compute current Position && offset
486 QDesktopWidget
*desktop
= QApplication::desktop();
487 int currentScreen
= desktop
->screenNumber( pos() );
489 if( currentScreen
!= -1 ) {
491 m_screen
= currentScreen
;
494 emit
positionChanged();
500 void OSDPreviewWidget::mouseMoveEvent( QMouseEvent
*e
)
502 if( m_dragging
&& this == mouseGrabber() )
504 // Here we implement a "snap-to-grid" like positioning system for the preview widget
506 const QRect screen
= QApplication::desktop()->screenGeometry( m_screen
);
507 const uint hcenter
= screen
.width() / 2;
508 const uint eGlobalPosX
= e
->globalPos().x() - screen
.left();
509 const uint snapZone
= screen
.width() / 24;
511 QPoint destination
= e
->globalPos() - m_dragOffset
- screen
.topLeft();
512 int maxY
= screen
.height() - height() - MARGIN
;
513 if( destination
.y() < MARGIN
) destination
.ry() = MARGIN
;
514 if( destination
.y() > maxY
) destination
.ry() = maxY
;
516 if( eGlobalPosX
< (hcenter
-snapZone
) ) {
518 destination
.rx() = MARGIN
;
520 else if( eGlobalPosX
> (hcenter
+snapZone
) ) {
522 destination
.rx() = screen
.width() - MARGIN
- width();
525 const uint eGlobalPosY
= e
->globalPos().y() - screen
.top();
526 const uint vcenter
= screen
.height()/2;
528 destination
.rx() = hcenter
- width()/2;
530 if( eGlobalPosY
>= (vcenter
-snapZone
) && eGlobalPosY
<= (vcenter
+snapZone
) )
532 m_alignment
= Center
;
533 destination
.ry() = vcenter
- height()/2;
535 else m_alignment
= Middle
;
538 destination
+= screen
.topLeft();
546 ////// Amarok::OSD below /////////////////////
548 #include "metabundle.h"
550 Amarok::OSD::OSD(): OSDWidget( 0 )
552 connect( CollectionDB::instance(), SIGNAL( coverChanged( const QString
&, const QString
& ) ),
553 this, SLOT( slotCoverChanged( const QString
&, const QString
& ) ) );
554 connect( CollectionDB::instance(), SIGNAL( imageFetched( const QString
& ) ),
555 this, SLOT( slotImageChanged( const QString
& ) ) );
559 Amarok::OSD::show( const MetaBundle
&bundle
) //slot
562 if( bundle
.url().isEmpty() )
563 text
= i18n( "No track playing" );
567 QVector
<QString
> tags
;
568 tags
.append(bundle
.prettyTitle());
569 for( int i
= 0; i
< PlaylistItem::NUM_COLUMNS
; ++i
)
570 tags
.append(bundle
.prettyText( i
));
572 if( bundle
.length() <= 0 )
573 tags
[PlaylistItem::Length
+1].clear();
575 if( AmarokConfig::osdUsePlaylistColumns() )
578 QVector
<int> availableTags
; //eg, ones that aren't empty
579 static const QList
<int> parens
= //display these in parentheses
580 QList
<int>() << PlaylistItem::PlayCount
<< PlaylistItem::Year
<< PlaylistItem::Comment
581 << PlaylistItem::Genre
<< PlaylistItem::Length
<< PlaylistItem::Bitrate
582 << PlaylistItem::LastPlayed
<< PlaylistItem::Score
<< PlaylistItem::Filesize
;
583 OSDWidget::setMoodbar();
584 OSDWidget::setRating( 0 );
585 for( int i
= 0, n
= Playlist::instance()->numVisibleColumns(); i
< n
; ++i
)
587 const int column
= Playlist::instance()->mapToLogicalColumn( i
);
588 if( !tags
.at( column
+ 1 ).isEmpty() && column
!= PlaylistItem::Rating
)
589 availableTags
.append(column
);
590 if( column
== PlaylistItem::Rating
)
591 OSDWidget::setRating( bundle
.rating() );
592 else if( column
== PlaylistItem::Mood
)
593 OSDWidget::setMoodbar( bundle
);
596 for( int n
= availableTags
.count(), i
= 0; i
< n
; ++i
)
598 const int column
= availableTags
.at( i
);
599 QString append
= ( i
== 0 ) ? ""
600 : ( n
> 1 && i
== n
/ 2 ) ? "\n"
601 : ( parens
.contains( column
) || parens
.contains( availableTags
.at( i
- 1 ) ) ) ? " "
603 append
+= ( parens
.contains( column
) ? "(%1)" : "%1" );
604 text
+= append
.arg( tags
.at( column
+ 1 ) );
609 QMap
<QString
, QString
> args
;
610 args
["prettytitle"] = bundle
.prettyTitle();
611 for( int i
= 0; i
< PlaylistItem::NUM_COLUMNS
; ++i
)
612 args
[bundle
.exactColumnName( i
).toLower()] = bundle
.prettyText( i
);
614 if( bundle
.length() <= 0 )
615 args
["length"].clear();
618 uint time
=EngineController::instance()->engine()->position();
619 uint sec
=(time
/1000)%60; //is there a better way to calculate the time?
621 uint min
=(time
/60)%60;
623 uint hour
=(time
/60)%60;
624 QString timeformat
="";
627 timeformat
+= QString::number(hour
);
630 timeformat
+=QString::number(min
);
634 timeformat
+=QString::number(sec
);
635 args
["elapsed"]=timeformat
;
636 QStringx osd
= AmarokConfig::osdText();
638 // hacky, but works...
639 if( osd
.contains( "%rating" ) )
640 OSDWidget::setRating( AmarokConfig::useRatings() ? bundle
.rating() : 0 );
642 OSDWidget::setRating( 0 );
644 osd
.replace( "%rating", "" );
646 if( osd
.contains( "%moodbar" ) && AmarokConfig::showMoodbar() )
647 OSDWidget::setMoodbar( bundle
);
648 osd
.replace( "%moodbar", "" );
650 text
= osd
.namedOptArgs( args
);
652 // KDE 3.3 rejects \n in the .kcfg file, and KConfig turns \n into \\n, so...
653 text
.replace( "\\n", "\n" );
656 if ( AmarokConfig::osdCover() ) {
657 //avoid showing the generic cover. we can overwrite this by passing an arg.
658 //get large cover for scaling if big cover needed
660 QString location
= QString();
661 if( bundle
.podcastBundle() )
662 location
= CollectionDB::instance()->podcastImage( bundle
, false, 0 );
664 location
= CollectionDB::instance()->albumImage( bundle
, false, 0 );
666 if ( location
.find( "nocover" ) != -1 )
667 setImage( Amarok::icon() );
669 setImage( QImage( location
) );
672 text
= text
.trimmed();
676 text
= MetaBundle::prettyTitle( bundle
.url().fileName() ).trimmed();
678 if( text
.startsWith( "- " ) ) //When we only have a title tag, _something_ prepends a fucking hyphen. Remove that.
679 text
= text
.mid( 2 );
681 if( text
.isEmpty() ) //still
682 text
= i18n("No information available for this track");
684 OSDWidget::show( text
);
688 Amarok::OSD::applySettings()
690 setAlignment( static_cast<OSDWidget::Alignment
>( AmarokConfig::osdAlignment() ) );
691 setDuration( AmarokConfig::osdDuration() );
692 setEnabled( AmarokConfig::osdEnabled() );
693 setOffset( AmarokConfig::osdYOffset() );
694 setScreen( AmarokConfig::osdScreen() );
695 setFont( AmarokConfig::osdFont() );
696 setDrawShadow( AmarokConfig::osdDrawShadow() );
698 if( AmarokConfig::osdUseCustomColors() )
700 setTextColor( AmarokConfig::osdTextColor() );
706 Amarok::OSD::forceToggleOSD()
709 const bool b
= isEnabled();
711 //TODO paort 2.0: fix this
712 //show( EngineController::instance()->bundle() );
720 Amarok::OSD::slotCoverChanged( const QString
&artist
, const QString
&album
)
723 if( AmarokConfig::osdCover() && artist
== EngineController::instance()->bundle().artist()
724 && album
== EngineController::instance()->bundle().album() )
726 QString location
= CollectionDB::instance()->albumImage( artist
, album
, false, 0 );
728 if( location
.find( "nocover" ) != -1 )
729 setImage( Amarok::icon() );
731 setImage( location
);
737 Amarok::OSD::slotImageChanged( const QString
&remoteURL
)
740 QString url
= EngineController::instance()->bundle().url().url();
741 PodcastEpisodeBundle peb
;
742 if( CollectionDB::instance()->getPodcastEpisodeBundle( url
, &peb
) )
744 PodcastChannelBundle pcb
;
745 if( CollectionDB::instance()->getPodcastChannelBundle( peb
.parent().url(), &pcb
) )
747 if( pcb
.imageURL().url() == remoteURL
)
749 QString location
= CollectionDB::instance()->podcastImage( remoteURL
, false, 0 );
750 if( location
== CollectionDB::instance()->notAvailCover( false, 0 ) )
751 setImage( Amarok::icon() );
753 setImage( location
);
761 /* Code copied from kshadowengine.cpp
763 * Copyright (C) 2003 Laur Ivan <laurivan@eircom.net>
766 * - Bernardo Hung <deciare@gta.igs.net> for the enhanced shadow
767 * algorithm (currently used)
768 * - Tim Jansen <tim@tjansen.de> for the API updates and fixes.
770 * This library is free software; you can redistribute it and/or
771 * modify it under the terms of the GNU Library General Public
772 * License version 2 as published by the Free Software Foundation.
774 * This library is distributed in the hope that it will be useful,
775 * but WITHOUT ANY WARRANTY; without even the implied warranty of
776 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
777 * Library General Public License for more details.
779 * You should have received a copy of the GNU Library General Public License
780 * along with this library; see the file COPYING.LIB. If not, write to
781 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
782 * Boston, MA 02110-1301, USA.
785 namespace ShadowEngine
787 // Not sure, doesn't work above 10
788 static const int MULTIPLICATION_FACTOR
= 3;
789 // Multiplication factor for pixels directly above, under, or next to the text
790 static const double AXIS_FACTOR
= 2.0;
791 // Multiplication factor for pixels diagonal to the text
792 static const double DIAGONAL_FACTOR
= 0.1;
794 static const int MAX_OPACITY
= 200;
796 double decay( QImage
&, int, int );
798 QImage
makeShadow( const QPixmap
& textPixmap
, const QColor
&bgColor
)
800 const int w
= textPixmap
.width();
801 const int h
= textPixmap
.height();
802 const int bgr
= bgColor
.red();
803 const int bgg
= bgColor
.green();
804 const int bgb
= bgColor
.blue();
808 // This is the source pixmap
809 QImage img
= textPixmap
.toImage();
811 QImage
result( w
, h
, QImage::Format_ARGB32
);
812 result
.fill( 0 ); // fill with black
814 static const int M
= 5;
815 for( int i
= M
; i
< w
- M
; i
++) {
816 for( int j
= M
; j
< h
- M
; j
++ )
818 alphaShadow
= (int) decay( img
, i
, j
);
820 result
.setPixel( i
,j
, qRgba( bgr
, bgg
, bgb
, qMin( MAX_OPACITY
, alphaShadow
) ) );
827 double decay( QImage
& source
, int i
, int j
)
829 //if ((i < 1) || (j < 1) || (i > source.width() - 2) || (j > source.height() - 2))
833 alphaShadow
=(qGray(source
.pixel(i
-1,j
-1)) * DIAGONAL_FACTOR
+
834 qGray(source
.pixel(i
-1,j
)) * AXIS_FACTOR
+
835 qGray(source
.pixel(i
-1,j
+1)) * DIAGONAL_FACTOR
+
836 qGray(source
.pixel(i
,j
-1)) * AXIS_FACTOR
+
838 qGray(source
.pixel(i
,j
+1)) * AXIS_FACTOR
+
839 qGray(source
.pixel(i
+1,j
-1)) * DIAGONAL_FACTOR
+
840 qGray(source
.pixel(i
+1,j
)) * AXIS_FACTOR
+
841 qGray(source
.pixel(i
+1,j
+1)) * DIAGONAL_FACTOR
) / MULTIPLICATION_FACTOR
;