AlbumArt.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. #include "precomp__gen_ff.h"
  2. #include <api/core/api_core.h>
  3. #include "main.h"
  4. #include "AlbumArt.h"
  5. #include "wa2frontend.h"
  6. #include <api.h>
  7. #include <tataki/bitmap/bitmap.h>
  8. #include <api\wnd\notifmsg.h>
  9. #include <api/script/scriptmgr.h>
  10. #include <api/script/script.h>
  11. #define ALBUMART_MAX_THREADS 4
  12. const wchar_t albumArtXuiObjectStr[] = L"AlbumArt"; // This is the xml tag
  13. char albumArtXuiSvcName[] = "Album Art XUI object"; // this is the name of the xuiservice
  14. AlbumArtScriptController _albumartController;
  15. AlbumArtScriptController *albumartController = &_albumartController;
  16. BEGIN_SERVICES( wa2AlbumArt_Svcs );
  17. DECLARE_SERVICE( XuiObjectCreator<AlbumArtXuiSvc> );
  18. END_SERVICES( wa2AlbumArt_Svcs, _wa2AlbumArt_Svcs );
  19. // --------------------------------------------------------
  20. // Maki Script Object
  21. // --------------------------------------------------------
  22. // -- Functions table -------------------------------------
  23. function_descriptor_struct AlbumArtScriptController::exportedFunction[] = {
  24. {L"refresh", 0, (void *)AlbumArt::script_vcpu_refresh },
  25. {L"onAlbumArtLoaded", 1, (void *)AlbumArt::script_vcpu_onAlbumArtLoaded },
  26. {L"isLoading", 0, (void *)AlbumArt::script_vcpu_isLoading },
  27. };
  28. const wchar_t *AlbumArtScriptController::getClassName()
  29. {
  30. return L"AlbumArtLayer";
  31. }
  32. const wchar_t *AlbumArtScriptController::getAncestorClassName()
  33. {
  34. return L"Layer";
  35. }
  36. ScriptObject *AlbumArtScriptController::instantiate()
  37. {
  38. AlbumArt *a = new AlbumArt;
  39. ASSERT( a != NULL );
  40. return a->getScriptObject();
  41. }
  42. void AlbumArtScriptController::destroy( ScriptObject *o )
  43. {
  44. AlbumArt *a = static_cast<AlbumArt *>( o->vcpu_getInterface( albumArtGuid ) );
  45. ASSERT( a != NULL );
  46. delete a;
  47. }
  48. void *AlbumArtScriptController::encapsulate( ScriptObject *o )
  49. {
  50. return NULL; // no encapsulation yet
  51. }
  52. void AlbumArtScriptController::deencapsulate( void *o )
  53. {}
  54. int AlbumArtScriptController::getNumFunctions()
  55. {
  56. return sizeof( exportedFunction ) / sizeof( function_descriptor_struct );
  57. }
  58. const function_descriptor_struct *AlbumArtScriptController::getExportedFunctions()
  59. {
  60. return exportedFunction;
  61. }
  62. GUID AlbumArtScriptController::getClassGuid()
  63. {
  64. return albumArtGuid;
  65. }
  66. XMLParamPair AlbumArt::params[] =
  67. {
  68. {ALBUMART_NOTFOUNDIMAGE, L"NOTFOUNDIMAGE"},
  69. {ALBUMART_SOURCE, L"SOURCE"},
  70. {ALBUMART_VALIGN, L"VALIGN"},
  71. {ALBUMART_ALIGN, L"ALIGN"},
  72. {ALBUMART_STRETCHED, L"STRETCHED"},
  73. {ALBUMART_NOREFRESH, L"NOAUTOREFRESH"},
  74. };
  75. class AlbumArtThreadContext
  76. {
  77. public:
  78. AlbumArtThreadContext( const wchar_t *_filename, AlbumArt *_wnd )
  79. {
  80. /* lazy load these two handles */
  81. if ( !_wnd->hMainThread )
  82. _wnd->hMainThread = WASABI_API_APP->main_getMainThreadHandle();
  83. if ( !_wnd->thread_semaphore )
  84. _wnd->thread_semaphore = CreateSemaphore( 0, ALBUMART_MAX_THREADS, ALBUMART_MAX_THREADS, 0 );
  85. wnd = _wnd;
  86. iterator = wnd->iterator;
  87. h = w = 0;
  88. bits = 0;
  89. filename = _wcsdup( _filename );
  90. }
  91. void FreeBits()
  92. {
  93. if ( bits )
  94. WASABI_API_MEMMGR->sysFree( bits );
  95. bits = 0;
  96. }
  97. ~AlbumArtThreadContext()
  98. {
  99. if ( wnd )
  100. {
  101. if ( wnd->thread_semaphore )
  102. ReleaseSemaphore( wnd->thread_semaphore, 1, 0 );
  103. wnd->isLoading--;
  104. }
  105. free( filename );
  106. }
  107. static void CALLBACK AlbumArtNotifyAPC( ULONG_PTR p );
  108. bool LoadArt();
  109. int h;
  110. int w;
  111. ARGB32 *bits;
  112. LONG iterator;
  113. wchar_t *filename;
  114. AlbumArt *wnd;
  115. };
  116. AlbumArt::AlbumArt()
  117. {
  118. getScriptObject()->vcpu_setInterface( albumArtGuid, ( void * )static_cast<AlbumArt *>( this ) );
  119. getScriptObject()->vcpu_setClassName( L"AlbumArtLayer" );
  120. getScriptObject()->vcpu_setController( albumartController );
  121. WASABI_API_MEDIACORE->core_addCallback( 0, this );
  122. w = 0;
  123. h = 0;
  124. iterator = 0;
  125. bits = 0;
  126. hMainThread = 0;
  127. thread_semaphore = 0;
  128. artBitmap = 0;
  129. valign = 0;
  130. align = 0;
  131. stretched = false;
  132. missing_art_image = L"winamp.cover.notfound"; // default to this.
  133. src_file = L"";
  134. forceRefresh = false;
  135. noAutoRefresh = false;
  136. noMakiCallback = false;
  137. isLoading = 0;
  138. /* register XML parameters */
  139. xuihandle = newXuiHandle();
  140. CreateXMLParameters( xuihandle );
  141. }
  142. void AlbumArt::CreateXMLParameters( int master_handle )
  143. {
  144. //ALBUMART_PARENT::CreateXMLParameters(master_handle);
  145. int numParams = sizeof( params ) / sizeof( params[ 0 ] );
  146. hintNumberOfParams( xuihandle, numParams );
  147. for ( int i = 0; i < numParams; i++ )
  148. addParam( xuihandle, params[ i ], XUI_ATTRIBUTE_IMPLIED );
  149. }
  150. AlbumArt::~AlbumArt()
  151. {
  152. WASABI_API_SYSCB->syscb_deregisterCallback( static_cast<MetadataCallbackI *>( this ) );
  153. WASABI_API_MEDIACORE->core_delCallback( 0, this );
  154. // wait for all of our threads to finish
  155. InterlockedIncrement( &iterator ); // our kill switch (will invalidate iterator on all outstanding threads)
  156. if ( thread_semaphore )
  157. {
  158. for ( int i = 0; i < ALBUMART_MAX_THREADS; i++ )
  159. {
  160. if ( WaitForMultipleObjectsEx( 1, &thread_semaphore, FALSE, INFINITE, TRUE ) != WAIT_OBJECT_0 )
  161. i--;
  162. }
  163. }
  164. delete artBitmap;
  165. if ( bits )
  166. WASABI_API_MEMMGR->sysFree( bits );
  167. if ( thread_semaphore )
  168. CloseHandle( thread_semaphore );
  169. if ( hMainThread )
  170. CloseHandle( hMainThread );
  171. }
  172. bool AlbumArt::layer_isInvalid()
  173. {
  174. return !bits;
  175. }
  176. void CALLBACK AlbumArtThreadContext::AlbumArtNotifyAPC( ULONG_PTR p )
  177. {
  178. AlbumArtThreadContext *context = (AlbumArtThreadContext *)p;
  179. if ( context->wnd->iterator == context->iterator )
  180. context->wnd->ArtLoaded( context->w, context->h, context->bits );
  181. else
  182. context->FreeBits();
  183. delete context;
  184. }
  185. bool AlbumArtThreadContext::LoadArt()
  186. {
  187. if ( wnd->iterator != iterator )
  188. return false;
  189. if ( AGAVE_API_ALBUMART->GetAlbumArt( filename, L"cover", &w, &h, &bits ) != ALBUMART_SUCCESS )
  190. {
  191. bits = 0;
  192. w = 0;
  193. h = 0;
  194. }
  195. if ( wnd->iterator == iterator ) // make sure we're still valid
  196. {
  197. QueueUserAPC( AlbumArtNotifyAPC, wnd->hMainThread, (ULONG_PTR)this );
  198. return true;
  199. }
  200. else
  201. {
  202. FreeBits();
  203. return false;
  204. }
  205. }
  206. static int AlbumArtThreadPoolFunc( HANDLE handle, void *user_data, intptr_t id )
  207. {
  208. AlbumArtThreadContext *context = (AlbumArtThreadContext *)user_data;
  209. if ( context->LoadArt() == false )
  210. {
  211. delete context;
  212. }
  213. return 0;
  214. }
  215. void AlbumArt::ArtLoaded( int _w, int _h, ARGB32 *_bits )
  216. {
  217. if ( bits )
  218. WASABI_API_MEMMGR->sysFree( bits );
  219. if ( artBitmap )
  220. {
  221. delete artBitmap;
  222. artBitmap = 0;
  223. }
  224. bits = _bits;
  225. w = _w;
  226. h = _h;
  227. if ( !bits )
  228. {
  229. SkinBitmap *albumart = missing_art_image.getBitmap();
  230. if ( albumart )
  231. {
  232. w = albumart->getWidth();
  233. h = albumart->getHeight();
  234. }
  235. }
  236. deleteRegion();
  237. makeRegion();
  238. notifyParent( ChildNotify::AUTOWHCHANGED );
  239. invalidate();
  240. // notify maki scripts that albumart has been found or not
  241. if ( !noMakiCallback && _bits )
  242. {
  243. AlbumArt::script_vcpu_onAlbumArtLoaded( SCRIPT_CALL, this->getScriptObject(), MAKE_SCRIPT_BOOLEAN( (int)bits ) );
  244. }
  245. }
  246. void AlbumArt::onSetVisible( int show )
  247. {
  248. if ( show )
  249. {
  250. corecb_onUrlChange( wa2.GetCurrentFile() );
  251. }
  252. else
  253. {
  254. if ( bits )
  255. {
  256. WASABI_API_MEMMGR->sysFree( bits );
  257. delete artBitmap;
  258. artBitmap = 0;
  259. }
  260. bits = 0;
  261. }
  262. }
  263. int AlbumArt::corecb_onUrlChange( const wchar_t *filename )
  264. {
  265. // Martin> if we call this from maki we want to do a refresh regardless of the albumartlayer being visible or not
  266. if ( forceRefresh || ( !noAutoRefresh && isVisible() ) )
  267. {
  268. isLoading++;
  269. // Martin > do a check for a specific file, defined via source param
  270. if ( WCSICMP( src_file, L"" ) )
  271. filename = src_file;
  272. InterlockedIncrement( &iterator );
  273. AlbumArtThreadContext *context = new AlbumArtThreadContext( filename, this );
  274. // make sure we have an available thread free (wait for one if we don't)
  275. while ( WaitForMultipleObjectsEx( 1, &thread_semaphore, FALSE, INFINITE, TRUE ) != WAIT_OBJECT_0 )
  276. {}
  277. // int vis__ = isVisible();
  278. WASABI_API_THREADPOOL->RunFunction( 0, AlbumArtThreadPoolFunc, context, 0, api_threadpool::FLAG_LONG_EXECUTION );
  279. }
  280. return 1;
  281. }
  282. int AlbumArt::onInit()
  283. {
  284. int r = ALBUMART_PARENT::onInit();
  285. WASABI_API_SYSCB->syscb_registerCallback( static_cast<MetadataCallbackI *>( this ) );
  286. AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() );
  287. return r;
  288. }
  289. int AlbumArt::skincb_onColorThemeChanged( const wchar_t *newcolortheme )
  290. {
  291. ALBUMART_PARENT::skincb_onColorThemeChanged( newcolortheme );
  292. invalidate();
  293. return 0;
  294. }
  295. SkinBitmap *AlbumArt::getBitmap()
  296. {
  297. if ( artBitmap )
  298. return artBitmap;
  299. if ( bits )
  300. {
  301. artBitmap = new HQSkinBitmap( bits, w, h ); //TH WDP2-212
  302. return artBitmap;
  303. }
  304. return missing_art_image.getBitmap();
  305. }
  306. void AlbumArt::layer_adjustDest( RECT *r )
  307. {
  308. if ( !w || !h )
  309. return;
  310. if ( stretched )
  311. return;
  312. //getClientRect(r);
  313. // maintain 'square' stretching
  314. int dstW = r->right - r->left;
  315. int dstH = r->bottom - r->top;
  316. double aspX = (double)( dstW ) / (double)w;
  317. double aspY = (double)( dstH ) / (double)h;
  318. double asp = min( aspX, aspY );
  319. int newW = (int)( w * asp );
  320. int newH = (int)( h * asp );
  321. // Align
  322. int offsetX = ( dstW - newW ) / 2;
  323. if ( align == 1 )
  324. offsetX *= 2;
  325. else if ( align == -1 )
  326. offsetX = 0;
  327. // Valign
  328. int offsetY = ( dstH - newH ) / 2;
  329. if ( valign == 1 )
  330. offsetY *= 2;
  331. else if ( valign == -1 )
  332. offsetY = 0;
  333. r->left += offsetX;
  334. r->right = r->left + newW;
  335. r->top += offsetY;
  336. r->bottom = r->top + newH;
  337. // This prevents parts of the image being cut off (if the img has the same dimensions as the rect) on moving/clicking winamp
  338. // (they will just flicker, but at least they won't stay now)
  339. // benski> CUT!!! no no no no no this is very bad because this gets called inside layer::onPaint
  340. //invalidate();
  341. }
  342. /*
  343. int AlbumArt::getWidth()
  344. {
  345. RECT r;
  346. getClientRect(&r);
  347. getDest(&r);
  348. return r.right-r.left;
  349. }
  350. int AlbumArt::getHeight()
  351. {
  352. RECT r;
  353. getClientRect(&r);
  354. getDest(&r);
  355. return r.bottom-r.top;
  356. }
  357. */
  358. /*
  359. int AlbumArt::onPaint(Canvas *canvas)
  360. {
  361. ALBUMART_PARENT::onPaint(canvas);
  362. if (bits)
  363. {
  364. SkinBitmap albumart(bits, w, h);
  365. RECT dst;
  366. getBufferPaintDest(&dst);
  367. albumart.stretchToRectAlpha(canvas, &dst, getPaintingAlpha());
  368. }
  369. return 1;
  370. }
  371. */
  372. int AlbumArt::setXuiParam( int _xuihandle, int attrid, const wchar_t *name, const wchar_t *strval )
  373. {
  374. if ( xuihandle != _xuihandle )
  375. return ALBUMART_PARENT::setXuiParam( _xuihandle, attrid, name, strval );
  376. switch ( attrid )
  377. {
  378. case ALBUMART_NOTFOUNDIMAGE:
  379. missing_art_image = strval;
  380. if ( !bits )
  381. {
  382. noMakiCallback = true;
  383. ArtLoaded( 0, 0, 0 );
  384. noMakiCallback = false;
  385. }
  386. break;
  387. case ALBUMART_SOURCE:
  388. src_file = strval;
  389. AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() ); // This Param should _always_ hold our current file
  390. break;
  391. case ALBUMART_VALIGN:
  392. if ( !WCSICMP( strval, L"top" ) )
  393. valign = -1;
  394. else if ( !WCSICMP( strval, L"bottom" ) )
  395. valign = 1;
  396. else
  397. valign = 0;
  398. deferedInvalidate();
  399. break;
  400. case ALBUMART_ALIGN:
  401. if ( !WCSICMP( strval, L"left" ) )
  402. align = -1;
  403. else if ( !WCSICMP( strval, L"right" ) )
  404. align = 1;
  405. else
  406. align = 0;
  407. deferedInvalidate();
  408. break;
  409. case ALBUMART_STRETCHED:
  410. if ( !WCSICMP( strval, L"0" ) || !WCSICMP( strval, L"" ) )
  411. stretched = false;
  412. else
  413. stretched = true;
  414. deferedInvalidate();
  415. break;
  416. case ALBUMART_NOREFRESH:
  417. if ( !WCSICMP( strval, L"0" ) || !WCSICMP( strval, L"" ) )
  418. noAutoRefresh = false;
  419. else
  420. noAutoRefresh = true;
  421. break;
  422. default:
  423. return 0;
  424. }
  425. return 1;
  426. }
  427. void AlbumArt::metacb_ArtUpdated( const wchar_t *filename )
  428. {
  429. // it'd be nice to do this, but we can't guarantee that our file didn't get updated in the process
  430. //const wchar_t *curFn = wa2.GetCurrentFile();
  431. // if (curFn && filename && *filename && *curFn && !_wcsicmp(filename, curFn))
  432. AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() );
  433. }
  434. scriptVar AlbumArt::script_vcpu_refresh( SCRIPT_FUNCTION_PARAMS, ScriptObject *o )
  435. {
  436. SCRIPT_FUNCTION_INIT;
  437. AlbumArt *a = static_cast<AlbumArt *>( o->vcpu_getInterface( albumArtGuid ) );
  438. if ( a )
  439. {
  440. a->forceRefresh = true;
  441. a->corecb_onUrlChange( wa2.GetCurrentFile() );
  442. a->forceRefresh = false;
  443. }
  444. RETURN_SCRIPT_VOID;
  445. }
  446. scriptVar AlbumArt::script_vcpu_isLoading( SCRIPT_FUNCTION_PARAMS, ScriptObject *o )
  447. {
  448. SCRIPT_FUNCTION_INIT;
  449. AlbumArt *a = static_cast<AlbumArt *>( o->vcpu_getInterface( albumArtGuid ) );
  450. if ( a )
  451. {
  452. return MAKE_SCRIPT_BOOLEAN( !!a->isLoading );
  453. }
  454. return MAKE_SCRIPT_BOOLEAN( false );
  455. }
  456. scriptVar AlbumArt::script_vcpu_onAlbumArtLoaded( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar success )
  457. {
  458. SCRIPT_FUNCTION_INIT
  459. PROCESS_HOOKS1( o, albumartController, success );
  460. SCRIPT_FUNCTION_CHECKABORTEVENT;
  461. SCRIPT_EXEC_EVENT1( o, success );
  462. }