vid_gdi+.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. #include "main.h"
  2. #include "vid_subs.h"
  3. #include "vid_gdi+.h"
  4. #include "WinampAttributes.h"
  5. #include "../nu/AutoWide.h"
  6. #include "../nsutil/image.h"
  7. GDIPVideoOutput gdiplusVideo;
  8. static void colorspace_convert(UINT inputtype, const char * inputbuf, char * output, int flip, int width, int height);
  9. void GDIPVideoOutput::SetupGraphics()
  10. {
  11. // create new canvas
  12. if (graphics) delete graphics;
  13. graphics = new Graphics(parent, FALSE);
  14. graphics->Clear(Color(0));
  15. HDC h = graphics->GetHDC();
  16. // recreate back device context
  17. if (graphicsback) delete graphicsback; // we must delete this before deleting backdc
  18. if (backdc) DeleteDC(backdc);
  19. backdc = CreateCompatibleDC(h);
  20. // make sure back device context has right size and color depth
  21. HBITMAP memBM = CreateCompatibleBitmap(h, winw, winh);
  22. SelectObject(backdc, memBM);
  23. DeleteObject(memBM);
  24. // create back graphics canvas
  25. graphicsback = new Graphics(backdc);
  26. graphicsback->Clear(Color(0));
  27. graphics->ReleaseHDC(h);
  28. // set parameters
  29. /* fuck it, all default for now.
  30. graphicsback->SetInterpolationMode(InterpolationModeBilinear);
  31. graphicsback->SetCompositingQuality(CompositingQualityHighSpeed);
  32. graphicsback->SetCompositingMode(CompositingModeSourceCopy);
  33. graphicsback->SetSmoothingMode(SmoothingModeNone);
  34. */
  35. }
  36. GDIPVideoOutput::GDIPVideoOutput() : graphics(0), frame(0), type(0), graphicsback(0), backdc(0), w(0), h(0), flip(0), winw(0), winh(0), gdiplusToken(0), subs(0), needschange(0), parent(0), adjuster(0)
  37. {
  38. }
  39. GDIPVideoOutput::~GDIPVideoOutput()
  40. {
  41. }
  42. int GDIPVideoOutput::create(HWND parent, VideoAspectAdjuster *_adjuster, int w, int h, unsigned int type, int flipit, double aspectratio) //return 1 if ok
  43. {
  44. GdiplusStartupInput gdiplusStartupInput;
  45. GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
  46. needschange = 1;
  47. adjuster = _adjuster;
  48. needschange = 0;
  49. RECT r;
  50. GetWindowRect(parent, &r);
  51. winw = r.right - r.left;
  52. winh = r.bottom = r.top;
  53. this->parent = parent;
  54. this->flip = flipit;
  55. this->w = w;
  56. this->h = h;
  57. this->type = type;
  58. SetupGraphics();
  59. frame = new Bitmap(w, h, graphicsback);
  60. ZeroMemory(&lastrect, sizeof(RECT));
  61. return 1;
  62. }
  63. // TODO: verify that this works
  64. bool GDIPVideoOutput::FillFrame(Bitmap * frame, void *buf)
  65. {
  66. switch (type)
  67. {
  68. case VIDEO_MAKETYPE('R', 'G', '3', '2'):
  69. {
  70. BITMAPINFO info;
  71. info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  72. info.bmiHeader.biWidth = w;
  73. info.bmiHeader.biHeight = h;
  74. info.bmiHeader.biPlanes = 1;
  75. info.bmiHeader.biBitCount = 32;
  76. info.bmiHeader.biCompression = BI_RGB;
  77. info.bmiHeader.biSizeImage = 0;
  78. info.bmiHeader.biXPelsPerMeter = 0;
  79. info.bmiHeader.biYPelsPerMeter = 0;
  80. info.bmiHeader.biXPelsPerMeter = 0;
  81. info.bmiHeader.biYPelsPerMeter = 0;
  82. info.bmiHeader.biClrUsed = 0;
  83. info.bmiHeader.biClrImportant = 0;
  84. frame->FromBITMAPINFO(&info, buf);
  85. }
  86. return true;
  87. case VIDEO_MAKETYPE('R', 'G', '2', '4'):
  88. {
  89. BITMAPINFO info;
  90. info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  91. info.bmiHeader.biWidth = w;
  92. info.bmiHeader.biHeight = h;
  93. info.bmiHeader.biPlanes = 1;
  94. info.bmiHeader.biBitCount = 24;
  95. info.bmiHeader.biCompression = BI_RGB;
  96. info.bmiHeader.biSizeImage = 0;
  97. info.bmiHeader.biXPelsPerMeter = 0;
  98. info.bmiHeader.biYPelsPerMeter = 0;
  99. info.bmiHeader.biXPelsPerMeter = 0;
  100. info.bmiHeader.biYPelsPerMeter = 0;
  101. info.bmiHeader.biClrUsed = 0;
  102. info.bmiHeader.biClrImportant = 0;
  103. frame->FromBITMAPINFO(&info, buf);
  104. }
  105. return true;
  106. }
  107. return false;
  108. }
  109. void GDIPVideoOutput::displayFrame(const char *buf, int size, int time)
  110. {
  111. // TODO: verify that this works before uncommenting if (!FillFrame(frame, const_cast<char *>(buf)))
  112. {
  113. BitmapData d;
  114. d.Width = w;
  115. d.Height = h;
  116. d.PixelFormat = PixelFormat32bppRGB;
  117. d.Stride = 4 * w;
  118. d.Scan0 = 0;
  119. // write the frame to our bitmap object
  120. if (frame->LockBits(&Rect(0, 0, w, h), ImageLockModeWrite, PixelFormat32bppRGB, &d) != Ok)
  121. {
  122. needschange = 1; return;
  123. }
  124. colorspace_convert(type, buf, (char*)d.Scan0, flip, w, h);
  125. frame->UnlockBits(&d);
  126. }
  127. // fix aspect ratio
  128. RECT r = {0, 0, winw, winh};
  129. adjuster->adjustAspect(r);
  130. if (memcmp(&r, &lastrect, sizeof(RECT))) graphicsback->Clear(Color(0));
  131. lastrect = r;
  132. // draw the image
  133. graphicsback->DrawImage(frame, r.left, r.top, r.right - r.left, r.bottom - r.top);
  134. if (subs)
  135. { // draw subtitles
  136. //graphicsback->DrawString(AutoWide(subs->text),-1,&Font(L"Arial.ttf",36),PointF(subs->xPos,subs->yPos),&SolidBrush(Color(subs->colorRed,subs->colorGreen,subs->colorBlue)));
  137. }
  138. // flip graphics and graphicsback
  139. HDC h = graphics->GetHDC();
  140. HDC b = graphicsback->GetHDC();
  141. BitBlt(h, r.left, r.top, r.right - r.left, r.bottom - r.top, b, r.left, r.top, SRCCOPY);
  142. graphicsback->ReleaseHDC(b);
  143. graphics->ReleaseHDC(h);
  144. }
  145. int GDIPVideoOutput::needChange()
  146. {
  147. return needschange;
  148. }
  149. void GDIPVideoOutput::close()
  150. {
  151. if (graphics) delete graphics; graphics = 0;
  152. if (frame) delete frame; frame = 0;
  153. if (graphicsback) delete graphicsback; graphicsback = 0;
  154. if (backdc) DeleteDC(backdc); backdc = 0;
  155. subs = 0;
  156. type = 0;
  157. GdiplusShutdown(gdiplusToken);
  158. }
  159. void GDIPVideoOutput::Refresh()
  160. {}
  161. void GDIPVideoOutput::timerCallback()
  162. {
  163. RECT r;
  164. GetWindowRect(parent, &r);
  165. UINT w, h;
  166. w = r.right - r.left;
  167. h = r.bottom - r.top;
  168. bool change = (w != winw || h != winh);
  169. if (change)
  170. {
  171. winw = w;
  172. winh = h;
  173. // sizes have changed, we must reset the graphics
  174. SetupGraphics();
  175. }
  176. }
  177. //mmm. ctrl+c ctrl+v.
  178. static void colorspace_convert(UINT type, const char * buf, char * lpSurface, int flip, int width, int height)
  179. {
  180. const int lPitch = width * 4;
  181. if (type == VIDEO_MAKETYPE('Y', 'V', '1', '2'))
  182. {
  183. const YV12_PLANES *planes = (YV12_PLANES *)buf;
  184. // convert yv12 to rgb
  185. const int bytes = 4;
  186. int i, j, y00, y01, y10, y11, u, v;
  187. unsigned char *pY = (unsigned char *)planes->y.baseAddr;
  188. unsigned char *pU = (unsigned char *)planes->u.baseAddr;
  189. unsigned char *pV = (unsigned char *)planes->v.baseAddr;
  190. unsigned char *pOut = (unsigned char*)lpSurface;
  191. const int rvScale = (int)(2.017 * 65536.0); //91881;
  192. const int gvScale = - (int)(0.392 * 65536.0); // -22553;
  193. const int guScale = - (int)(0.813 * 65536.0); // -46801;
  194. const int buScale = (int)(1.596 * 65536.0); //116129;
  195. const int yScale = (int)(1.164 * 65536.0); //(1.164*65536.0);
  196. int addOut = lPitch * 2 - width * bytes;
  197. int yrb = planes->y.rowBytes;
  198. int addL = lPitch;
  199. /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
  200. #define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
  201. if (flip)
  202. {
  203. pOut += (lPitch) * (height - 1);
  204. addOut = -lPitch * 2 - width * bytes;
  205. addL = -addL;
  206. }
  207. for (j = 0; j <= height - 2; j += 2)
  208. {
  209. for (i = 0; i <= width - 2; i += 2)
  210. {
  211. y00 = *pY - 16;
  212. y01 = *(pY + 1) - 16;
  213. y10 = *(pY + yrb) - 16;
  214. y11 = *(pY + yrb + 1) - 16;
  215. u = (*pU++) - 128;
  216. v = (*pV++) - 128;
  217. {
  218. int r, g, b;
  219. g = guScale * v + gvScale * u;
  220. r = buScale * v;
  221. b = rvScale * u;
  222. y00 *= yScale; y01 *= yScale;
  223. y10 *= yScale; y11 *= yScale;
  224. {
  225. {
  226. unsigned char *rgb = pOut;
  227. /* Write out top two pixels */
  228. rgb[0] = LIMIT(b + y00); rgb[1] = LIMIT(g + y00); rgb[2] = LIMIT(r + y00);
  229. rgb[4] = LIMIT(b + y01); rgb[5] = LIMIT(g + y01); rgb[6] = LIMIT(r + y01);
  230. /* Skip down to next line to write out bottom two pixels */
  231. rgb += addL;
  232. rgb[0] = LIMIT(b + y10); rgb[1] = LIMIT(g + y10); rgb[2] = LIMIT(r + y10);
  233. rgb[4] = LIMIT(b + y11); rgb[5] = LIMIT(g + y11); rgb[6] = LIMIT(r + y11);
  234. }
  235. }
  236. }
  237. pY += 2;
  238. pOut += 2 * bytes;
  239. }
  240. pY += yrb + yrb - width;
  241. pU += planes->u.rowBytes - width / 2;
  242. pV += planes->v.rowBytes - width / 2;
  243. pOut += addOut;
  244. }
  245. }
  246. else if (type == VIDEO_MAKETYPE('R', 'G', '3', '2'))
  247. {
  248. if (flip)
  249. nsutil_image_CopyFlipped_U8((uint8_t *)lpSurface, lPitch, (const uint8_t *)buf, width*4, width, height);
  250. else
  251. nsutil_image_Copy_U8((uint8_t *)lpSurface, lPitch, (const uint8_t *)buf, width*4, width, height);
  252. }
  253. else if (type == VIDEO_MAKETYPE('Y', 'U', 'Y', '2') || type == VIDEO_MAKETYPE('U', 'Y', 'V', 'Y'))
  254. {
  255. char *b = (char *)lpSurface;
  256. int l2 = lPitch;
  257. if (flip)
  258. {
  259. b += (height - 1) * l2;
  260. l2 = -l2;
  261. }
  262. {
  263. {
  264. // yuy2->rgb32 conversion
  265. unsigned char *src = (unsigned char *)buf;
  266. unsigned char *dst = (unsigned char *)lpSurface;
  267. int line, col; //, linewidth;
  268. int y, yy;
  269. int u, v;
  270. int vr, ug, vg, ub;
  271. unsigned char *py, *pu, *pv;
  272. //linewidth = width - (width >> 1);
  273. py = src;
  274. pu = src + 1;
  275. pv = src + 3;
  276. int pitchadd = lPitch - (width * 4);
  277. for (line = 0; line < height; line++)
  278. {
  279. for (col = 0; col < width; col++)
  280. {
  281. #undef LIMIT
  282. #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
  283. y = *py;
  284. yy = y << 8;
  285. u = *pu - 128;
  286. ug = 88 * u;
  287. ub = 454 * u;
  288. v = *pv - 128;
  289. vg = 183 * v;
  290. vr = 359 * v;
  291. *dst++ = LIMIT(yy + ub); // b
  292. *dst++ = LIMIT(yy - ug - vg); // g
  293. *dst++ = LIMIT(yy + vr); // r
  294. dst++;
  295. py += 2;
  296. if ((col & 1) == 1)
  297. {
  298. pu += 4; // skip yvy every second y
  299. pv += 4; // skip yuy every second y
  300. }
  301. } // ..for col
  302. dst += pitchadd;
  303. } /* ..for line */
  304. }
  305. }
  306. }
  307. else if (type == VIDEO_MAKETYPE('R', 'G', '2', '4'))
  308. {
  309. if (flip)
  310. nsutil_image_ConvertFlipped_RGB24_RGB32((RGB32 *)lpSurface, lPitch, (const uint8_t *)buf, width*3, width, height);
  311. else
  312. nsutil_image_Convert_RGB24_RGB32((RGB32 *)lpSurface, lPitch, (const uint8_t *)buf, width*3, width, height);
  313. }
  314. }