diff options
Diffstat (limited to 'Src/Winamp/vid_gdi+.cpp')
-rw-r--r-- | Src/Winamp/vid_gdi+.cpp | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/Src/Winamp/vid_gdi+.cpp b/Src/Winamp/vid_gdi+.cpp new file mode 100644 index 00000000..49eb903f --- /dev/null +++ b/Src/Winamp/vid_gdi+.cpp @@ -0,0 +1,359 @@ +#include "main.h" +#include "vid_subs.h" +#include "vid_gdi+.h" +#include "WinampAttributes.h" +#include "../nu/AutoWide.h" +#include "../nsutil/image.h" + +GDIPVideoOutput gdiplusVideo; + +static void colorspace_convert(UINT inputtype, const char * inputbuf, char * output, int flip, int width, int height); + +void GDIPVideoOutput::SetupGraphics() +{ + // create new canvas + if (graphics) delete graphics; + graphics = new Graphics(parent, FALSE); + graphics->Clear(Color(0)); + + HDC h = graphics->GetHDC(); + + // recreate back device context + if (graphicsback) delete graphicsback; // we must delete this before deleting backdc + if (backdc) DeleteDC(backdc); + backdc = CreateCompatibleDC(h); + + // make sure back device context has right size and color depth + HBITMAP memBM = CreateCompatibleBitmap(h, winw, winh); + SelectObject(backdc, memBM); + DeleteObject(memBM); + + // create back graphics canvas + graphicsback = new Graphics(backdc); + graphicsback->Clear(Color(0)); + + graphics->ReleaseHDC(h); + + // set parameters + /* fuck it, all default for now. + graphicsback->SetInterpolationMode(InterpolationModeBilinear); + graphicsback->SetCompositingQuality(CompositingQualityHighSpeed); + graphicsback->SetCompositingMode(CompositingModeSourceCopy); + graphicsback->SetSmoothingMode(SmoothingModeNone); + */ +} + +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) +{ +} + +GDIPVideoOutput::~GDIPVideoOutput() +{ +} + +int GDIPVideoOutput::create(HWND parent, VideoAspectAdjuster *_adjuster, int w, int h, unsigned int type, int flipit, double aspectratio) //return 1 if ok +{ + GdiplusStartupInput gdiplusStartupInput; + GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + + needschange = 1; + adjuster = _adjuster; + needschange = 0; + RECT r; + GetWindowRect(parent, &r); + winw = r.right - r.left; + winh = r.bottom = r.top; + this->parent = parent; + this->flip = flipit; + this->w = w; + this->h = h; + this->type = type; + SetupGraphics(); + frame = new Bitmap(w, h, graphicsback); + ZeroMemory(&lastrect, sizeof(RECT)); + return 1; +} + +// TODO: verify that this works +bool GDIPVideoOutput::FillFrame(Bitmap * frame, void *buf) +{ + switch (type) + { + case VIDEO_MAKETYPE('R', 'G', '3', '2'): + { + BITMAPINFO info; + info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info.bmiHeader.biWidth = w; + info.bmiHeader.biHeight = h; + info.bmiHeader.biPlanes = 1; + info.bmiHeader.biBitCount = 32; + info.bmiHeader.biCompression = BI_RGB; + info.bmiHeader.biSizeImage = 0; + info.bmiHeader.biXPelsPerMeter = 0; + info.bmiHeader.biYPelsPerMeter = 0; + info.bmiHeader.biXPelsPerMeter = 0; + info.bmiHeader.biYPelsPerMeter = 0; + info.bmiHeader.biClrUsed = 0; + info.bmiHeader.biClrImportant = 0; + + frame->FromBITMAPINFO(&info, buf); + + } + return true; + case VIDEO_MAKETYPE('R', 'G', '2', '4'): + { + BITMAPINFO info; + info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info.bmiHeader.biWidth = w; + info.bmiHeader.biHeight = h; + info.bmiHeader.biPlanes = 1; + info.bmiHeader.biBitCount = 24; + info.bmiHeader.biCompression = BI_RGB; + info.bmiHeader.biSizeImage = 0; + info.bmiHeader.biXPelsPerMeter = 0; + info.bmiHeader.biYPelsPerMeter = 0; + info.bmiHeader.biXPelsPerMeter = 0; + info.bmiHeader.biYPelsPerMeter = 0; + info.bmiHeader.biClrUsed = 0; + info.bmiHeader.biClrImportant = 0; + + frame->FromBITMAPINFO(&info, buf); + + } + return true; + } + return false; +} + +void GDIPVideoOutput::displayFrame(const char *buf, int size, int time) +{ + // TODO: verify that this works before uncommenting if (!FillFrame(frame, const_cast<char *>(buf))) + { + BitmapData d; + d.Width = w; + d.Height = h; + d.PixelFormat = PixelFormat32bppRGB; + d.Stride = 4 * w; + d.Scan0 = 0; + + // write the frame to our bitmap object + if (frame->LockBits(&Rect(0, 0, w, h), ImageLockModeWrite, PixelFormat32bppRGB, &d) != Ok) + { + needschange = 1; return; + } + colorspace_convert(type, buf, (char*)d.Scan0, flip, w, h); + frame->UnlockBits(&d); + } + + // fix aspect ratio + RECT r = {0, 0, winw, winh}; + adjuster->adjustAspect(r); + if (memcmp(&r, &lastrect, sizeof(RECT))) graphicsback->Clear(Color(0)); + lastrect = r; + + // draw the image + graphicsback->DrawImage(frame, r.left, r.top, r.right - r.left, r.bottom - r.top); + if (subs) + { // draw subtitles + //graphicsback->DrawString(AutoWide(subs->text),-1,&Font(L"Arial.ttf",36),PointF(subs->xPos,subs->yPos),&SolidBrush(Color(subs->colorRed,subs->colorGreen,subs->colorBlue))); + } + + // flip graphics and graphicsback + HDC h = graphics->GetHDC(); + HDC b = graphicsback->GetHDC(); + BitBlt(h, r.left, r.top, r.right - r.left, r.bottom - r.top, b, r.left, r.top, SRCCOPY); + graphicsback->ReleaseHDC(b); + graphics->ReleaseHDC(h); +} + +int GDIPVideoOutput::needChange() +{ + return needschange; +} + +void GDIPVideoOutput::close() +{ + if (graphics) delete graphics; graphics = 0; + if (frame) delete frame; frame = 0; + if (graphicsback) delete graphicsback; graphicsback = 0; + if (backdc) DeleteDC(backdc); backdc = 0; + subs = 0; + type = 0; + + GdiplusShutdown(gdiplusToken); +} + +void GDIPVideoOutput::Refresh() +{} + +void GDIPVideoOutput::timerCallback() +{ + RECT r; + GetWindowRect(parent, &r); + UINT w, h; + w = r.right - r.left; + h = r.bottom - r.top; + bool change = (w != winw || h != winh); + if (change) + { + winw = w; + winh = h; + // sizes have changed, we must reset the graphics + SetupGraphics(); + } +} + +//mmm. ctrl+c ctrl+v. +static void colorspace_convert(UINT type, const char * buf, char * lpSurface, int flip, int width, int height) +{ + const int lPitch = width * 4; + if (type == VIDEO_MAKETYPE('Y', 'V', '1', '2')) + { + const YV12_PLANES *planes = (YV12_PLANES *)buf; + // convert yv12 to rgb + const int bytes = 4; + int i, j, y00, y01, y10, y11, u, v; + unsigned char *pY = (unsigned char *)planes->y.baseAddr; + unsigned char *pU = (unsigned char *)planes->u.baseAddr; + unsigned char *pV = (unsigned char *)planes->v.baseAddr; + unsigned char *pOut = (unsigned char*)lpSurface; + const int rvScale = (int)(2.017 * 65536.0); //91881; + const int gvScale = - (int)(0.392 * 65536.0); // -22553; + const int guScale = - (int)(0.813 * 65536.0); // -46801; + const int buScale = (int)(1.596 * 65536.0); //116129; + const int yScale = (int)(1.164 * 65536.0); //(1.164*65536.0); + int addOut = lPitch * 2 - width * bytes; + int yrb = planes->y.rowBytes; + int addL = lPitch; + + /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */ +#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) + + if (flip) + { + pOut += (lPitch) * (height - 1); + addOut = -lPitch * 2 - width * bytes; + addL = -addL; + } + + for (j = 0; j <= height - 2; j += 2) + { + for (i = 0; i <= width - 2; i += 2) + { + y00 = *pY - 16; + y01 = *(pY + 1) - 16; + y10 = *(pY + yrb) - 16; + y11 = *(pY + yrb + 1) - 16; + u = (*pU++) - 128; + v = (*pV++) - 128; + + { + int r, g, b; + + g = guScale * v + gvScale * u; + r = buScale * v; + b = rvScale * u; + + y00 *= yScale; y01 *= yScale; + y10 *= yScale; y11 *= yScale; + + { + { + unsigned char *rgb = pOut; + /* Write out top two pixels */ + rgb[0] = LIMIT(b + y00); rgb[1] = LIMIT(g + y00); rgb[2] = LIMIT(r + y00); + rgb[4] = LIMIT(b + y01); rgb[5] = LIMIT(g + y01); rgb[6] = LIMIT(r + y01); + + /* Skip down to next line to write out bottom two pixels */ + rgb += addL; + rgb[0] = LIMIT(b + y10); rgb[1] = LIMIT(g + y10); rgb[2] = LIMIT(r + y10); + rgb[4] = LIMIT(b + y11); rgb[5] = LIMIT(g + y11); rgb[6] = LIMIT(r + y11); + } + } + } + + pY += 2; + pOut += 2 * bytes; + } + pY += yrb + yrb - width; + pU += planes->u.rowBytes - width / 2; + pV += planes->v.rowBytes - width / 2; + pOut += addOut; + } + } + else if (type == VIDEO_MAKETYPE('R', 'G', '3', '2')) + { + if (flip) + nsutil_image_CopyFlipped_U8((uint8_t *)lpSurface, lPitch, (const uint8_t *)buf, width*4, width, height); + else + nsutil_image_Copy_U8((uint8_t *)lpSurface, lPitch, (const uint8_t *)buf, width*4, width, height); + } + else if (type == VIDEO_MAKETYPE('Y', 'U', 'Y', '2') || type == VIDEO_MAKETYPE('U', 'Y', 'V', 'Y')) + { + char *b = (char *)lpSurface; + int l2 = lPitch; + if (flip) + { + b += (height - 1) * l2; + l2 = -l2; + } + { + { + // yuy2->rgb32 conversion + unsigned char *src = (unsigned char *)buf; + unsigned char *dst = (unsigned char *)lpSurface; + int line, col; //, linewidth; + int y, yy; + int u, v; + int vr, ug, vg, ub; + unsigned char *py, *pu, *pv; + + //linewidth = width - (width >> 1); + py = src; + pu = src + 1; + pv = src + 3; + + int pitchadd = lPitch - (width * 4); + + for (line = 0; line < height; line++) + { + for (col = 0; col < width; col++) + { +#undef LIMIT +#define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) ) + + y = *py; + yy = y << 8; + u = *pu - 128; + ug = 88 * u; + ub = 454 * u; + v = *pv - 128; + vg = 183 * v; + vr = 359 * v; + + *dst++ = LIMIT(yy + ub); // b + *dst++ = LIMIT(yy - ug - vg); // g + *dst++ = LIMIT(yy + vr); // r + dst++; + + py += 2; + if ((col & 1) == 1) + { + pu += 4; // skip yvy every second y + pv += 4; // skip yuy every second y + } + } // ..for col + dst += pitchadd; + } /* ..for line */ + } + } + } + else if (type == VIDEO_MAKETYPE('R', 'G', '2', '4')) + { + if (flip) + nsutil_image_ConvertFlipped_RGB24_RGB32((RGB32 *)lpSurface, lPitch, (const uint8_t *)buf, width*3, width, height); + else + nsutil_image_Convert_RGB24_RGB32((RGB32 *)lpSurface, lPitch, (const uint8_t *)buf, width*3, width, height); + + } +}
\ No newline at end of file |