VobSubImage.cpp
上传用户:xjjlds
上传日期:2015-12-05
资源大小:22823k
文件大小:26k
源码类别:

多媒体编程

开发平台:

Visual C++

  1. /* 
  2.  * Copyright (C) 2003-2005 Gabest
  3.  * http://www.gabest.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 GNU Make; see the file COPYING.  If not, write to
  17.  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
  18.  *  http://www.gnu.org/copyleft/gpl.html
  19.  *
  20.  */
  21. #include "stdafx.h"
  22. #include "VobSubImage.h"
  23. CVobSubImage::CVobSubImage()
  24. {
  25. iLang = iIdx = -1;
  26. fForced = false;
  27. start = delay = 0;
  28. rect = CRect(0,0,0,0);
  29. lpPixels = lpTemp1 = lpTemp2 = NULL;
  30. org = CSize(0,0);
  31. }
  32. CVobSubImage::~CVobSubImage()
  33. {
  34. Free();
  35. }
  36. bool CVobSubImage::Alloc(int w, int h)
  37. {
  38. // if there is nothing to crop TrimSubImage might even add a 1 pixel
  39. // wide border around the text, that's why we need a bit more memory
  40. // to be allocated.
  41. if(lpTemp1 == NULL || w*h > org.cx*org.cy)
  42. {
  43. Free();
  44. lpTemp1 = new RGBQUAD[w*h];
  45. if(!lpTemp1) return(false);
  46. lpTemp2 = new RGBQUAD[(w+2)*(h+2)];
  47. if(!lpTemp2) {delete [] lpTemp1; lpTemp1 = NULL; return(false);}
  48. org.cx = w; 
  49. org.cy = h;
  50. }
  51. lpPixels = lpTemp1;
  52. return(true);
  53. }
  54. void CVobSubImage::Free()
  55. {
  56. if(lpTemp1) delete [] lpTemp1;
  57. lpTemp1 = NULL;
  58. if(lpTemp2) delete [] lpTemp2;
  59. lpTemp2 = NULL;
  60. lpPixels = NULL;
  61. }
  62. bool CVobSubImage::Decode(BYTE* lpData, int packetsize, int datasize,
  63.   bool fCustomPal, 
  64.   int tridx, 
  65.   RGBQUAD* orgpal /*[16]*/, RGBQUAD* cuspal /*[4]*/,
  66.   bool fTrim)
  67. {
  68. GetPacketInfo(lpData, packetsize, datasize);
  69. if(!Alloc(rect.Width(), rect.Height())) return(false);
  70. lpPixels = lpTemp1;
  71. nPlane = 0;
  72. fAligned = 1;
  73. this->fCustomPal = fCustomPal;
  74. this->orgpal = orgpal;
  75. this->tridx = tridx;
  76. this->cuspal = cuspal;
  77. CPoint p(rect.left, rect.top);
  78. int end0 = nOffset[1];
  79. int end1 = datasize;
  80. while((nPlane == 0 && nOffset[0] < end0) || (nPlane == 1 && nOffset[1] < end1))
  81. {
  82. DWORD code;
  83. if((code = GetNibble(lpData)) >= 0x4
  84. || (code = (code << 4) | GetNibble(lpData)) >= 0x10
  85. || (code = (code << 4) | GetNibble(lpData)) >= 0x40
  86. || (code = (code << 4) | GetNibble(lpData)) >= 0x100)
  87. {
  88. DrawPixels(p, code >> 2, code & 3);
  89. if((p.x += code >> 2) < rect.right) continue;
  90. }
  91. DrawPixels(p, rect.right - p.x, code & 3);
  92. if(!fAligned) GetNibble(lpData); // align to byte
  93. p.x = rect.left;
  94. p.y++;
  95. nPlane = 1 - nPlane;
  96. }
  97. rect.bottom = min(p.y, rect.bottom);
  98. if(fTrim) TrimSubImage();
  99. return(true);
  100. }
  101. void CVobSubImage::GetPacketInfo(BYTE* lpData, int packetsize, int datasize)
  102. {
  103. // delay = 0;
  104. int i, nextctrlblk = datasize;
  105. WORD pal, tr;
  106. do
  107. {
  108. i = nextctrlblk;
  109. int t = (lpData[i] << 8) | lpData[i+1]; i += 2;
  110. nextctrlblk = (lpData[i] << 8) | lpData[i+1]; i += 2;
  111. if(nextctrlblk > packetsize || nextctrlblk < datasize)
  112. {
  113. ASSERT(0);
  114. return;
  115. }
  116. bool fBreak = false;
  117. while(!fBreak)
  118. {
  119. int len = 0;
  120. switch(lpData[i])
  121. {
  122. case 0x00: len = 0; break;
  123. case 0x01: len = 0; break;
  124. case 0x02: len = 0; break;
  125. case 0x03: len = 2; break;
  126. case 0x04: len = 2; break;
  127. case 0x05: len = 6; break;
  128. case 0x06: len = 4; break;
  129. default: len = 0; break;
  130. }
  131. if(i+len >= packetsize)
  132. {
  133. TRACE(_T("Warning: Wrong subpicture parameter block endingn"));
  134. break;
  135. }
  136. switch(lpData[i++])
  137. {
  138. case 0x00: // forced start displaying
  139. fForced = true;
  140. break;
  141. case 0x01: // start displaying
  142. fForced = false;
  143. break;
  144. case 0x02: // stop displaying
  145. delay = 1024 * t / 90;
  146. break;
  147. case 0x03:
  148. pal = (lpData[i] << 8) | lpData[i+1]; i += 2;
  149. break;
  150. case 0x04:
  151. tr = (lpData[i] << 8) | lpData[i+1]; i += 2;
  152. //tr &= 0x00f0;
  153. break;
  154. case 0x05:
  155. rect = CRect((lpData[i] << 4) + (lpData[i+1] >> 4), 
  156. (lpData[i+3] << 4) + (lpData[i+4] >> 4), 
  157. ((lpData[i+1] & 0x0f) << 8) + lpData[i+2] + 1, 
  158. ((lpData[i+4] & 0x0f) << 8) + lpData[i+5] + 1);
  159. i += 6;
  160. break;
  161. case 0x06:
  162. nOffset[0] = (lpData[i] << 8) + lpData[i+1]; i += 2;
  163. nOffset[1] = (lpData[i] << 8) + lpData[i+1]; i += 2;
  164. break;
  165. case 0xff: // end of ctrlblk
  166. fBreak = true;
  167. continue;
  168. default: // skip this ctrlblk
  169. fBreak = true;
  170. break;
  171. }
  172. }
  173. }
  174. while(i <= nextctrlblk && i < packetsize);
  175. for(i = 0; i < 4; i++) 
  176. {
  177. this->pal[i].pal = (pal >> (i << 2)) & 0xf;
  178. this->pal[i].tr = (tr >> (i << 2)) & 0xf;
  179. }
  180. }
  181. BYTE CVobSubImage::GetNibble(BYTE* lpData)
  182. {
  183. WORD& off = nOffset[nPlane];
  184. BYTE ret = (lpData[off] >> (fAligned << 2)) & 0x0f;
  185. fAligned = !fAligned;
  186. off += fAligned;
  187. return(ret);
  188. }
  189. void CVobSubImage::DrawPixels(CPoint p, int length, int colorid)
  190. {
  191. if(length <= 0
  192. || p.x + length < rect.left
  193. || p.x >= rect.right
  194. || p.y < rect.top
  195. || p.y >= rect.bottom) 
  196. {
  197. return;
  198. }
  199. if(p.x < rect.left) p.x = rect.left;
  200. if(p.x + length >= rect.right) length = rect.right - p.x;
  201. RGBQUAD* ptr = &lpPixels[rect.Width() * (p.y - rect.top) + (p.x - rect.left)];
  202. RGBQUAD c;
  203. if(!fCustomPal) 
  204. {
  205. c = orgpal[pal[colorid].pal];
  206. c.rgbReserved = (pal[colorid].tr<<4)|pal[colorid].tr;
  207. }
  208. else
  209. {
  210. c = cuspal[colorid];
  211. }
  212. while(length-- > 0) *ptr++ = c;
  213. }
  214. void CVobSubImage::TrimSubImage()
  215. {
  216. CRect r;
  217. r.left = rect.Width();
  218. r.top = rect.Height();
  219. r.right = 0;
  220. r.bottom = 0;
  221. RGBQUAD* ptr = lpTemp1;
  222. for(int j = 0, y = rect.Height(); j < y; j++)
  223. {
  224. for(int i = 0, x = rect.Width(); i < x; i++, ptr++)
  225. {
  226. if(ptr->rgbReserved)
  227. {
  228. if(r.top > j) r.top = j;
  229. if(r.bottom < j) r.bottom = j;
  230. if(r.left > i) r.left = i; 
  231. if(r.right < i) r.right = i; 
  232. }
  233. }
  234. }
  235. if(r.left > r.right || r.top > r.bottom) return;
  236. r += CRect(0, 0, 1, 1);
  237. r &= CRect(CPoint(0,0), rect.Size());
  238. int w = r.Width(), h = r.Height();
  239. DWORD offset = r.top*rect.Width() + r.left;
  240. r += CRect(1, 1, 1, 1);
  241. DWORD* src = (DWORD*)&lpTemp1[offset];
  242. DWORD* dst = (DWORD*)&lpTemp2[1 + w + 1];
  243. memset(lpTemp2, 0, (1 + w + 1)*sizeof(RGBQUAD));
  244. for(int height = h; height; height--, src += rect.Width())
  245. {
  246. *dst++ = 0;
  247. memcpy(dst, src, w*sizeof(RGBQUAD)); dst += w; 
  248. *dst++ = 0;
  249. }
  250. memset(dst, 0, (1 + w + 1)*sizeof(RGBQUAD));
  251. lpPixels = lpTemp2;
  252. rect = r + rect.TopLeft();
  253. }
  254. ////////////////////////////////
  255. #include "RTS.h"
  256. #include <math.h>
  257. #define GP(xx, yy) (((xx) < 0 || (yy) < 0 || (xx) >= w || (yy) >= h) ? 0 : p[(yy)*w+(xx)])
  258. COutlineList* CVobSubImage::GetOutlineList(CPoint& topleft)
  259. {
  260. int w = rect.Width(), h = rect.Height(), len = w*h;
  261. if(len <= 0) return NULL;
  262. CAutoVectorPtr<BYTE> p;
  263. if(!p.Allocate(len)) return NULL;
  264. COutlineList* ol = new COutlineList();
  265. if(!ol) return NULL;
  266. BYTE* cp = p;
  267. RGBQUAD* rgbp = (RGBQUAD*)lpPixels;
  268. for(int i = 0; i < len; i++, cp++, rgbp++)
  269. *cp = !!rgbp->rgbReserved;
  270. enum {UP, RIGHT, DOWN, LEFT};
  271. topleft.x = topleft.y = INT_MAX;
  272. while(1)
  273. {
  274. cp = p;
  275. int x, y; 
  276. for(y = 0; y < h; y++)
  277. {
  278. for(x = 0; x < w-1; x++, cp++)
  279. {
  280. if(cp[0] == 0 && cp[1] == 1) break;
  281. }
  282. if(x < w-1) break;
  283. cp++;
  284. }
  285. if(y == h) break;
  286. int prevdir, dir = UP;
  287. int ox = x, oy = y, odir = dir;
  288. CAutoPtr<COutline> o(new COutline);
  289. if(!o) break;
  290. do
  291. {
  292. CPoint pp;
  293. BYTE fl, fr, br;
  294. prevdir = dir;
  295. switch(prevdir)
  296. {
  297. case UP:
  298. pp = CPoint(x+1, y);
  299. fl = GP(x, y-1);
  300. fr = GP(x+1, y-1);
  301. br = GP(x+1, y);
  302. break;
  303. case RIGHT:
  304. pp = CPoint(x+1, y+1);
  305. fl = GP(x+1, y);
  306. fr = GP(x+1, y+1);
  307. br = GP(x, y+1);
  308. break;
  309. case DOWN:
  310. pp = CPoint(x, y+1);
  311. fl = GP(x, y+1);
  312. fr = GP(x-1, y+1);
  313. br = GP(x-1, y);
  314. break;
  315. case LEFT:
  316. pp = CPoint(x, y);
  317. fl = GP(x-1, y);
  318. fr = GP(x-1, y-1);
  319. br = GP(x, y-1);
  320. break;
  321. }
  322. // turning left if:
  323. // o . | o .
  324. // ^ o | < o
  325. // turning right if:
  326. // x x | x >
  327. // ^ o | x o
  328. //
  329. // o set, x empty, . can be anything
  330. if(fl==1) dir = (dir-1+4)&3;
  331. else if(fl!=1 && fr!=1 && br==1) dir = (dir+1)&3;
  332. else if(p[y*w+x]&16) {ASSERT(0); break;} // we are going around in one place (this must not happen if the starting conditions were correct)
  333. p[y*w+x] = (p[y*w+x]<<1) | 2; // increase turn count (== log2(highordbit(*p)))
  334. switch(dir)
  335. {
  336. case UP:
  337. if(prevdir == LEFT) {x--; y--;}
  338. if(prevdir == UP) y--;
  339. break;
  340. case RIGHT:
  341. if(prevdir == UP) {x++; y--;}
  342. if(prevdir == RIGHT) x++;
  343. break;
  344. case DOWN:
  345. if(prevdir == RIGHT) {x++; y++;}
  346. if(prevdir == DOWN) y++;
  347. break;
  348. case LEFT:
  349. if(prevdir == DOWN) {x--; y++;}
  350. if(prevdir == LEFT) x--;
  351. break;
  352. }
  353. int d = dir - prevdir;
  354. o->Add(pp, d == 3 ? -1 : d == -3 ? 1 : d);
  355. if(topleft.x > pp.x) topleft.x = pp.x;
  356. if(topleft.y > pp.y) topleft.y = pp.y;
  357. }
  358. while(!(x == ox && y == oy && dir == odir));
  359. if(o->pa.GetSize() > 0 && (x == ox && y == oy && dir == odir)) 
  360. {
  361. ol->AddTail(o);
  362. }
  363. else
  364. {
  365. ASSERT(0);
  366. }
  367. }
  368. return(ol);
  369. }
  370. static bool FitLine(COutline& o, int& start, int& end)
  371. {
  372. int len = o.pa.GetSize();
  373. if(len < 7) return(false); // small segments should be handled with beziers...
  374. for(start = 0; start < len && !o.da[start]; start++);
  375. for(end = len-1; end > start && !o.da[end]; end--);
  376. if(end-start < 8 || end-start < (len-end)+(start-0)) return(false);
  377. CUIntArray la, ra;
  378. int i, j, k;
  379. for(i = start+1, j = end, k = start; i <= j; i++)
  380. {
  381. if(!o.da[i]) continue;
  382. if(o.da[i] == o.da[k]) return(false);
  383. if(o.da[i] == -1) la.Add(i-k);
  384. else ra.Add(i-k);
  385. k = i;
  386. }
  387. bool fl = true, fr = true;
  388. // these tests are completly heuristic and might be redundant a bit...
  389. for(i = 0, j = la.GetSize(); i < j && fl; i++) {if(la[i] != 1) fl = false;} 
  390. for(i = 0, j = ra.GetSize(); i < j && fr; i++) {if(ra[i] != 1) fr = false;}
  391. if(!fl && !fr) return(false); // can't be a line if there are bigger steps than one in both directions (lines are usually drawn by stepping one either horizontally or vertically)
  392. if(fl && fr && 1.0*(end-start)/((len-end)*2+(start-0)*2) > 0.4) return(false); // if this section is relatively too small it may only be a rounded corner
  393. if(!fl && la.GetSize() > 0 && la.GetSize() <= 4 && (la[0] == 1 && la[la.GetSize()-1] == 1)) return(false); // one step at both ends, doesn't sound good for a line (may be it was skewed, so only eliminate smaller sections where beziers going to look just as good)
  394. if(!fr && ra.GetSize() > 0 && ra.GetSize() <= 4 && (ra[0] == 1 && ra[ra.GetSize()-1] == 1)) return(false); // -''-
  395. CUIntArray& a = !fl ? la : ra;
  396. len = a.GetSize();
  397. int sum = 0;
  398. for(i = 0, j = INT_MAX, k = 0; i < len; i++)
  399. {
  400. if(j > a[i]) j = a[i];
  401. if(k < a[i]) k = a[i];
  402. sum += a[i];
  403. }
  404. if(k - j > 2 && 1.0*sum/len < 2) return(false);
  405. if(k - j > 2 && 1.0*sum/len >= 2 && len < 4) return(false);
  406. if((la.GetSize()/2+ra.GetSize()/2)/2 <= 2)
  407. {
  408. if((k+j)/2 < 2 && k*j!=1) return(false);
  409. }
  410. double err = 0;
  411. CPoint sp = o.pa[start], ep = o.pa[end];
  412. double minerr = 0, maxerr = 0;
  413. double vx = ep.x - sp.x, vy = ep.y - sp.y, l = sqrt(vx*vx+vy*vy);
  414. vx /= l; vy /= l;
  415. for(i = start+1, j = end-1; i <= j; i++)
  416. {
  417. CPoint p = o.pa[i], dp = p - sp;
  418. double t = vx*dp.x+vy*dp.y, dx = vx*t + sp.x - p.x, dy = vy*t + sp.y - p.y;
  419. t = dx*dx+dy*dy;
  420. err += t;
  421. t = sqrt(t);
  422. if(vy*dx-dy*vx < 0) {if(minerr > -t) minerr = -t;}
  423. else {if(maxerr < t) maxerr = t;}
  424. }
  425. return((maxerr-minerr)/l < 0.1  || err/l < 1.5 || (fabs(maxerr) < 8 && fabs(minerr) < 8));
  426. }
  427. static int CalcPossibleCurveDegree(COutline& o)
  428. {
  429. int len2 = o.da.GetSize();
  430. CUIntArray la;
  431. for(int i = 0, j = 0; j < len2; j++)
  432. {
  433. if(j == len2-1 || o.da[j])
  434. {
  435. la.Add(j-i);
  436. i = j;
  437. }
  438. }
  439. int len = la.GetSize();
  440. int ret = 0;
  441. // check if we can find a reason to add a penalty degree, or two :P
  442. // it is mainly about looking for distant corners
  443. {
  444. int penalty = 0;
  445. int ma[2] = {0, 0};
  446. for(int i = 0; i < len; i++) ma[i&1] += la[i];
  447. int ca[2] = {ma[0], ma[1]};
  448. for(int i = 0; i < len; i++) 
  449. {
  450. ca[i&1] -= la[i];
  451. double c1 = 1.0*ca[0]/ma[0], c2 = 1.0*ca[1]/ma[1], c3 = 1.0*la[i]/ma[i&1];
  452. if(len2 > 16 && (fabs(c1-c2) > 0.7 || (c3 > 0.6 && la[i] > 5)))
  453. {penalty = 2; break;}
  454. if(fabs(c1-c2) > 0.6 || (c3 > 0.4 && la[i] > 5))
  455. {penalty = 1;}
  456. }
  457. ret += penalty;
  458. }
  459. la[0] <<= 1;
  460. la[len-1] <<= 1;
  461. for(int i = 0; i < len; i+=2)
  462. {
  463. if(la[i] > 1) {ret++; i--;} // prependicular to the last chosen section and bigger then 1 -> add a degree and continue with the other dir
  464. }
  465. return(ret);
  466. }
  467. inline double vectlen(CPoint p)
  468. {
  469. return(sqrt((double)(p.x*p.x+p.y*p.y)));
  470. }
  471. inline double vectlen(CPoint p1, CPoint p2)
  472. {
  473. return(vectlen(p2 - p1));
  474. }
  475. static bool MinMaxCosfi(COutline& o, double& mincf, double& maxcf) // not really cosfi, it is weighted by the distance from the segment endpoints, and since it would be always between -1 and 0, the applied sign marks side 
  476. {
  477. CPointArray& pa = o.pa;
  478. int len = (int)pa.GetSize();
  479. if(len < 6) return(false);
  480. mincf = 1;
  481. maxcf = -1;
  482. CPoint p = pa[len-1] - pa[0];
  483. double l = vectlen(p);
  484. for(int i = 2; i < len-2; i++) // skip the endpoints, they aren't accurate
  485. {
  486. CPoint p1 = pa[0] - pa[i], p2 = pa[len-1] - pa[i];
  487. double l1 = vectlen(p1), l2 = vectlen(p2);
  488. int sign = p1.x*p.y-p1.y*p.x >= 0 ? 1 : -1;
  489. double c = (1.0*len/2 - fabs(i - 1.0*len/2)) / len * 2; // c: 0 -> 1 -> 0
  490. double cosfi = (1+(p1.x*p2.x+p1.y*p2.y)/(l1*l2)) * sign * c;
  491. if(mincf > cosfi) mincf = cosfi;
  492. if(maxcf < cosfi) maxcf = cosfi;
  493. }
  494. return(true);
  495. }
  496. static bool FitBezierVH(COutline& o, CPoint& p1, CPoint& p2)
  497. {
  498. int i;
  499. CPointArray& pa = o.pa;
  500. int len = (int)pa.GetSize();
  501. if(len <= 1)
  502. {
  503. return(false);
  504. }
  505. else if(len == 2)
  506. {
  507. CPoint mid = pa[0]+pa[1];
  508. mid.x >>= 1;
  509. mid.y >>= 1;
  510. p1 = p2 = mid;
  511. return(true);
  512. }
  513. CPoint dir1 = pa[1] - pa[0], dir2 = pa[len-2] - pa[len-1];
  514. if((dir1.x&&dir1.y)||(dir2.x&&dir2.y)) 
  515. return(false); // we are only fitting beziers with hor./ver. endings
  516. if(CalcPossibleCurveDegree(o) > 3) 
  517. return(false);
  518. double mincf, maxcf;
  519. if(MinMaxCosfi(o, mincf, maxcf))
  520. {
  521. if(maxcf-mincf > 0.8 
  522. || maxcf-mincf > 0.6 && (maxcf >= 0.4 || mincf <= -0.4)) 
  523. return(false);
  524. }
  525. CPoint p0 = p1 = pa[0];
  526. CPoint p3 = p2 = pa[len-1];
  527. CArray<double,double&> pl;
  528. pl.SetSize(len);
  529. double c10 = 0, c11 = 0, c12 = 0, c13 = 0, c1x = 0, c1y = 0;
  530. double c20 = 0, c21 = 0, c22 = 0, c23 = 0, c2x = 0, c2y = 0;
  531. double length = 0;
  532. for(pl[0] = 0, i = 1; i < len; i++)
  533. {
  534. CPoint diff = (pa[i] - pa[i-1]);
  535. pl[i] = (length += sqrt((double)(diff.x*diff.x+diff.y*diff.y)));
  536. }
  537. for(i = 0; i < len; i++) 
  538. {
  539. double t1 = pl[i] / length;
  540. double t2 = t1*t1;
  541. double t3 = t2*t1;
  542. double it1 = 1 - t1;
  543. double it2 = it1*it1;
  544. double it3 = it2*it1;
  545. double dc1 = 3.0*it2*t1;
  546. double dc2 = 3.0*it1*t2;
  547. c10 += it3*dc1;
  548. c11 += dc1*dc1;
  549. c12 += dc2*dc1;
  550. c13 += t3*dc1;
  551. c1x += pa[i].x*dc1;
  552. c1y += pa[i].y*dc1;
  553. c20 += it3*dc2;
  554. c21 += dc1*dc2;
  555. c22 += dc2*dc2;
  556. c23 += t3*dc2;
  557. c2x += pa[i].x*dc2;
  558. c2y += pa[i].y*dc2;
  559. }
  560. if(dir1.y == 0 && dir2.x == 0)
  561. {
  562. p1.x = (int)((c1x - c10*p0.x - c12*p3.x - c13*p3.x) / c11 + 0.5);
  563. p2.y = (int)((c2y - c20*p0.y - c21*p0.y - c23*p3.y) / c22 + 0.5);
  564. }
  565. else if(dir1.x == 0 && dir2.y == 0)
  566. {
  567. p2.x = (int)((c2x - c20*p0.x - c21*p0.x - c23*p3.x) / c22 + 0.5);
  568. p1.y = (int)((c1y - c10*p0.y - c12*p3.y - c13*p3.y) / c11 + 0.5);
  569. }
  570. else if(dir1.y == 0 && dir2.y == 0)
  571. {
  572. // cramer's rule
  573. double D = c11*c22 - c12*c21;
  574. p1.x = (int)(((c1x-c10*p0.x-c13*p3.x)*c22 - c12*(c2x-c20*p0.x-c23*p3.x)) / D + 0.5);
  575. p2.x = (int)((c11*(c2x-c20*p0.x-c23*p3.x) - (c1x-c10*p0.x-c13*p3.x)*c21) / D + 0.5);
  576. }
  577. else if(dir1.x == 0 && dir2.x == 0)
  578. {
  579. // cramer's rule
  580. double D = c11*c22 - c12*c21;
  581. p1.y = (int)(((c1y-c10*p0.y-c13*p3.y)*c22 - c12*(c2y-c20*p0.y-c23*p3.y)) / D + 0.5);
  582. p2.y = (int)((c11*(c2y-c20*p0.y-c23*p3.y) - (c1y-c10*p0.y-c13*p3.y)*c21) / D + 0.5);
  583. }
  584. else // must not happen
  585. {
  586. ASSERT(0); 
  587. return(false);
  588. }
  589. // check for "inside-out" beziers
  590. CPoint dir3 = p1 - p0, dir4 = p2 - p3;
  591. if((dir1.x*dir3.x+dir1.y*dir3.y) <= 0 || (dir2.x*dir4.x+dir2.y*dir4.y) <= 0)
  592. return(false);
  593. return(true);
  594. }
  595. int CVobSubImage::GrabSegment(int start, COutline& o, COutline& ret)
  596. {
  597. ret.RemoveAll();
  598. int len = o.pa.GetSize();
  599. int cur = (start)%len, first = -1, last = -1;
  600. int curDir = 0, lastDir = 0;
  601. for(int i = 0; i < len; i++)
  602. {
  603. cur = (cur+1)%len;
  604. if(o.da[cur] == 0) continue;
  605. if(first == -1) first = cur;
  606. if(lastDir == o.da[cur])
  607. {
  608. CPoint startp = o.pa[first]+o.pa[start]; startp.x >>= 1; startp.y >>= 1;
  609. CPoint endp = o.pa[last]+o.pa[cur]; endp.x >>= 1; endp.y >>= 1;
  610. if(first < start) first += len;
  611. start = ((start+first)>>1)+1;
  612. if(start >= len) start -= len;
  613. if(cur < last) cur += len;
  614. cur = ((last+cur+1)>>1);
  615. if(cur >= len) cur -= len;
  616. ret.Add(startp, 0);
  617. while(start != cur)
  618. {
  619. ret.Add(o.pa[start], o.da[start]);
  620. start++;
  621. if(start >= len) start -= len;
  622. }
  623. ret.Add(endp, 0);
  624. return(last);
  625. }
  626. lastDir = o.da[cur];
  627. last = cur;
  628. }
  629. ASSERT(0);
  630. return(start);
  631. }
  632. void CVobSubImage::SplitOutline(COutline& o, COutline& o1, COutline& o2)
  633. {
  634. int len = o.pa.GetSize();
  635. if(len < 4) return;
  636. CUIntArray la, sa, ea;
  637. int i, j, k;
  638. for(i = 0, j = 0; j < len; j++)
  639. {
  640. if(j == len-1 || o.da[j])
  641. {
  642. la.Add(j-i);
  643. sa.Add(i);
  644. ea.Add(j);
  645. i = j;
  646. }
  647. }
  648. int maxlen = 0, maxidx = -1;
  649. int maxlen2 = 0, maxidx2 = -1;
  650. for(i = 0; i < la.GetSize(); i++)
  651. {
  652. if(maxlen < la[i])
  653. {
  654. maxlen = la[i];
  655. maxidx = i;
  656. }
  657. if(maxlen2 < la[i] && i > 0 && i < la.GetSize()-1)
  658. {
  659. maxlen2 = la[i];
  660. maxidx2 = i;
  661. }
  662. }
  663. if(maxlen == maxlen2) maxidx = maxidx2; // if equal choose the inner section
  664. j = (sa[maxidx] + ea[maxidx]) >> 1, k = (sa[maxidx] + ea[maxidx] + 1) >> 1;
  665. o1.RemoveAll();
  666. o2.RemoveAll();
  667. for(i = 0; i <= j; i++)
  668. o1.Add(o.pa[i], o.da[i]);
  669. if(j != k)
  670. {
  671. CPoint mid = o.pa[j]+o.pa[k]; mid.x >>= 1; mid.y >>= 1;
  672. o1.Add(mid, 0);
  673. o2.Add(mid, 0);
  674. }
  675. for(i = k; i < len; i++)
  676. o2.Add(o.pa[i], o.da[i]);
  677. }
  678. void CVobSubImage::AddSegment(COutline& o, CByteArray& pathTypes, CPointArray& pathPoints)
  679. {
  680. int i, len = o.pa.GetSize();
  681. if(len < 3) return;
  682. int nLeftTurns = 0, nRightTurns = 0;
  683. for(i = 0; i < len; i++)
  684. {
  685. if(o.da[i] == -1) nLeftTurns++;
  686. else if(o.da[i] == 1) nRightTurns++;
  687. }
  688. if(nLeftTurns == 0 && nRightTurns == 0) // line
  689. {
  690. pathTypes.Add(PT_LINETO);
  691. pathPoints.Add(o.pa[len-1]);
  692. return;
  693. }
  694. if(nLeftTurns == 0 || nRightTurns == 0) // b-spline
  695. {
  696. pathTypes.Add(PT_MOVETONC);
  697. pathPoints.Add(o.pa[0]+(o.pa[0]-o.pa[1]));
  698. for(i = 0; i < 3; i++)
  699. {
  700. pathTypes.Add(PT_BSPLINETO);
  701. pathPoints.Add(o.pa[i]);
  702. }
  703. for(; i < len; i++)
  704. {
  705. pathTypes.Add(PT_BSPLINEPATCHTO);
  706. pathPoints.Add(o.pa[i]);
  707. }
  708. pathTypes.Add(PT_BSPLINEPATCHTO);
  709. pathPoints.Add(o.pa[len-1]+(o.pa[len-1]-o.pa[len-2]));
  710. pathTypes.Add(PT_MOVETONC);
  711. pathPoints.Add(o.pa[len-1]);
  712. return;
  713. }
  714. int start, end;
  715. if(FitLine(o, start, end)) // b-spline, line, b-spline
  716. {
  717. pathTypes.Add(PT_MOVETONC);
  718. pathPoints.Add(o.pa[0]+(o.pa[0]-o.pa[1]));
  719. pathTypes.Add(PT_BSPLINETO);
  720. pathPoints.Add(o.pa[0]);
  721. pathTypes.Add(PT_BSPLINETO);
  722. pathPoints.Add(o.pa[1]);
  723. CPoint p[4], pp, d = o.pa[end] - o.pa[start];
  724. double l = sqrt((double)(d.x*d.x+d.y*d.y)), dx = 1.0 * d.x / l, dy = 1.0 * d.y / l;
  725. pp = o.pa[start]-o.pa[start-1];
  726. double l1 = abs(pp.x)+abs(pp.y);
  727. pp = o.pa[end]-o.pa[end+1];
  728. double l2 = abs(pp.x)+abs(pp.y);
  729. p[0] = CPoint((int)(1.0 * o.pa[start].x + dx*l1 + 0.5), (int)(1.0 * o.pa[start].y + dy*l1 + 0.5));
  730. p[1] = CPoint((int)(1.0 * o.pa[start].x + dx*l1*2 + 0.5), (int)(1.0 * o.pa[start].y + dy*l1*2 + 0.5));
  731. p[2] = CPoint((int)(1.0 * o.pa[end].x - dx*l2*2 + 0.5), (int)(1.0 * o.pa[end].y - dy*l2*2 + 0.5));
  732. p[3] = CPoint((int)(1.0 * o.pa[end].x - dx*l2 + 0.5), (int)(1.0 * o.pa[end].y - dy*l2 + 0.5));
  733. if(start == 1)
  734. {
  735. pathTypes.Add(PT_BSPLINETO);
  736. pathPoints.Add(p[0]);
  737. }
  738. else
  739. {
  740. pathTypes.Add(PT_BSPLINETO);
  741. pathPoints.Add(o.pa[2]);
  742. for(int i = 3; i <= start; i++)
  743. {
  744. pathTypes.Add(PT_BSPLINEPATCHTO);
  745. pathPoints.Add(o.pa[i]);
  746. }
  747. pathTypes.Add(PT_BSPLINEPATCHTO);
  748. pathPoints.Add(p[0]);
  749. }
  750. pathTypes.Add(PT_BSPLINEPATCHTO);
  751. pathPoints.Add(p[1]);
  752. pathTypes.Add(PT_MOVETONC);
  753. pathPoints.Add(p[0]);
  754. pathTypes.Add(PT_LINETO);
  755. pathPoints.Add(p[3]);
  756. pathTypes.Add(PT_MOVETONC);
  757. pathPoints.Add(p[2]);
  758. pathTypes.Add(PT_BSPLINEPATCHTO);
  759. pathPoints.Add(p[3]);
  760. for(i = end; i < len; i++)
  761. {
  762. pathTypes.Add(PT_BSPLINEPATCHTO);
  763. pathPoints.Add(o.pa[i]);
  764. }
  765. pathTypes.Add(PT_BSPLINEPATCHTO);
  766. pathPoints.Add(o.pa[len-1]+(o.pa[len-1]-o.pa[len-2]));
  767. pathTypes.Add(PT_MOVETONC);
  768. pathPoints.Add(o.pa[len-1]);
  769. return;
  770. }
  771. CPoint p1, p2;
  772. if(FitBezierVH(o, p1, p2)) // bezier
  773. {
  774. pathTypes.Add(PT_BEZIERTO);
  775. pathPoints.Add(p1);
  776. pathTypes.Add(PT_BEZIERTO);
  777. pathPoints.Add(p2);
  778. pathTypes.Add(PT_BEZIERTO);
  779. pathPoints.Add(o.pa[o.pa.GetSize()-1]);
  780. return;
  781. }
  782. COutline o1, o2;
  783. SplitOutline(o, o1, o2);
  784. AddSegment(o1, pathTypes, pathPoints);
  785. AddSegment(o2, pathTypes, pathPoints);
  786. }
  787. bool CVobSubImage::Polygonize(CByteArray& pathTypes, CPointArray& pathPoints, bool fSmooth, int scale)
  788. {
  789. CPoint topleft;
  790. CAutoPtr<COutlineList> ol(GetOutlineList(topleft));
  791. if(!ol) return(false);
  792. POSITION pos;
  793. pos = ol->GetHeadPosition();
  794. while(pos)
  795. {
  796. CPointArray& pa = ol->GetNext(pos)->pa;
  797. for(int i = 0; i < pa.GetSize(); i++)
  798. {
  799. pa[i].x = (pa[i].x-topleft.x)<<scale;
  800. pa[i].y = (pa[i].y-topleft.y)<<scale;
  801. }
  802. }
  803. pos = ol->GetHeadPosition();
  804. while(pos)
  805. {
  806. COutline& o = *ol->GetNext(pos), o2;
  807. if(fSmooth)
  808. {
  809. int i = 0, iFirst = -1;
  810. while(1)
  811. {
  812. i = GrabSegment(i, o, o2);
  813. if(i == iFirst) break;
  814. if(iFirst < 0) 
  815. {
  816. iFirst = i;
  817. pathTypes.Add(PT_MOVETO);
  818. pathPoints.Add(o2.pa[0]);
  819. }
  820. AddSegment(o2, pathTypes, pathPoints);
  821. }
  822. }
  823. else
  824. {
  825. /*
  826. for(int i = 1, len = o.pa.GetSize(); i < len; i++)
  827. {
  828.                 if(int dir = o.da[i-1])
  829. {
  830. CPoint dir2 = o.pa[i] - o.pa[i-1];
  831. dir2.x /= 2; dir2.y /= 2;
  832. CPoint dir1 = dir > 0 ? CPoint(dir2.y, -dir2.x) : CPoint(-dir2.y, dir2.x);
  833. i = i;
  834. o.pa[i-1] -= dir1;
  835. o.pa.InsertAt(i, o.pa[i-1] + dir2);
  836. o.da.InsertAt(i, -dir);
  837. o.pa.InsertAt(i+1, o.pa[i] + dir1);
  838. o.da.InsertAt(i+1, dir);
  839. i += 2;
  840. len += 2;
  841. }
  842. }
  843. */
  844. pathTypes.Add(PT_MOVETO);
  845. pathPoints.Add(o.pa[0]);
  846. for(int i = 1, len = o.pa.GetSize(); i < len; i++)
  847. {
  848. pathTypes.Add(PT_LINETO);
  849. pathPoints.Add(o.pa[i]);
  850. }
  851. }
  852. }
  853. return(pathTypes.GetSize() > 0);
  854. }
  855. bool CVobSubImage::Polygonize(CStringW& assstr, bool fSmooth, int scale)
  856. {
  857. CByteArray pathTypes;
  858. CPointArray pathPoints;
  859. if(!Polygonize(pathTypes, pathPoints, fSmooth, scale))
  860. return(false);
  861. assstr.Format(L"{\an7\pos(%d,%d)\p%d}", rect.left, rect.top, 1+scale);
  862. // assstr.Format(L"{\p%d}", 1+scale);
  863. BYTE lastType = 0;
  864. int nPoints = pathTypes.GetSize();
  865. for(int i = 0; i < nPoints; i++)
  866. {
  867. CStringW s;
  868. switch(pathTypes[i])
  869. {
  870. case PT_MOVETO: 
  871. if(lastType != PT_MOVETO) assstr += L"m ";
  872. s.Format(L"%d %d ", pathPoints[i].x, pathPoints[i].y); 
  873. break;
  874. case PT_MOVETONC: 
  875. if(lastType != PT_MOVETONC) assstr += L"n ";
  876. s.Format(L"%d %d ", pathPoints[i].x, pathPoints[i].y); 
  877. break;
  878. case PT_LINETO: 
  879. if(lastType != PT_LINETO) assstr += L"l ";
  880. s.Format(L"%d %d ", pathPoints[i].x, pathPoints[i].y); 
  881. break;
  882. case PT_BEZIERTO: 
  883. if(i < nPoints-2)
  884. {
  885. if(lastType != PT_BEZIERTO) assstr += L"b ";
  886. s.Format(L"%d %d %d %d %d %d ", pathPoints[i].x, pathPoints[i].y, pathPoints[i+1].x, pathPoints[i+1].y, pathPoints[i+2].x, pathPoints[i+2].y); 
  887. i+=2;
  888. }
  889. break;
  890. case PT_BSPLINETO: 
  891. if(i < nPoints-2)
  892. {
  893. if(lastType != PT_BSPLINETO) assstr += L"s ";
  894. s.Format(L"%d %d %d %d %d %d ", pathPoints[i].x, pathPoints[i].y, pathPoints[i+1].x, pathPoints[i+1].y, pathPoints[i+2].x, pathPoints[i+2].y); 
  895. i+=2;
  896. }
  897. break;
  898. case PT_BSPLINEPATCHTO: 
  899. if(lastType != PT_BSPLINEPATCHTO) assstr += L"p ";
  900. s.Format(L"%d %d ", pathPoints[i].x, pathPoints[i].y); 
  901. break;
  902. }
  903. lastType = pathTypes[i];
  904. assstr += s;
  905. }
  906. assstr += L"{\p0}";
  907. return(nPoints > 0);
  908. }
  909. void CVobSubImage::Scale2x()
  910. {
  911. int w = rect.Width(), h = rect.Height();
  912. DWORD* src = (DWORD*)lpPixels;
  913. DWORD* dst = new DWORD[w*h];
  914. for(int y = 0; y < h; y++)
  915. {
  916. for(int x = 0; x < w; x++, src++, dst++)
  917. {
  918. DWORD E = *src;
  919. DWORD A = x > 0 && y > 0 ? src[-w-1] : E;
  920. DWORD B = y > 0 ? src[-w] : E;
  921. DWORD C = x < w-1 && y > 0 ? src[-w+1] : E;
  922. DWORD D = x > 0 ? src[-1] : E;;
  923. DWORD F = x < w-1 ? src[+1] : E;;
  924. DWORD G = x > 0 && y < h-1 ? src[+w-1] : E;
  925. DWORD H = y < h-1 ? src[+w] : E;
  926. DWORD I = x < w-1 && y < h-1 ? src[+w+1] : E;
  927. DWORD E0 = D == B && B != F && D != H ? D : E;
  928. DWORD E1 = B == F && B != D && F != H ? F : E;
  929. DWORD E2 = D == H && D != B && H != F ? D : E;
  930. DWORD E3 = H == F && D != H && B != F ? F : E;
  931. *dst = ((((E0&0x00ff00ff)+(E1&0x00ff00ff)+(E2&0x00ff00ff)+(E3&0x00ff00ff)+2)>>2)&0x00ff00ff)
  932. | (((((E0>>8)&0x00ff00ff)+((E1>>8)&0x00ff00ff)+((E2>>8)&0x00ff00ff)+((E3>>8)&0x00ff00ff)+2)<<6)&0xff00ff00);
  933. }
  934. }
  935. src -= w*h;
  936. dst -= w*h;
  937. memcpy(src, dst, w*h*4);
  938. delete [] dst;
  939. }