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 <ruiz@kde.org>
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 //if osdUsePlaylistColumns()
23 #include "podcastbundle.h"
25 #include "StarManager.h"
27 #include <KApplication>
28 #include <KStandardDirs> //locate
31 #include <QDesktopWidget>
32 #include <QMouseEvent>
39 namespace ShadowEngine
41 QImage
makeShadow( const QPixmap
&textPixmap
, const QColor
&bgColor
);
45 #define MOODBAR_HEIGHT 20
48 OSDWidget::OSDWidget( QWidget
*parent
, const char *name
)
49 : QWidget( parent
, Qt::Window
| Qt::X11BypassWindowManagerHint
| Qt::WindowStaysOnTopHint
50 | Qt::FramelessWindowHint
| Qt::CustomizeWindowHint
)
52 , m_timer( new QTimer( this ) )
53 , m_alignment( Middle
)
56 , m_drawShadow( false )
60 setObjectName( name
);
61 setFocusPolicy( Qt::NoFocus
);
64 connect( m_timer
, SIGNAL(timeout()), SLOT(hide()) );
65 connect( CollectionDB::instance(), SIGNAL( ratingChanged( const QString
&, int ) ),
66 this, SLOT( ratingChanged( const QString
&, int ) ) );
68 //or crashes, KWindowSystem bug I think, crashes in QWidget::icon()
69 kapp
->setTopWidget( this );
73 OSDWidget::show( const QString
&text
, QImage newImage
)
75 if ( !newImage
.isNull() )
78 int w
= m_scaledCover
.width();
79 int h
= m_scaledCover
.height();
80 m_scaledCover
= m_cover
.scaled( w
, h
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
87 OSDWidget::ratingChanged( const QString
& path
, int rating
)
89 Meta::TrackPtr track
= EngineController::instance()->currentTrack();
92 if( track
->playableUrl().isLocalFile() && track
->playableUrl().path() == path
)
93 ratingChanged( rating
);
97 OSDWidget::ratingChanged( const short rating
)
99 m_text
= '\n' + i18n( "Rating changed" );
100 setRating( rating
); //Checks isEnabled() before doing anything
107 OSDWidget::volChanged( unsigned char volume
)
112 m_newvolume
= volume
;
113 m_text
= m_newvolume
? i18n("Volume: %1%", m_newvolume
) : i18n("Mute");
120 OSDWidget::show() //virtual
122 if ( !isEnabled() || m_text
.isEmpty() )
125 const uint M
= fontMetrics().width( 'x' );
127 const QRect oldGeometry
= QRect( pos(), size() );
128 const QRect newGeometry
= determineMetrics( M
);
130 if( newGeometry
.width() > 0 && newGeometry
.height() > 0 )
133 m_size
= newGeometry
.size();
134 //render( M, newGeometry.size() );
135 setGeometry( newGeometry
);
137 // bitBlt( this, 0, 0, &m_buffer );
139 if( m_duration
) //duration 0 -> stay forever
140 m_timer
->start( m_duration
, true ); //calls hide()
143 warning() << "Attempted to make an invalid sized OSD\n";
149 OSDWidget::determineMetrics( const uint M
)
151 // sometimes we only have a tiddly cover
152 const QSize minImageSize
= m_cover
.size().boundedTo( QSize(100,100) );
154 // determine a sensible maximum size, don't cover the whole desktop or cross the screen
155 const QSize
margin( (M
+ MARGIN
) * 2, (M
+ MARGIN
) * 2 ); //margins
156 const QSize image
= m_cover
.isNull() ? QSize( 0, 0 ) : minImageSize
;
157 const QSize max
= QApplication::desktop()->screen( m_screen
)->size() - margin
;
159 // If we don't do that, the boundingRect() might not be suitable for drawText() (Qt issue N67674)
160 m_text
.replace( QRegExp(" +\n"), "\n" );
161 // remove consecutive line breaks
162 m_text
.replace( QRegExp("\n+"), "\n" );
164 // The osd cannot be larger than the screen
165 QRect rect
= fontMetrics().boundingRect( 0, 0, max
.width() - image
.width(), max
.height(),
166 Qt::AlignCenter
| Qt::WordBreak
, m_text
);
170 static const QString tmp
= QString ("******").insert( 3,
171 ( i18n("Volume: 100%").length() >= i18n("Mute").length() )?
172 i18n("Volume: 100%") : i18n("Mute") );
174 QRect tmpRect
= fontMetrics().boundingRect( 0, 0,
175 max
.width() - image
.width(), max
.height() - fontMetrics().height(),
176 Qt::AlignCenter
| Qt::WordBreak
, tmp
);
177 tmpRect
.setHeight( tmpRect
.height() + fontMetrics().height() / 2 );
184 QPixmap
* star
= StarManager::instance()->getStar( 1 );
185 if( rect
.width() < star
->width() * 5 )
186 rect
.setWidth( star
->width() * 5 ); //changes right edge position
187 rect
.setHeight( rect
.height() + star
->height() + M
); //changes bottom edge pos
191 rect
.setHeight( rect
.height() + MOODBAR_HEIGHT
+ M
);
193 if( !m_cover
.isNull() )
195 const int availableWidth
= max
.width() - rect
.width() - M
; //WILL be >= (minImageSize.width() - M)
197 m_scaledCover
= m_cover
.scaled(
198 qMin( availableWidth
, m_cover
.width() ),
199 qMin( rect
.height(), m_cover
.height() ),
200 Qt::KeepAspectRatio
, Qt::SmoothTransformation
); //this will force us to be with our bounds
203 if( m_drawShadow
&& !m_scaledCover
.hasAlpha() &&
204 ( m_scaledCover
.width() > 22 || m_scaledCover
.height() > 22 ) )
205 shadowWidth
= static_cast<uint
>( m_scaledCover
.width() / 100.0 * 6.0 );
207 const int widthIncludingImage
= rect
.width()
208 + m_scaledCover
.width()
210 + M
; //margin between text + image
212 rect
.setWidth( widthIncludingImage
);
215 // expand in all directions by M
216 rect
.adjust( -M
, -M
, M
, M
);
218 const QSize newSize
= rect
.size();
219 const QRect screen
= QApplication::desktop()->screenGeometry( m_screen
);
220 QPoint
newPos( MARGIN
, m_y
);
222 switch( m_alignment
)
228 newPos
.rx() = screen
.width() - MARGIN
- newSize
.width();
232 newPos
.ry() = (screen
.height() - newSize
.height()) / 2;
237 newPos
.rx() = (screen
.width() - newSize
.width()) / 2;
241 //ensure we don't dip below the screen
242 if ( newPos
.y() + newSize
.height() > screen
.height() - MARGIN
)
243 newPos
.ry() = screen
.height() - MARGIN
- newSize
.height();
245 // correct for screen position
246 newPos
+= screen
.topLeft();
248 return QRect( newPos
, rect
.size() );
252 OSDWidget::paintEvent( QPaintEvent
* )
254 /// render with margin/spacing @param M and @param size
260 QRect
rect( point
, size
);
261 rect
.adjust( 0, 0, -1, -1 );
264 const uint xround
= (M
* 200) / size
.width();
265 const uint yround
= (M
* 200) / size
.height();
271 mask
.fill( Qt::color0
);
274 p
.setBrush( Qt::color1
);
275 p
.drawRoundRect( rect
, xround
, yround
);
282 foregroundColor().getHsv( &h
, &s
, &v
);
283 shadowColor
= v
> 128 ? Qt::black
: Qt::white
;
286 int align
= Qt::AlignCenter
| Qt::WordBreak
;
290 p
.fillRect( rect
, backgroundColor() );
292 p
.setPen( backgroundColor().dark() );
293 p
.drawRoundRect( rect
, xround
, yround
);
295 rect
.adjust( M
, M
, -M
, -M
);
297 if( !m_cover
.isNull() )
300 r
.setTop( (size
.height() - m_scaledCover
.height()) / 2 );
301 r
.setSize( m_scaledCover
.size() );
303 if( !m_scaledCover
.hasAlpha() && m_drawShadow
&&
304 ( m_scaledCover
.width() > 22 || m_scaledCover
.height() > 22 ) ) {
305 // don't draw a shadow for eg, the Amarok icon
307 const uint shadowSize
= static_cast<uint
>( m_scaledCover
.width() / 100.0 * 6.0 );
309 const QString folder
= Amarok::saveLocation( "covershadow-cache/" );
310 const QString file
= QString( "shadow_albumcover%1x%2.png" ).arg( m_scaledCover
.width() + shadowSize
)
311 .arg( m_scaledCover
.height() + shadowSize
);
312 if ( QFile::exists( folder
+ file
) )
313 shadow
.load( folder
+ file
);
315 shadow
.load( KStandardDirs::locate( "data", "amarok/images/shadow_albumcover.png" ) );
316 shadow
= shadow
.scaled( m_scaledCover
.width() + shadowSize
, m_scaledCover
.height() + shadowSize
,
317 Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
318 shadow
.save( folder
+ file
, "PNG" );
322 target
.convertFromImage( shadow
); //FIXME slow
323 copyBlt( &target
, 0, 0, &m_scaledCover
);
324 m_scaledCover
= target
;
325 r
.setTop( (size
.height() - m_scaledCover
.height()) / 2 );
326 r
.setSize( m_scaledCover
.size() );
329 p
.drawPixmap( r
.topLeft(), m_scaledCover
);
331 rect
.rLeft() += m_scaledCover
.width() + M
;
334 QPixmap
* star
= StarManager::instance()->getStar( m_rating
/2 );
335 int graphicsHeight
= 0;
340 = m_moodbarBundle
.moodbar().draw( rect
.width(), MOODBAR_HEIGHT
);
342 r
.setTop( rect
.bottom() - moodbar
.height()
343 - (m_rating
? star
->height() + M
: 0) );
344 graphicsHeight
+= moodbar
.height() + M
;
346 p
.drawPixmap( r
.left(), r
.top(), moodbar
);
347 m_moodbarBundle
= MetaBundle();
355 r
.setLeft(( rect
.left() + rect
.width() / 2 ) - star
->width() * m_rating
/ 4 );
356 r
.setTop( rect
.bottom() - star
->height() );
357 graphicsHeight
+= star
->height() + M
;
359 bool half
= m_rating
%2;
363 QPixmap
* halfStar
= StarManager::instance()->getHalfStar( m_rating
/2 + 1 );
364 p
.drawPixmap( r
.left() + star
->width() * ( m_rating
/ 2 ), r
.top(), *halfStar
);
365 star
= StarManager::instance()->getStar( m_rating
/2 + 1 );
368 for( int i
= 0; i
< m_rating
/2; i
++ )
370 p
.drawPixmap( r
.left() + i
* star
->width(), r
.top(), *star
);
376 rect
.setBottom( rect
.bottom() - graphicsHeight
);
378 // Draw "shadow" text effect (black outline)
381 QPixmap
pixmap( rect
.size() + QSize(10,10) );
382 pixmap
.fill( Qt::black
);
384 QPainter
p2( &pixmap
);
385 p2
.setFont( font() );
386 p2
.setPen( Qt::white
);
387 p2
.setBrush( Qt::white
);
388 p2
.drawText( QRect(QPoint(5,5), rect
.size()), align
, m_text
);
391 p
.drawImage( rect
.topLeft() - QPoint(5,5), ShadowEngine::makeShadow( pixmap
, shadowColor
) );
394 p
.setPen( foregroundColor() );
396 p
.drawText( rect
, align
, m_text
);
400 OSDWidget::event( QEvent
*e
)
404 case QEvent::ApplicationPaletteChange
:
405 if( !AmarokConfig::osdUseCustomColors() )
406 unsetColors(); //use new palette's colours
410 return QWidget::event( e
);
415 OSDWidget::mousePressEvent( QMouseEvent
* )
421 OSDWidget::unsetColors()
423 const QColorGroup c
= QApplication::palette().active();
425 setPaletteForegroundColor( c
.highlightedText() );
426 setPaletteBackgroundColor( c
.highlight() );
430 OSDWidget::setScreen( int screen
)
433 const int n
= QApplication::desktop()->numScreens();
434 m_screen
= (screen
>= n
) ? n
-1 : screen
;
439 OSDWidget::useMoodbar( void )
441 return (m_moodbarBundle
.moodbar().state() == Moodbar::Loaded
&&
442 AmarokConfig::showMoodbar() );
445 ////// OSDPreviewWidget below /////////////////////
448 #include <kiconloader.h>
453 QImage
icon() { return QImage( KIconLoader().iconPath( "amarok", -KIconLoader::SizeHuge
) ); }
456 OSDPreviewWidget::OSDPreviewWidget( QWidget
*parent
)
457 : OSDWidget( parent
, "osdpreview" )
458 , m_dragging( false )
460 m_text
= i18n( "OSD Preview - drag to reposition" );
462 m_alignment
= Center
;
463 m_cover
= Amarok::icon();
466 void OSDPreviewWidget::mousePressEvent( QMouseEvent
*event
)
468 m_dragOffset
= event
->pos();
470 if( event
->button() == Qt::LeftButton
&& !m_dragging
) {
471 grabMouse( Qt::SizeAllCursor
);
477 void OSDPreviewWidget::mouseReleaseEvent( QMouseEvent
* /*event*/ )
484 // compute current Position && offset
485 QDesktopWidget
*desktop
= QApplication::desktop();
486 int currentScreen
= desktop
->screenNumber( pos() );
488 if( currentScreen
!= -1 ) {
490 m_screen
= currentScreen
;
493 emit
positionChanged();
499 void OSDPreviewWidget::mouseMoveEvent( QMouseEvent
*e
)
501 if( m_dragging
&& this == mouseGrabber() )
503 // Here we implement a "snap-to-grid" like positioning system for the preview widget
505 const QRect screen
= QApplication::desktop()->screenGeometry( m_screen
);
506 const uint hcenter
= screen
.width() / 2;
507 const uint eGlobalPosX
= e
->globalPos().x() - screen
.left();
508 const uint snapZone
= screen
.width() / 24;
510 QPoint destination
= e
->globalPos() - m_dragOffset
- screen
.topLeft();
511 int maxY
= screen
.height() - height() - MARGIN
;
512 if( destination
.y() < MARGIN
) destination
.ry() = MARGIN
;
513 if( destination
.y() > maxY
) destination
.ry() = maxY
;
515 if( eGlobalPosX
< (hcenter
-snapZone
) ) {
517 destination
.rx() = MARGIN
;
519 else if( eGlobalPosX
> (hcenter
+snapZone
) ) {
521 destination
.rx() = screen
.width() - MARGIN
- width();
524 const uint eGlobalPosY
= e
->globalPos().y() - screen
.top();
525 const uint vcenter
= screen
.height()/2;
527 destination
.rx() = hcenter
- width()/2;
529 if( eGlobalPosY
>= (vcenter
-snapZone
) && eGlobalPosY
<= (vcenter
+snapZone
) )
531 m_alignment
= Center
;
532 destination
.ry() = vcenter
- height()/2;
534 else m_alignment
= Middle
;
537 destination
+= screen
.topLeft();
545 ////// Amarok::OSD below /////////////////////
547 #include "metabundle.h"
549 Amarok::OSD::OSD(): OSDWidget( 0 )
551 connect( CollectionDB::instance(), SIGNAL( coverChanged( const QString
&, const QString
& ) ),
552 this, SLOT( slotCoverChanged( const QString
&, const QString
& ) ) );
553 connect( CollectionDB::instance(), SIGNAL( imageFetched( const QString
& ) ),
554 this, SLOT( slotImageChanged( const QString
& ) ) );
558 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
);
689 Amarok::OSD::applySettings()
691 setAlignment( static_cast<OSDWidget::Alignment
>( AmarokConfig::osdAlignment() ) );
692 setDuration( AmarokConfig::osdDuration() );
693 setEnabled( AmarokConfig::osdEnabled() );
694 setOffset( AmarokConfig::osdYOffset() );
695 setScreen( AmarokConfig::osdScreen() );
696 setFont( AmarokConfig::osdFont() );
697 setDrawShadow( AmarokConfig::osdDrawShadow() );
699 if( AmarokConfig::osdUseCustomColors() )
701 setTextColor( AmarokConfig::osdTextColor() );
707 Amarok::OSD::forceToggleOSD()
710 const bool b
= isEnabled();
712 //TODO paort 2.0: fix this
713 //show( EngineController::instance()->bundle() );
721 Amarok::OSD::slotCoverChanged( const QString
&artist
, const QString
&album
)
724 if( AmarokConfig::osdCover() && artist
== EngineController::instance()->bundle().artist()
725 && album
== EngineController::instance()->bundle().album() )
727 QString location
= CollectionDB::instance()->albumImage( artist
, album
, false, 0 );
729 if( location
.find( "nocover" ) != -1 )
730 setImage( Amarok::icon() );
732 setImage( location
);
738 Amarok::OSD::slotImageChanged( const QString
&remoteURL
)
741 QString url
= EngineController::instance()->bundle().url().url();
742 PodcastEpisodeBundle peb
;
743 if( CollectionDB::instance()->getPodcastEpisodeBundle( url
, &peb
) )
745 PodcastChannelBundle pcb
;
746 if( CollectionDB::instance()->getPodcastChannelBundle( peb
.parent().url(), &pcb
) )
748 if( pcb
.imageURL().url() == remoteURL
)
750 QString location
= CollectionDB::instance()->podcastImage( remoteURL
, false, 0 );
751 if( location
== CollectionDB::instance()->notAvailCover( false, 0 ) )
752 setImage( Amarok::icon() );
754 setImage( location
);
762 /* Code copied from kshadowengine.cpp
764 * Copyright (C) 2003 Laur Ivan <laurivan@eircom.net>
767 * - Bernardo Hung <deciare@gta.igs.net> for the enhanced shadow
768 * algorithm (currently used)
769 * - Tim Jansen <tim@tjansen.de> for the API updates and fixes.
771 * This library is free software; you can redistribute it and/or
772 * modify it under the terms of the GNU Library General Public
773 * License version 2 as published by the Free Software Foundation.
775 * This library is distributed in the hope that it will be useful,
776 * but WITHOUT ANY WARRANTY; without even the implied warranty of
777 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
778 * Library General Public License for more details.
780 * You should have received a copy of the GNU Library General Public License
781 * along with this library; see the file COPYING.LIB. If not, write to
782 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
783 * Boston, MA 02110-1301, USA.
786 namespace ShadowEngine
788 // Not sure, doesn't work above 10
789 static const int MULTIPLICATION_FACTOR
= 3;
790 // Multiplication factor for pixels directly above, under, or next to the text
791 static const double AXIS_FACTOR
= 2.0;
792 // Multiplication factor for pixels diagonal to the text
793 static const double DIAGONAL_FACTOR
= 0.1;
795 static const int MAX_OPACITY
= 200;
797 double decay( QImage
&, int, int );
799 QImage
makeShadow( const QPixmap
& textPixmap
, const QColor
&bgColor
)
801 const int w
= textPixmap
.width();
802 const int h
= textPixmap
.height();
803 const int bgr
= bgColor
.red();
804 const int bgg
= bgColor
.green();
805 const int bgb
= bgColor
.blue();
809 // This is the source pixmap
810 QImage img
= textPixmap
.toImage();
812 QImage
result( w
, h
, QImage::Format_ARGB32
);
813 result
.fill( 0 ); // fill with black
815 static const int M
= 5;
816 for( int i
= M
; i
< w
- M
; i
++) {
817 for( int j
= M
; j
< h
- M
; j
++ )
819 alphaShadow
= (int) decay( img
, i
, j
);
821 result
.setPixel( i
,j
, qRgba( bgr
, bgg
, bgb
, qMin( MAX_OPACITY
, alphaShadow
) ) );
828 double decay( QImage
& source
, int i
, int j
)
830 //if ((i < 1) || (j < 1) || (i > source.width() - 2) || (j > source.height() - 2))
834 alphaShadow
=(qGray(source
.pixel(i
-1,j
-1)) * DIAGONAL_FACTOR
+
835 qGray(source
.pixel(i
-1,j
)) * AXIS_FACTOR
+
836 qGray(source
.pixel(i
-1,j
+1)) * DIAGONAL_FACTOR
+
837 qGray(source
.pixel(i
,j
-1)) * AXIS_FACTOR
+
839 qGray(source
.pixel(i
,j
+1)) * AXIS_FACTOR
+
840 qGray(source
.pixel(i
+1,j
-1)) * DIAGONAL_FACTOR
+
841 qGray(source
.pixel(i
+1,j
)) * AXIS_FACTOR
+
842 qGray(source
.pixel(i
+1,j
+1)) * DIAGONAL_FACTOR
) / MULTIPLICATION_FACTOR
;