/xbmc/cores/dvdplayer/DVDDemuxSPU.cpp

http://github.com/xbmc/xbmc · C++ · 650 lines · 451 code · 92 blank · 107 comment · 77 complexity · a449b019c127cbf382b77e8e4a3251a0 MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2013 Team XBMC
  3. * http://xbmc.org
  4. *
  5. * This Program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2, or (at your option)
  8. * any later version.
  9. *
  10. * This Program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with XBMC; see the file COPYING. If not, see
  17. * <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include "DVDDemuxSPU.h"
  21. #include "DVDClock.h"
  22. #include "utils/log.h"
  23. #undef ALIGN
  24. #define ALIGN(value, alignment) (((value)+((alignment)-1))&~((alignment)-1))
  25. // #define SPU_DEBUG
  26. void DebugLog(const char *format, ...)
  27. {
  28. #ifdef SPU_DEBUG
  29. static char temp_spubuffer[1024];
  30. va_list va;
  31. va_start(va, format);
  32. _vsnprintf(temp_spubuffer, 1024, format, va);
  33. va_end(va);
  34. CLog::Log(LOGDEBUG,temp_spubuffer);
  35. #endif
  36. }
  37. CDVDDemuxSPU::CDVDDemuxSPU()
  38. {
  39. memset(&m_spuData, 0, sizeof(m_spuData));
  40. memset(m_clut, 0, sizeof(m_clut));
  41. m_bHasClut = false;
  42. }
  43. CDVDDemuxSPU::~CDVDDemuxSPU()
  44. {
  45. if (m_spuData.data) free(m_spuData.data);
  46. }
  47. void CDVDDemuxSPU::Reset()
  48. {
  49. FlushCurrentPacket();
  50. // We can't reset this during playback, cause we don't always
  51. // get a new clut from libdvdnav leading to invalid colors
  52. // so let's just never reset it. It will only be reset
  53. // when dvdplayer is destructed and constructed
  54. // m_bHasClut = false;
  55. // memset(m_clut, 0, sizeof(m_clut));
  56. }
  57. void CDVDDemuxSPU::FlushCurrentPacket()
  58. {
  59. if (m_spuData.data) free(m_spuData.data);
  60. memset(&m_spuData, 0, sizeof(m_spuData));
  61. }
  62. CDVDOverlaySpu* CDVDDemuxSPU::AddData(uint8_t* data, int iSize, double pts)
  63. {
  64. SPUData* pSPUData = &m_spuData;
  65. if (pSPUData->iNeededSize > 0 &&
  66. (pSPUData->iSize != pSPUData->iNeededSize) &&
  67. ((pSPUData->iSize + iSize) > pSPUData->iNeededSize))
  68. {
  69. DebugLog("corrupt spu data: packet does not fit");
  70. m_spuData.iNeededSize = 0;
  71. m_spuData.iSize = 0;
  72. return NULL;
  73. }
  74. // check if we are about to start a new packet
  75. if (pSPUData->iSize == pSPUData->iNeededSize)
  76. {
  77. // for now we don't delete the memory assosiated with m_spuData.data
  78. pSPUData->iSize = 0;
  79. // check spu data lenght, only needed / possible in the first spu pakcet
  80. unsigned __int16 length = data[0] << 8 | data[1];
  81. if (length == 0)
  82. {
  83. DebugLog("corrupt spu data: zero packet");
  84. m_spuData.iNeededSize = 0;
  85. m_spuData.iSize = 0;
  86. return NULL;
  87. }
  88. if (length > iSize) pSPUData->iNeededSize = length;
  89. else pSPUData->iNeededSize = iSize;
  90. // set presentation time stamp
  91. if (pts > 0) pSPUData->pts = pts;
  92. }
  93. // allocate data if not already done ( done in blocks off 16384 bytes )
  94. // or allocate some more if 16384 bytes is not enough
  95. if((pSPUData->iSize + iSize) > pSPUData->iAllocatedSize)
  96. pSPUData->data = (uint8_t*)realloc(pSPUData->data, ALIGN(pSPUData->iSize + iSize, 0x4000));
  97. if(!pSPUData->data)
  98. return NULL; // crap realloc failed, this will have leaked some memory due to odd realloc
  99. // add new data
  100. memcpy(pSPUData->data + pSPUData->iSize, data, iSize);
  101. pSPUData->iSize += iSize;
  102. if (pSPUData->iNeededSize - pSPUData->iSize == 1) // to make it even
  103. {
  104. DebugLog("missing 1 byte to complete packet, adding 0xff");
  105. pSPUData->data[pSPUData->iSize] = 0xff;
  106. pSPUData->iSize++;
  107. }
  108. if (pSPUData->iSize == pSPUData->iNeededSize)
  109. {
  110. DebugLog("got complete spu packet\n length: %i bytes\n stream: %i\n", pSPUData->iSize);
  111. return ParsePacket(pSPUData);
  112. }
  113. return NULL;
  114. }
  115. #define CMD_END 0xFF
  116. #define FSTA_DSP 0x00
  117. #define STA_DSP 0x01
  118. #define STP_DSP 0x02
  119. #define SET_COLOR 0x03
  120. #define SET_CONTR 0x04
  121. #define SET_DAREA 0x05
  122. #define SET_DSPXA 0x06
  123. #define CHG_COLCON 0x07
  124. CDVDOverlaySpu* CDVDDemuxSPU::ParsePacket(SPUData* pSPUData)
  125. {
  126. unsigned int alpha[4];
  127. uint8_t* pUnparsedData = NULL;
  128. if (pSPUData->iNeededSize != pSPUData->iSize)
  129. {
  130. DebugLog("GetPacket, packet is incomplete, missing: %i bytes", (pSPUData->iNeededSize - pSPUData->iSize));
  131. }
  132. if (pSPUData->data[pSPUData->iSize - 1] != 0xff)
  133. {
  134. DebugLog("GetPacket, missing end of data 0xff");
  135. }
  136. CDVDOverlaySpu* pSPUInfo = new CDVDOverlaySpu();
  137. uint8_t* p = pSPUData->data; // pointer to walk through all data
  138. // get data length
  139. unsigned __int16 datalength = p[2] << 8 | p[3]; // datalength + 4 control bytes
  140. pUnparsedData = pSPUData->data + 4;
  141. // if it is set to 0 it means it's a menu overlay by defualt
  142. // this is not what we want too, cause you get strange results on a parse error
  143. pSPUInfo->iPTSStartTime = -1;
  144. //skip data packet and goto control sequence
  145. p += datalength;
  146. bool bHasNewDCSQ = true;
  147. while (bHasNewDCSQ)
  148. {
  149. DebugLog(" starting new SP_DCSQT");
  150. // p is beginning of first SP_DCSQT now
  151. unsigned __int16 delay = p[0] << 8 | p[1];
  152. unsigned __int16 next_DCSQ = p[2] << 8 | p[3];
  153. //offset within the Sub-Picture Unit to the next SP_DCSQ. If this is the last SP_DCSQ, it points to itself.
  154. bHasNewDCSQ = ((pSPUData->data + next_DCSQ) != p);
  155. // skip 4 bytes
  156. p += 4;
  157. while (*p != CMD_END && (unsigned int)(p - pSPUData->data) <= pSPUData->iSize)
  158. {
  159. switch (*p)
  160. {
  161. case FSTA_DSP:
  162. p++;
  163. DebugLog(" GetPacket, FSTA_DSP: Forced Start Display, no arguments");
  164. pSPUInfo->iPTSStartTime = pSPUData->pts;
  165. pSPUInfo->iPTSStopTime = 0x9000000000000LL;
  166. pSPUInfo->bForced = true;
  167. // delay is always 0, the dvdplayer should decide when to display the packet (menu highlight)
  168. break;
  169. case STA_DSP:
  170. {
  171. p++;
  172. pSPUInfo->iPTSStartTime = pSPUData->pts;
  173. pSPUInfo->iPTSStartTime += (double)delay * 1024 * DVD_TIME_BASE / 90000;
  174. DebugLog(" GetPacket, STA_DSP: Start Display, delay: %i", ((delay * 1024) / 90000));
  175. }
  176. break;
  177. case STP_DSP:
  178. {
  179. p++;
  180. pSPUInfo->iPTSStopTime = pSPUData->pts;
  181. pSPUInfo->iPTSStopTime += (double)delay * 1024 * DVD_TIME_BASE / 90000;
  182. DebugLog(" GetPacket, STP_DSP: Stop Display, delay: %i", ((delay * 1024) / 90000));
  183. }
  184. break;
  185. case SET_COLOR:
  186. {
  187. p++;
  188. if (m_bHasClut)
  189. {
  190. pSPUInfo->bHasColor = true;
  191. unsigned int idx[4];
  192. // 0, 1, 2, 3
  193. idx[0] = (p[0] >> 4) & 0x0f;
  194. idx[1] = (p[0]) & 0x0f;
  195. idx[2] = (p[1] >> 4) & 0x0f;
  196. idx[3] = (p[1]) & 0x0f;
  197. for (int i = 0; i < 4 ; i++) // emphasis 1, emphasis 2, pattern, back ground
  198. {
  199. uint8_t* iColor = m_clut[idx[i]];
  200. pSPUInfo->color[3 - i][0] = iColor[0]; // Y
  201. pSPUInfo->color[3 - i][1] = iColor[1]; // Cr
  202. pSPUInfo->color[3 - i][2] = iColor[2]; // Cb
  203. }
  204. }
  205. DebugLog(" GetPacket, SET_COLOR:");
  206. p += 2;
  207. }
  208. break;
  209. case SET_CONTR: // alpha
  210. {
  211. p++;
  212. // 3, 2, 1, 0
  213. alpha[0] = (p[0] >> 4) & 0x0f;
  214. alpha[1] = (p[0]) & 0x0f;
  215. alpha[2] = (p[1] >> 4) & 0x0f;
  216. alpha[3] = (p[1]) & 0x0f;
  217. // Ignore blank alpha palette.
  218. if (alpha[0] | alpha[1] | alpha[2] | alpha[3])
  219. {
  220. pSPUInfo->bHasAlpha = true;
  221. // 0, 1, 2, 3
  222. pSPUInfo->alpha[0] = alpha[3]; //0 // background, should be hidden
  223. pSPUInfo->alpha[1] = alpha[2]; //1
  224. pSPUInfo->alpha[2] = alpha[1]; //2 // wm button overlay
  225. pSPUInfo->alpha[3] = alpha[0]; //3
  226. }
  227. DebugLog(" GetPacket, SET_CONTR:");
  228. p += 2;
  229. }
  230. break;
  231. case SET_DAREA:
  232. {
  233. p++;
  234. pSPUInfo->x = (p[0] << 4) | (p[1] >> 4);
  235. pSPUInfo->y = (p[3] << 4) | (p[4] >> 4);
  236. pSPUInfo->width = (((p[1] & 0x0f) << 8) | p[2]) - pSPUInfo->x + 1;
  237. pSPUInfo->height = (((p[4] & 0x0f) << 8) | p[5]) - pSPUInfo->y + 1;
  238. DebugLog(" GetPacket, SET_DAREA: x,y:%i,%i width,height:%i,%i",
  239. pSPUInfo->x, pSPUInfo->y, pSPUInfo->width, pSPUInfo->height);
  240. p += 6;
  241. }
  242. break;
  243. case SET_DSPXA:
  244. {
  245. p++;
  246. unsigned __int16 tfaddr = (p[0] << 8 | p[1]); // offset in packet
  247. unsigned __int16 bfaddr = (p[2] << 8 | p[3]); // offset in packet
  248. pSPUInfo->pTFData = (tfaddr - 4); //pSPUInfo->pData + (tfaddr - 4); // pSPUData->data = packet startaddr - 4
  249. pSPUInfo->pBFData = (bfaddr - 4); //pSPUInfo->pData + (bfaddr - 4); // pSPUData->data = packet startaddr - 4
  250. p += 4;
  251. DebugLog(" GetPacket, SET_DSPXA: tf: %i bf: %i ", tfaddr, bfaddr);
  252. }
  253. break;
  254. case CHG_COLCON:
  255. {
  256. p++;
  257. unsigned __int16 paramlength = p[0] << 8 | p[1];
  258. DebugLog("GetPacket, CHG_COLCON, skippin %i bytes", paramlength);
  259. p += paramlength;
  260. }
  261. break;
  262. default:
  263. DebugLog("GetPacket, error parsing control sequence");
  264. delete pSPUInfo;
  265. return NULL;
  266. break;
  267. }
  268. }
  269. DebugLog(" end off SP_DCSQT");
  270. if (*p == CMD_END) p++;
  271. else
  272. {
  273. DebugLog("GetPacket, end off SP_DCSQT, but did not found 0xff (CMD_END)");
  274. }
  275. }
  276. // parse the rle.
  277. // this should be chnaged so it get's converted to a yuv overlay
  278. return ParseRLE(pSPUInfo, pUnparsedData);
  279. }
  280. /*****************************************************************************
  281. * AddNibble: read a nibble from a source packet and add it to our integer.
  282. *****************************************************************************/
  283. inline unsigned int AddNibble( unsigned int i_code, uint8_t* p_src, unsigned int* pi_index )
  284. {
  285. if ( *pi_index & 0x1 )
  286. {
  287. return ( i_code << 4 | ( p_src[(*pi_index)++ >> 1] & 0xf ) );
  288. }
  289. else
  290. {
  291. return ( i_code << 4 | p_src[(*pi_index)++ >> 1] >> 4 );
  292. }
  293. }
  294. /*****************************************************************************
  295. * ParseRLE: parse the RLE part of the subtitle
  296. *****************************************************************************
  297. * This part parses the subtitle graphical data and stores it in a more
  298. * convenient structure for later decoding. For more information on the
  299. * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
  300. *****************************************************************************/
  301. CDVDOverlaySpu* CDVDDemuxSPU::ParseRLE(CDVDOverlaySpu* pSPU, uint8_t* pUnparsedData)
  302. {
  303. uint8_t* p_src = pUnparsedData;
  304. unsigned int i_code = 0;
  305. unsigned int i_width = pSPU->width;
  306. unsigned int i_height = pSPU->height;
  307. unsigned int i_x, i_y;
  308. // allocate a buffer for the result
  309. unsigned __int16* p_dest = (unsigned __int16*)pSPU->result;
  310. /* The subtitles are interlaced, we need two offsets */
  311. unsigned int i_id = 0; /* Start on the even SPU layer */
  312. unsigned int pi_table[2];
  313. /* Colormap statistics */
  314. int i_border = -1;
  315. int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0;
  316. pi_table[ 0 ] = pSPU->pTFData << 1;
  317. pi_table[ 1 ] = pSPU->pBFData << 1;
  318. for ( i_y = 0 ; i_y < i_height ; i_y++ )
  319. {
  320. unsigned int *pi_offset = pi_table + i_id;
  321. for ( i_x = 0 ; i_x < i_width ; i_x += i_code >> 2 )
  322. {
  323. i_code = AddNibble( 0, p_src, pi_offset );
  324. if ( i_code < 0x04 )
  325. {
  326. i_code = AddNibble( i_code, p_src, pi_offset );
  327. if ( i_code < 0x10 )
  328. {
  329. i_code = AddNibble( i_code, p_src, pi_offset );
  330. if ( i_code < 0x040 )
  331. {
  332. i_code = AddNibble( i_code, p_src, pi_offset );
  333. if ( i_code < 0x0100 )
  334. {
  335. /* If the 14 first bits are set to 0, then it's a
  336. * new line. We emulate it. */
  337. if ( i_code < 0x0004 )
  338. {
  339. i_code |= ( i_width - i_x ) << 2;
  340. }
  341. else
  342. {
  343. /* We have a boo boo ! */
  344. CLog::Log(LOGERROR, "ParseRLE: unknown RLE code 0x%.4x", i_code);
  345. return NULL;
  346. }
  347. }
  348. }
  349. }
  350. }
  351. if ( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
  352. {
  353. CLog::Log(LOGERROR, "ParseRLE: out of bounds, %i at (%i,%i) is out of %ix%i",
  354. i_code >> 2, i_x, i_y, i_width, i_height );
  355. return NULL;
  356. }
  357. // keep trace of all occouring pixels, even keeping the background in mind
  358. stats[i_code & 0x3] += i_code >> 2;
  359. // count the number of pixels for every occouring parts, without background
  360. if (pSPU->alpha[i_code & 0x3] != 0x00)
  361. {
  362. // the last non background pixel is probably the border color
  363. i_border = i_code & 0x3;
  364. stats[i_border] += i_code >> 2;
  365. }
  366. /* Check we aren't overwriting our data range
  367. This occurs on "The Triplets of BelleVille" region 4 disk (NTSC)"
  368. where we use around 96k rather than 64k + 20bytes */
  369. if ((uint8_t *)p_dest >= pSPU->result + sizeof(pSPU->result))
  370. {
  371. CLog::Log(LOGERROR, "ParseRLE: Overrunning our data range. Need %li bytes", (long)((uint8_t *)p_dest - pSPU->result));
  372. return NULL;
  373. }
  374. *p_dest++ = i_code;
  375. }
  376. /* Check that we didn't go too far */
  377. if ( i_x > i_width )
  378. {
  379. CLog::Log(LOGERROR, "ParseRLE: i_x overflowed, %i > %i", i_x, i_width );
  380. return NULL;
  381. }
  382. /* Byte-align the stream */
  383. if ( *pi_offset & 0x1 )
  384. {
  385. (*pi_offset)++;
  386. }
  387. /* Swap fields */
  388. i_id = ~i_id & 0x1;
  389. }
  390. /* We shouldn't get any padding bytes */
  391. if ( i_y < i_height )
  392. {
  393. DebugLog("ParseRLE: padding bytes found in RLE sequence" );
  394. DebugLog("ParseRLE: send mail to <sam@zoy.org> if you want to help debugging this" );
  395. /* Skip them just in case */
  396. while ( i_y < i_height )
  397. {
  398. /* Check we aren't overwriting our data range
  399. This occurs on "The Triplets of BelleVille" region 4 disk (NTSC)"
  400. where we use around 96k rather than 64k + 20bytes */
  401. if ((uint8_t *)p_dest >= pSPU->result + sizeof(pSPU->result))
  402. {
  403. CLog::Log(LOGERROR, "ParseRLE: Overrunning our data range. Need %li bytes", (long)((uint8_t *)p_dest - pSPU->result));
  404. return NULL;
  405. }
  406. *p_dest++ = i_width << 2;
  407. i_y++;
  408. }
  409. return NULL;
  410. }
  411. DebugLog("ParseRLE: valid subtitle, size: %ix%i, position: %i,%i",
  412. pSPU->width, pSPU->height, pSPU->x, pSPU->y );
  413. // forced spu's (menu overlays) retrieve their alpha/color information from InputStreamNavigator::GetCurrentButtonInfo
  414. // also they may contain completely covering data wich is supposed to be hidden normally
  415. // since whole spu is drawn, if this is done for forced, that may be displayed
  416. // so we must trust what is given
  417. if( !pSPU->bForced )
  418. {
  419. // Handle color if no palette was found.
  420. // we only set it if there is a valid i_border color
  421. if (!pSPU->bHasColor)
  422. {
  423. CLog::Log(LOGINFO, "%s - no color palette found, using default", __FUNCTION__);
  424. FindSubtitleColor(i_border, stats, pSPU);
  425. }
  426. // check alpha values, for non forced spu's we use a default value
  427. if (pSPU->bHasAlpha)
  428. {
  429. // check alpha values
  430. // the array stats represents the nr of pixels for each color channel
  431. // thus if there are no pixels to display, we assume the alphas are incorrect.
  432. if (!CanDisplayWithAlphas(pSPU->alpha, stats))
  433. {
  434. CLog::Log(LOGINFO, "%s - no matching color and alpha found, resetting alpha", __FUNCTION__);
  435. pSPU->alpha[0] = 0x00; // back ground
  436. pSPU->alpha[1] = 0x0f;
  437. pSPU->alpha[2] = 0x0f;
  438. pSPU->alpha[3] = 0x0f;
  439. }
  440. }
  441. else
  442. {
  443. CLog::Log(LOGINFO, "%s - ignoring blank alpha palette, using default", __FUNCTION__);
  444. pSPU->alpha[0] = 0x00; // back ground
  445. pSPU->alpha[1] = 0x0f;
  446. pSPU->alpha[2] = 0x0f;
  447. pSPU->alpha[3] = 0x0f;
  448. }
  449. }
  450. return pSPU;
  451. }
  452. void CDVDDemuxSPU::FindSubtitleColor(int last_color, int stats[4], CDVDOverlaySpu* pSPU)
  453. {
  454. const int COLOR_INNER = 0;
  455. const int COLOR_SHADE = 1;
  456. const int COLOR_BORDER = 2;
  457. //uint8_t custom_subtitle_color[4][3] = { // blue, yellow and something else (xine)
  458. // { 0x80, 0x90, 0x80 }, // inner color
  459. // { 0x00, 0x90, 0x00 }, // shade color
  460. // { 0x00, 0x90, 0xff } // border color
  461. //};
  462. uint8_t custom_subtitle_color[4][3] = { // inner color white, gray shading and a black border
  463. { 0xff, 0x80, 0x80 }, // inner color, white
  464. { 0x80, 0x80, 0x80 }, // shade color, gray
  465. { 0x00, 0x80, 0x80 } // border color, black
  466. };
  467. //uint8_t custom_subtitle_color[4][3] = { // completely white and a black border
  468. // { 0xff, 0x80, 0x80 }, // inner color, white
  469. // { 0xff, 0x80, 0x80 }, // shade color, white
  470. // { 0x00, 0x80, 0x80 } // border color, black
  471. //};
  472. int nrOfUsedColors = 0;
  473. for (int i = 0; i < 4; i++)
  474. {
  475. if (pSPU->alpha[i] > 0) nrOfUsedColors++;
  476. }
  477. if (nrOfUsedColors == 0)
  478. {
  479. // nothing todo
  480. DebugLog("FindSubtitleColor: all 4 alpha channels are 0, nothing todo");
  481. }
  482. else if (nrOfUsedColors == 1)
  483. {
  484. // only one color is used, probably the inner color
  485. for (int i = 0; i < 4; i++) // find the position that is used
  486. {
  487. if (pSPU->alpha[i] > 0)
  488. {
  489. pSPU->color[i][0] = custom_subtitle_color[COLOR_INNER][0]; // Y
  490. pSPU->color[i][1] = custom_subtitle_color[COLOR_INNER][1]; // Cr ?
  491. pSPU->color[i][2] = custom_subtitle_color[COLOR_INNER][2]; // Cb ?
  492. return;
  493. }
  494. }
  495. }
  496. else
  497. {
  498. // old code
  499. if (last_color >= 0 && last_color < 4)
  500. {
  501. int i, i_inner = -1, i_shade = -1;
  502. // Set the border color, the last color is probably the border color
  503. pSPU->color[last_color][0] = custom_subtitle_color[COLOR_BORDER][0];
  504. pSPU->color[last_color][1] = custom_subtitle_color[COLOR_BORDER][1];
  505. pSPU->color[last_color][2] = custom_subtitle_color[COLOR_BORDER][2];
  506. stats[last_color] = 0;
  507. // find the inner colors
  508. for ( i = 0 ; i < 4 && i_inner == -1 ; i++ )
  509. {
  510. if ( stats[i] )
  511. {
  512. i_inner = i;
  513. }
  514. }
  515. // try to find the shade color
  516. for ( ; i < 4 && i_shade == -1 ; i++)
  517. {
  518. if ( stats[i] )
  519. {
  520. if ( stats[i] > stats[i_inner] )
  521. {
  522. i_shade = i_inner;
  523. i_inner = i;
  524. }
  525. else
  526. {
  527. i_shade = i;
  528. }
  529. }
  530. }
  531. /* Set the inner color */
  532. if ( i_inner != -1 )
  533. {
  534. // white color
  535. pSPU->color[i_inner][0] = custom_subtitle_color[COLOR_INNER][0]; // Y
  536. pSPU->color[i_inner][1] = custom_subtitle_color[COLOR_INNER][1]; // Cr ?
  537. pSPU->color[i_inner][2] = custom_subtitle_color[COLOR_INNER][2]; // Cb ?
  538. }
  539. /* Set the anti-aliasing color */
  540. if ( i_shade != -1 )
  541. {
  542. // gray
  543. pSPU->color[i_shade][0] = custom_subtitle_color[COLOR_SHADE][0];
  544. pSPU->color[i_shade][1] = custom_subtitle_color[COLOR_SHADE][1];
  545. pSPU->color[i_shade][2] = custom_subtitle_color[COLOR_SHADE][2];
  546. }
  547. DebugLog("ParseRLE: using custom palette (border %i, inner %i, shade %i)", last_color, i_inner, i_shade);
  548. }
  549. }
  550. }
  551. bool CDVDDemuxSPU::CanDisplayWithAlphas(int a[4], int stats[4])
  552. {
  553. return(
  554. a[0] * stats[0] > 0 ||
  555. a[1] * stats[1] > 0 ||
  556. a[2] * stats[2] > 0 ||
  557. a[3] * stats[3] > 0);
  558. }