/* * vconvert.cxx * * Classes to support streaming video input (grabbing) and output. * * Portable Windows Library * * Copyright (c) 1993-2000 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Contributor(s): Derek Smithies (derek@indranet.co.nz) * Thorsten Westheider (thorsten.westheider@teleos-web.de) * Mark Cooke (mpc@star.sr.bham.ac.uk) * * $Log: vconvert.cxx,v $ * Revision 1.45.2.9 2006/12/30 21:48:54 dsandras * Backported patch from HEAD that fixes Ekiga bug #333311. * * Revision 1.45.2.8 2006/11/01 17:48:13 dsandras * Added patch from Brian Lu to fix compilation on * OpenSolaris. * * Revision 1.45.2.7 2006/04/30 21:28:38 dsandras * Backport from HEAD. * * Revision 1.53 2006/04/30 21:26:46 dsandras * Fixed green color problem when converting images in some of the color formats * used by the iSight thanks to Luc Saillard . * * Revision 1.45.2.6 2006/04/23 20:21:47 dsandras * Backported fix from HEAD. * * Revision 1.52 2006/04/23 20:20:49 dsandras * Added resizing capability for VYUY YUYV formats thanks to Luc Saillard. * Many thanks! * * Revision 1.45.2.5 2006/03/12 11:15:14 dsandras * Fix for MJPEG thanks to Luc Saillard. (Backport from HEAD). * * Revision 1.45.2.4 2006/03/07 20:55:28 dsandras * More backports from HEAD. * * Revision 1.49 2006/03/07 20:53:51 dsandras * Added support for JPEG based webcams, thanks to Luc Saillard . * * Revision 1.45.2.3 2006/02/22 11:53:29 csoutheren * Backports from HEAD * * Revision 1.45.2.2 2006/01/31 09:19:04 csoutheren * Backport from CVS head * * Revision 1.45.2.1 2006/01/30 00:03:12 csoutheren * Backported support for cameras that return MJPEG streams * Thanks to Luc Saillard and Damien Sandras * * Revision 1.48 2006/02/22 11:17:53 csoutheren * Applied patch #1425825 * MaxOSX compatibility * * Revision 1.47 2006/01/31 03:24:43 csoutheren * Removed MJPEG capabilities when compiling with Microsoft compilers * * Revision 1.46 2006/01/29 22:46:38 csoutheren * Added support for cameras that return MJPEG streams * Thanks to Luc Saillard and Damien Sandras * * Revision 1.45 2006/01/16 20:18:01 dsandras * Applied patch from Luc Saillard to improve conversion * routines. Thanks!! * * Revision 1.44 2006/01/09 18:19:12 dsandras * Add YUY2 (or YUV420) format. Resizing to YUV420P is done, but it's not very * efficient. * Fix the gray border when doing padding for YUV420P (change it to black). * Logitech webcam fusion export only big format in yuy2. * Patches provided by Luc Saillard . Many thanks! * * Revision 1.43 2006/01/07 13:33:02 dsandras * Added code allowing real resizing on YUV420P streams thanks to Luc Sailard . Thank you very much for that nice patch Luc! * * Revision 1.42 2005/11/30 12:47:42 csoutheren * Removed tabs, reformatted some code, and changed tags for Doxygen * * Revision 1.41 2005/08/09 09:08:11 rjongbloed * Merged new video code from branch back to the trunk. * * Revision 1.40.6.2 2005/07/17 12:58:15 rjongbloed * Sorted out the ordering or Red. Blue, Cr and Cb in RGB/BGR/YUV420 formats * * Revision 1.40.6.1 2005/07/17 09:27:08 rjongbloed * Major revisions of the PWLib video subsystem including: * removal of F suffix on colour formats for vertical flipping, all done with existing bool * working through use of RGB and BGR formats so now consistent * cleaning up the plug in system to use virtuals instead of pointers to functions. * rewrite of SDL to be a plug in compatible video output device. * extensive enhancement of video test program * * Revision 1.40 2005/01/25 06:35:28 csoutheren * Removed warnings under MSVC * * Revision 1.39 2005/01/04 07:44:03 csoutheren * More changes to implement the new configuration methodology, and also to * attack the global static problem * * Revision 1.38 2004/11/07 06:28:00 rjongbloed * Removed warnings about unused parameters in SBGGR8 conversion functions. Left one * in (flip vertical) as it should be implemented but wasn't. * * Revision 1.37 2004/10/27 09:24:18 dsandras * Added patch from Nicola Orru' to convert from SBGGR8 to YUV420P. Thanks! * * Revision 1.36 2004/09/21 13:01:08 dsandras * Added conversion from sbggr to rgb thanks to an anonymous patcher. * * Revision 1.35 2003/11/23 22:17:35 dsandras * Added YUV420P to BGR24 and BGR32 conversion. * * Revision 1.34 2003/06/14 02:57:36 rjongbloed * REmoved redundent parameter, grey scale does not have rgb increment! * * Revision 1.33 2003/06/09 22:37:24 dereksmithies * Fix from Clive Nicolson to make b/w colour conversions work (i.e. grey palette). * many thanks! * * Revision 1.32 2003/04/03 09:28:37 robertj * Added reversed RGB byte order versions (BGR24), thanks Damien Sandras * * Revision 1.31 2003/03/31 11:30:14 rogerh * make 'cb' and 'cr' contain the values that their name implies. * * Revision 1.30 2002/09/01 23:00:05 dereks * Fix noise in flipped RGB image. Thanks Alex Phtahov. * * Revision 1.29 2002/02/26 02:23:21 dereks * Reduced verbosity in PTRACE output for when video is enabled. * * Revision 1.28 2002/02/20 02:37:26 dereks * Initial release of Firewire camera support for linux. * Many thanks to Ryutaroh Matsumoto . * * Revision 1.27 2002/02/03 19:55:57 dereks * *** empty log message *** * * Revision 1.26 2002/01/08 01:32:50 robertj * Tidied up some PTRACE debug output. * * Revision 1.25 2002/01/04 04:11:45 dereks * Add video flip code from Walter Whitlock, which flips code at the grabber. * * Revision 1.24 2001/12/08 00:33:11 robertj * Changed some (unsigned int *) to (DWORD *) as the latter is assured to be a * pointer to a 32 bit integer and the former is not. * * Revision 1.23 2001/12/06 22:14:45 dereks * Improve YUV 422 resize routine so it now subsamples as required. * * Revision 1.22 2001/12/03 02:21:50 dereks * Add YUV420P to RGB24F, RGB32F converters. * * Revision 1.21 2001/12/02 21:53:56 dereks * Additional debug information * * Revision 1.20 2001/11/28 04:43:10 robertj * Added synonym colour class for equivalent colour format strings. * Allowed for setting ancestor classes in PCOLOUR_CONVERTER() macro. * Moved static functions into internal class to avoid pasing lots of parameters. * Added conversions for flipped RGB colour formats. * * Revision 1.19 2001/09/06 02:06:36 robertj * Fixed bug in detecting size mismatch, thanks Vjacheslav Andrejev * * Revision 1.18 2001/08/22 02:14:08 robertj * Fixed MSVC compatibility. * * Revision 1.17 2001/08/22 02:06:17 robertj * Resolved confusion with YUV411P and YUV420P video formats, thanks Mark Cooke. * * Revision 1.16 2001/08/20 07:01:26 robertj * Fixed wierd problems with YUV411P and YUV420P formats, thanks Mark Cooke. * * Revision 1.15 2001/08/16 23:17:29 robertj * Added 420P to 411P converter, thanks Mark Cooke. * * Revision 1.14 2001/08/03 10:13:56 robertj * Changes to previous check in to support MSVC. * * Revision 1.13 2001/08/03 04:21:51 dereks * Add colour/size conversion for YUV422->YUV411P * Add Get/Set Brightness,Contrast,Hue,Colour for PVideoDevice, and * Linux PVideoInputDevice. * Add lots of PTRACE statement for debugging colour conversion. * Add support for Sony Vaio laptop under linux. Requires 2.4.7 kernel. * * Revision 1.12 2001/07/20 05:23:51 robertj * Added YUV411P to RGB24 converter. * * Revision 1.11 2001/05/14 05:10:38 robertj * Fixed problems with video colour converters registration, could not rely * on static PList being initialised before all registration instances. * * Revision 1.10 2001/03/20 02:21:57 robertj * More enhancements from Mark Cooke * * Revision 1.9 2001/03/08 23:36:03 robertj * Added backward compatibility SetFrameSize() function. * Added internal SimpleConvert() function for same type converters. * Fixed some documentation. * * Revision 1.8 2001/03/08 08:31:34 robertj * Numerous enhancements to the video grabbing code including resizing * infrastructure to converters. Thanks a LOT, Mark Cooke. * * Revision 1.7 2001/03/07 01:39:56 dereks * Fix image flip (top to bottom) in YUV411P to RGB24 conversion * * Revision 1.6 2001/03/06 23:48:32 robertj * Fixed naming convention on video converter classes. * * Revision 1.5 2001/03/03 23:25:07 robertj * Fixed use of video conversion function, returning bytes in destination frame. * * Revision 1.4 2001/03/03 06:13:01 robertj * Major upgrade of video conversion and grabbing classes. * * Revision 1.3 2001/03/03 05:06:31 robertj * Major upgrade of video conversion and grabbing classes. * * Revision 1.2 2000/12/19 23:58:14 robertj * Fixed MSVC compatibility issues. * * Revision 1.1 2000/12/19 22:20:26 dereks * Add video channel classes to connect to the PwLib PVideoInputDevice class. * Add PFakeVideoInput class to generate test images for video. */ #include #ifdef __GNUC__ #pragma implementation "vconvert.h" #endif #include #if defined(__GNUC__) || defined(__sun) #include "tinyjpeg.h" #endif static PColourConverterRegistration * RegisteredColourConvertersListHead = NULL; PSYNONYM_COLOUR_CONVERTER(SBGGR8, SBGGR8); PSYNONYM_COLOUR_CONVERTER(Grey, Grey); PSYNONYM_COLOUR_CONVERTER(RGB24, RGB24); PSYNONYM_COLOUR_CONVERTER(BGR24, BGR24); PSYNONYM_COLOUR_CONVERTER(RGB32, RGB32); PSYNONYM_COLOUR_CONVERTER(BGR32, BGR32); PSYNONYM_COLOUR_CONVERTER(YUV411P,YUV411P); PSYNONYM_COLOUR_CONVERTER(YUV420P,IYUV); PSYNONYM_COLOUR_CONVERTER(IYUV, YUV420P); PSYNONYM_COLOUR_CONVERTER(YUV420P,I420); PSYNONYM_COLOUR_CONVERTER(I420, YUV420P); class PStandardColourConverter : public PColourConverter { PCLASSINFO(PStandardColourConverter, PColourConverter); protected: PStandardColourConverter( const PString & srcFmt, const PString & dstFmt, unsigned w, unsigned h ) : PColourConverter(srcFmt, dstFmt, w, h) { } BOOL SBGGR8toYUV420P( const BYTE * srgb, BYTE * rgb, PINDEX * bytesReturned ) const; BOOL SBGGR8toRGB( const BYTE * srgb, BYTE * rgb, PINDEX * bytesReturned ) const; void GreytoYUV420PSameSize( const BYTE * rgb, BYTE * yuv ) const; void GreytoYUV420PWithResize( const BYTE * rgb, BYTE * yuv ) const; BOOL GreytoYUV420P( const BYTE * rgb, BYTE * yuv, PINDEX * bytesReturned ) const; void RGBtoYUV420PSameSize( const BYTE * rgb, BYTE * yuv, unsigned rgbIncrement, unsigned redOffset, unsigned blueOffset ) const; void RGBtoYUV420PWithResize( const BYTE * rgb, BYTE * yuv, unsigned rgbIncrement, unsigned redOffset, unsigned blueOffset ) const; BOOL RGBtoYUV420P( const BYTE * rgb, BYTE * yuv, PINDEX * bytesReturned, unsigned rgbIncrement, unsigned redOffset, unsigned blueOffset ) const; BOOL YUV420PtoRGB( const BYTE * yuv, BYTE * rgb, PINDEX * bytesReturned, unsigned rgbIncrement, unsigned redOffset, unsigned blueOffset ) const; BOOL SwapRedAndBlue( const BYTE * src, BYTE * dst, PINDEX * bytesReturned, unsigned srcIncrement, unsigned dstIncrement ) const; void ResizeYUV422( const BYTE * src, BYTE * dest ) const; void ResizeYUV420P( const BYTE * src, BYTE * dest ) const; void UYVY422toYUV420PSameSize( const BYTE *uyvy, BYTE *yuv420p ) const; void UYVY422toYUV420PWithResize( const BYTE *uyvy, BYTE *yuv420p ) const; void YUY2toYUV420PSameSize( const BYTE *yuy2, BYTE *yuv420p ) const; void YUY2toYUV420PWithResize( const BYTE *yuy2, BYTE *yuv420p ) const; bool MJPEGtoYUV420PSameSize( const BYTE *yuy2, BYTE *yuv420p ); #if defined (__GNUC__) || defined (__sun) bool MJPEGtoXXX( const BYTE *mjpeg, BYTE *output_data, PINDEX *bytesReturned, int format ); bool MJPEGtoYUV420P( const BYTE *mjpeg, BYTE *yuv420p, PINDEX *bytesReturned ); bool MJPEGtoXXXSameSize( const BYTE *yuy2, BYTE *rgb, int format ); #endif }; #define PSTANDARD_COLOUR_CONVERTER(from,to) \ PCOLOUR_CONVERTER2(P_##from##_##to,PStandardColourConverter,#from,#to) /////////////////////////////////////////////////////////////////////////////// // PColourConverter PColourConverterRegistration::PColourConverterRegistration(const PString & srcColourFormat, const PString & destColourFormat) : PCaselessString(srcColourFormat+'\t'+destColourFormat) { PColourConverterRegistration * test = RegisteredColourConvertersListHead; while (test != NULL) { if (*test == *this) return; test = test->link; } link = RegisteredColourConvertersListHead; RegisteredColourConvertersListHead = this; } PColourConverter * PColourConverter::Create(const PString & srcColourFormat, const PString & destColourFormat, unsigned width, unsigned height) { PString converterName = srcColourFormat + '\t' + destColourFormat; PColourConverterRegistration * find = RegisteredColourConvertersListHead; while (find != NULL) { if (*find == converterName) { return find->Create(width, height); } find = find->link; } PTRACE(2,"PColCnv\tCreate error. Did not find " << srcColourFormat << "->" << destColourFormat); return NULL; } PColourConverter::PColourConverter(const PString & src, const PString & dst, unsigned width, unsigned height) : srcColourFormat(src), dstColourFormat(dst) { PTRACE(6,"PColCnv\tPColourConverter constructed: " << src << "->" << dst << ' ' << width << 'x'<< height); #ifndef P_MACOSX jdec = NULL; #endif verticalFlip = FALSE; SetFrameSize(width,height); } BOOL PColourConverter::SetFrameSize(unsigned width, unsigned height) { BOOL ok1 = SetSrcFrameSize(width, height); BOOL ok2 = SetDstFrameSize(width, height, FALSE); PTRACE(2,"PColCnv\tSetFrameSize: " << width << 'x' << height << (ok1 && ok2 ? " OK" : " Failed")); return ok1 && ok2; } BOOL PColourConverter::SetSrcFrameSize(unsigned width, unsigned height) { srcFrameWidth = width; srcFrameHeight = height; srcFrameBytes = PVideoDevice::CalculateFrameBytes(srcFrameWidth, srcFrameHeight, srcColourFormat); PTRACE(srcFrameBytes != 0 ? 6 : 2, "PColCnv\tSetSrcFrameSize " << ((srcFrameBytes != 0) ? "Succeed": "Fail") << "ed, " << srcColourFormat << ' ' << srcFrameWidth << 'x' << srcFrameHeight << ", " << srcFrameBytes << " bytes."); return srcFrameBytes != 0; } BOOL PColourConverter::SetDstFrameSize(unsigned width, unsigned height, BOOL bScale) { dstFrameWidth = width; dstFrameHeight = height; scaleNotCrop = bScale; dstFrameBytes = PVideoDevice::CalculateFrameBytes(dstFrameWidth, dstFrameHeight, dstColourFormat); PTRACE(dstFrameBytes != 0 ? 6 : 2, "PColCnv\tSetDstFrameSize " << ((dstFrameBytes != 0) ? "Succeed": "Fail") << "ed, " << dstColourFormat << ' ' << dstFrameWidth << 'x' << dstFrameHeight << ", " << dstFrameBytes << " bytes."); return dstFrameBytes != 0; } BOOL PColourConverter::GetSrcFrameSize(unsigned &width, unsigned &height) const { width = srcFrameWidth; height = srcFrameHeight; return TRUE; } BOOL PColourConverter::GetDstFrameSize(unsigned &width, unsigned &height) const { width = dstFrameWidth; height = dstFrameHeight; return TRUE; } BOOL PColourConverter::ConvertInPlace(BYTE * frameBuffer, PINDEX * bytesReturned, BOOL noIntermediateFrame) { if (Convert(frameBuffer, frameBuffer, bytesReturned)) return TRUE; if (noIntermediateFrame) { PTRACE(2,"PColCnv\tError in ConvertInPlace, no intermediate frame available."); return FALSE; } BYTE * intermediate = intermediateFrameStore.GetPointer(dstFrameBytes); PINDEX bytes; if (!Convert(frameBuffer, intermediate, &bytes)) return FALSE; memcpy(frameBuffer, intermediate, bytes); if (bytesReturned != NULL) *bytesReturned = bytes; return TRUE; } /////////////////////////////////////////////////////////////////////////////// PSynonymColourRegistration::PSynonymColourRegistration(const char * srcFmt, const char * dstFmt) : PColourConverterRegistration(srcFmt,dstFmt) { } PColourConverter * PSynonymColourRegistration::Create(unsigned w, unsigned h) const { PINDEX tab = Find('\t'); return new PSynonymColour(Left(tab), Mid(tab+1), w, h); } BOOL PSynonymColour::Convert(const BYTE *srcFrameBuffer, BYTE *dstFrameBuffer, unsigned int __srcFrameBytes, PINDEX * bytesReturned) { srcFrameBytes = __srcFrameBytes; return Convert(srcFrameBuffer, dstFrameBuffer, bytesReturned); } BOOL PSynonymColour::Convert(const BYTE *srcFrameBuffer, BYTE *dstFrameBuffer, PINDEX * bytesReturned) { if ((srcFrameWidth != dstFrameWidth) || (srcFrameHeight != dstFrameHeight)) { PTRACE(2,"PColCnv\tCannot do synonym colour conversion, source and destination frame size not equal."); return FALSE; } if (verticalFlip) { PINDEX rowSize = dstFrameBytes/srcFrameHeight; if (rowSize*srcFrameHeight != dstFrameBytes) { PTRACE(2,"PColCnv\tCannot do synonym colour conversion, frame does not have equal sized scan lines."); return FALSE; } if (srcFrameBuffer != dstFrameBuffer) { const BYTE * srcRowPtr = srcFrameBuffer; BYTE * dstRowPtr = dstFrameBuffer + srcFrameHeight*rowSize; for (unsigned y = 0; y < srcFrameHeight; y++) { dstRowPtr -= rowSize; memcpy(dstRowPtr, srcRowPtr, rowSize); srcRowPtr += rowSize; } } else { BYTE * rowPtr1 = dstFrameBuffer; BYTE * rowPtr2 = dstFrameBuffer + srcFrameHeight*rowSize; PBYTEArray temp(rowSize); for (unsigned y = 0; y < srcFrameHeight; y += 2) { rowPtr2 -= rowSize; memcpy(temp.GetPointer(), rowPtr1, rowSize); memcpy(rowPtr1, rowPtr2, rowSize); memcpy(rowPtr2, temp.GetPointer(), rowSize); rowPtr1 += rowSize; } } } else { if (srcFrameBuffer != dstFrameBuffer) memcpy(dstFrameBuffer, srcFrameBuffer, dstFrameBytes); } if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } /////////////////////////////////////////////////////////////////////////////// #define BLACK_Y 0 #define BLACK_U 128 #define BLACK_V 128 #define greytoy(r, y) y=r #define greytoyuv(r, y, u, v) greytoy(r,y); u=BLACK_U; v=BLACK_V void PStandardColourConverter::GreytoYUV420PSameSize(const BYTE * grey, BYTE * yuv) const { const unsigned planeSize = srcFrameWidth*srcFrameHeight; const unsigned halfWidth = srcFrameWidth >> 1; // get pointers to the data BYTE * yplane = yuv; BYTE * uplane = yuv + planeSize; BYTE * vplane = yuv + planeSize + (planeSize >> 2); const BYTE * greyIndex = grey; for (unsigned y = 0; y < srcFrameHeight; y++) { BYTE * yline = yplane + (y * srcFrameWidth); BYTE * uline = uplane + ((y >> 1) * halfWidth); BYTE * vline = vplane + ((y >> 1) * halfWidth); if (verticalFlip) greyIndex = grey + srcFrameWidth*(srcFrameHeight-1-y); for (unsigned x = 0; x < srcFrameWidth; x+=2) { greytoy(*greyIndex, *yline); greyIndex++; yline++; greytoyuv(*greyIndex, *yline, *uline, *vline); greyIndex++; yline++; uline++; vline++; } } } // Simple crop/pad version. Image aligned to top-left // and cropped / padded with black borders as required. void PStandardColourConverter::GreytoYUV420PWithResize(const BYTE * grey, BYTE * yuv) const { int planeSize = dstFrameWidth*dstFrameHeight; const int halfWidth = dstFrameWidth >> 1; unsigned min_width, min_height; min_width = (dstFrameWidth < srcFrameWidth) ? dstFrameWidth : srcFrameWidth; min_height = (dstFrameHeight < srcFrameHeight) ? dstFrameHeight : srcFrameHeight; // get pointers to the data BYTE * yplane = yuv; BYTE * uplane = yuv + planeSize; BYTE * vplane = yuv + planeSize + (planeSize >> 2); const BYTE * greyIndex = grey; for (unsigned y = 0; y < min_height; y++) { BYTE * yline = yplane + (y * dstFrameWidth); BYTE * uline = uplane + ((y >> 1) * halfWidth); BYTE * vline = vplane + ((y >> 1) * halfWidth); if (verticalFlip) greyIndex = grey + srcFrameWidth*(min_height-1-y); for (unsigned x = 0; x < min_width; x+=2) { greytoy(*greyIndex, *yline); greyIndex++; yline++; greytoyuv(*greyIndex, *yline, *uline, *vline); greyIndex++; yline++; uline++; vline++; } // Crop if source width > dest width if (srcFrameWidth > dstFrameWidth) greyIndex += srcFrameWidth - dstFrameWidth; // Pad if dest width < source width if (dstFrameWidth > srcFrameWidth) { memset(yline, BLACK_Y, dstFrameWidth - srcFrameWidth); memset(uline, BLACK_U, (dstFrameWidth - srcFrameWidth)>>1); memset(vline, BLACK_V, (dstFrameWidth - srcFrameWidth)>>1); } } // Pad if dest height > source height if (dstFrameHeight > srcFrameHeight) { BYTE * yline = yplane + (srcFrameHeight * dstFrameWidth); BYTE * uline = uplane + ((srcFrameHeight >> 1) * halfWidth); BYTE * vline = vplane + ((srcFrameHeight >> 1) * halfWidth); unsigned fill = (dstFrameHeight - srcFrameHeight) * dstFrameWidth; memset(yline, BLACK_Y, fill); memset(uline, BLACK_U, fill >> 2); memset(vline, BLACK_V, fill >> 2); } } BOOL PStandardColourConverter::GreytoYUV420P(const BYTE * grey, BYTE * yuv, PINDEX * bytesReturned) const { if (grey == yuv) return FALSE; // Cannot do in place conversion if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight)) GreytoYUV420PSameSize(grey, yuv); else GreytoYUV420PWithResize(grey, yuv); if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } #define RGB2Y(r, g, b, y) \ y=(BYTE)(((int)257*(r) +(int)504*(g) +(int)98*(b))/1000) #define RGB2YUV(r, g, b, y, cb, cr) \ RGB2Y(r, g, b, y); \ cb=(BYTE)((-148*(r) -291*(g) +439*(b))/1000 + 128); \ cr=(BYTE)(( 439*(r) -368*(g) - 71*(b))/1000 + 128) void PStandardColourConverter::RGBtoYUV420PSameSize(const BYTE * rgb, BYTE * yuv, unsigned rgbIncrement, unsigned redOffset, unsigned blueOffset) const { const unsigned planeSize = srcFrameWidth*srcFrameHeight; const unsigned halfWidth = srcFrameWidth >> 1; // get pointers to the data BYTE * yplane = yuv; BYTE * uplane = yuv + planeSize; BYTE * vplane = yuv + planeSize + (planeSize >> 2); const BYTE * rgbIndex = rgb; for (unsigned y = 0; y < srcFrameHeight; y++) { BYTE * yline = yplane + (y * srcFrameWidth); BYTE * uline = uplane + ((y >> 1) * halfWidth); BYTE * vline = vplane + ((y >> 1) * halfWidth); if (verticalFlip) rgbIndex = rgb + (srcFrameWidth*(srcFrameHeight-1-y)*rgbIncrement); for (unsigned x = 0; x < srcFrameWidth; x+=2) { RGB2Y(rgbIndex[redOffset], rgbIndex[1], rgbIndex[blueOffset], *yline); rgbIndex += rgbIncrement; yline++; RGB2YUV(rgbIndex[redOffset], rgbIndex[1], rgbIndex[blueOffset], *yline, *uline, *vline); rgbIndex += rgbIncrement; yline++; uline++; vline++; } } } // Simple crop/pad version. Image aligned to top-left // and cropped / padded with black borders as required. void PStandardColourConverter::RGBtoYUV420PWithResize(const BYTE * rgb, BYTE * yuv, unsigned rgbIncrement, unsigned redOffset, unsigned blueOffset) const { int planeSize = dstFrameWidth*dstFrameHeight; const int halfWidth = dstFrameWidth >> 1; unsigned min_width, min_height; min_width = (dstFrameWidth < srcFrameWidth) ? dstFrameWidth : srcFrameWidth; min_height = (dstFrameHeight < srcFrameHeight) ? dstFrameHeight : srcFrameHeight; // get pointers to the data BYTE * yplane = yuv; BYTE * uplane = yuv + planeSize; BYTE * vplane = yuv + planeSize + (planeSize >> 2); const BYTE * rgbIndex = rgb; for (unsigned y = 0; y < min_height; y++) { BYTE * yline = yplane + (y * dstFrameWidth); BYTE * uline = uplane + ((y >> 1) * halfWidth); BYTE * vline = vplane + ((y >> 1) * halfWidth); if (verticalFlip) rgbIndex = rgb + (srcFrameWidth*(min_height-1-y)*rgbIncrement); for (unsigned x = 0; x < min_width; x+=2) { RGB2Y(rgbIndex[redOffset], rgbIndex[1], rgbIndex[blueOffset], *yline); rgbIndex += rgbIncrement; yline++; RGB2YUV(rgbIndex[redOffset], rgbIndex[1], rgbIndex[blueOffset], *yline, *uline, *vline); rgbIndex += rgbIncrement; yline++; uline++; vline++; } // Crop if source width > dest width if (srcFrameWidth > dstFrameWidth) rgbIndex += rgbIncrement * (srcFrameWidth - dstFrameWidth); // Pad if dest width < source width if (dstFrameWidth > srcFrameWidth) { memset(yline, BLACK_Y, dstFrameWidth - srcFrameWidth); memset(uline, BLACK_U, (dstFrameWidth - srcFrameWidth)>>1); memset(vline, BLACK_V, (dstFrameWidth - srcFrameWidth)>>1); } } // Pad if dest height > source height if (dstFrameHeight > srcFrameHeight) { BYTE * yline = yplane + (srcFrameHeight * dstFrameWidth); BYTE * uline = uplane + ((srcFrameHeight >> 1) * halfWidth); BYTE * vline = vplane + ((srcFrameHeight >> 1) * halfWidth); unsigned fill = (dstFrameHeight - srcFrameHeight) * dstFrameWidth; memset(yline, BLACK_Y, fill); memset(uline, BLACK_U, fill >> 2); memset(vline, BLACK_V, fill >> 2); } } BOOL PStandardColourConverter::RGBtoYUV420P(const BYTE * rgb, BYTE * yuv, PINDEX * bytesReturned, unsigned rgbIncrement, unsigned redOffset, unsigned blueOffset) const { if (rgb == yuv) return FALSE; // Cannot do in place conversion if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight)) RGBtoYUV420PSameSize(rgb, yuv, rgbIncrement, redOffset, blueOffset); else RGBtoYUV420PWithResize(rgb, yuv, rgbIncrement, redOffset, blueOffset); if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } PSTANDARD_COLOUR_CONVERTER(Grey,YUV420P) { return GreytoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned); } PSTANDARD_COLOUR_CONVERTER(RGB24,YUV420P) { return RGBtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 0, 2); } PSTANDARD_COLOUR_CONVERTER(BGR24,YUV420P) { return RGBtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 2, 0); } PSTANDARD_COLOUR_CONVERTER(RGB32,YUV420P) { return RGBtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 0, 2); } PSTANDARD_COLOUR_CONVERTER(BGR32,YUV420P) { return RGBtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 2, 0); } /* * Format YUY2 or YUV422(non planar): * * off: 0 Y00 U00 Y01 V00 Y02 U01 Y03 V01 * off: 8 Y10 U10 Y11 V10 Y12 U11 Y13 V11 * off:16 Y20 U20 Y21 V20 Y22 U21 Y23 V21 * off:24 Y30 U30 Y31 V30 Y32 U31 Y33 V31 * length:32 bytes * * Format YUV420P: * off: 00 Y00 Y01 Y02 Y03 * off: 04 Y10 Y11 Y12 Y13 * off: 08 Y20 Y21 Y22 Y23 * off: 12 Y30 Y31 Y32 Y33 * off: 16 U00 U02 U20 U22 * off: 20 V00 V02 V20 V22 * * So, we loose some bit of information when converting YUY2 to YUV420 * * NOTE: This algorithm works only if the width and the height is pair. */ void PStandardColourConverter::YUY2toYUV420PSameSize(const BYTE *yuy2, BYTE *yuv420p) const { const BYTE *s; BYTE *y, *u, *v; unsigned int x, h; int npixels = srcFrameWidth * srcFrameHeight; s = yuy2; y = yuv420p; u = yuv420p + npixels; v = u + npixels/4; for (h=0; h npixels ) { // dest is bigger than the source. No subsampling. // Place the src in the middle of the destination. unsigned int yOffset = (dstFrameHeight - srcFrameHeight)/2; unsigned int xOffset = (dstFrameWidth - srcFrameWidth)/2; unsigned int bpixels = yOffset * dstFrameWidth; /* Top border */ memset(y, BLACK_Y, bpixels); y += bpixels; memset(u, BLACK_U, bpixels/4); u += bpixels/4; memset(v, BLACK_V, bpixels/4); v += bpixels/4; for (h=0; h>FIX_FLOAT; unsigned int yy2 = (fy+dy)>>FIX_FLOAT; const unsigned char *line1, *line2; unsigned char lastU, lastV; line1 = s + (yy*2*srcFrameWidth); line2 = s + (yy2*2*srcFrameWidth); lastU = line1[0]; lastV = line1[2]; for (fx=0, x=0; x>FIX_FLOAT)*2; *y++ = line1[xx+1]; if ( (xx&2) == 0) { *u++ = lastU = (line1[xx+1] + line2[xx+1])/2; *v++ = lastV = (line1[xx+3] + line2[xx+3])/2; } else { *u++ = lastU; *v++ = lastV = (line1[xx+1] + line2[xx+1])/2; } xx = ((fx+dx)>>FIX_FLOAT)*2; *y++ = line1[xx+1]; if ( (xx&2) == 0) lastU = (line1[xx+1] + line2[xx+1])/2; else lastV = (line1[xx+1] + line2[xx+1])/2; } /* Copy the second line without U&V */ for (fx=0, x=0; x>FIX_FLOAT)*2; *y++ = line2[xx]; } } /* end of for (fy=0, h=0; h (srcFrameWidth*srcFrameHeight) ) { //dest is bigger than the source. No subsampling. //Place the src in the middle of the destination. unsigned yOffset = dstFrameHeight - srcFrameHeight; unsigned xOffset = dstFrameWidth - srcFrameWidth; BYTE *s_ptr,*d_ptr; d_ptr = (yOffset * dstFrameWidth) + xOffset + dest; s_ptr = (BYTE *)src; for (unsigned y = 0; y < srcFrameHeight; y++) { memcpy(d_ptr,s_ptr, srcFrameWidth*2); d_ptr += 2*dstFrameWidth; s_ptr += 2*srcFrameWidth; } } else { // source is bigger than the destination. // unsigned subSample = 1 + (srcFrameHeight/dstFrameHeight) ; unsigned yOffset = dstFrameHeight - (srcFrameHeight/subSample); unsigned xOffset = dstFrameWidth - (srcFrameWidth/subSample); unsigned subSample2 = subSample*2; DWORD *s_ptr = (DWORD * )src; DWORD *d_ptr = (DWORD *) dest + ((yOffset * dstFrameWidth) + xOffset)/4 ; DWORD *sl_ptr, *dl_ptr; for (unsigned y = 0; y < srcFrameHeight; y+= subSample) { sl_ptr = s_ptr; dl_ptr = d_ptr; for (unsigned x = 0; x < srcFrameWidth; x+= subSample2) { *dl_ptr++ = *sl_ptr; sl_ptr += subSample; } d_ptr += dstFrameWidth/2; s_ptr += srcFrameWidth*subSample/2; } } } PSTANDARD_COLOUR_CONVERTER(YUV422,YUV422) { if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; if (srcFrameBuffer == dstFrameBuffer) return TRUE; if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight)) memcpy(dstFrameBuffer,srcFrameBuffer,srcFrameWidth*srcFrameHeight*2); else ResizeYUV422(srcFrameBuffer, dstFrameBuffer); return TRUE; } // Consider a YUV420P image of 4x4 pixels. // // A plane of Y values A B C D // E F G H // I J K L // M N O P // // A plane of U values 1 . 2 . // . . . . // 3 . 4 . // . . . . // // A plane of V values 1 . 2 . // . . . . // 3 . 4 . // . . . . // // YUV420P is stored as all Y (w*h), then U (w*h/4), then V // thus, a 4x4 image requires 24 bytes of storage. // // Image has two possible transformations. // padded (src smaller than dst) // subsampled and padded (src bigger than dst) void PStandardColourConverter::ResizeYUV420P(const BYTE * src, BYTE * dest) const { unsigned int i, y, x, npixels; BYTE *d; const BYTE *s; npixels = dstFrameWidth * dstFrameHeight; if ( (dstFrameWidth*dstFrameHeight) > (srcFrameWidth*srcFrameHeight) ) { // dest is bigger than the source. No subsampling. // Place the src in the middle of the destination. unsigned int yOffset = (dstFrameHeight - srcFrameHeight)/2; unsigned int xOffset = (dstFrameWidth - srcFrameWidth)/2; d = dest; for (i=0; i < npixels; i++) *d++ = BLACK_Y; for (i=0; i < npixels/4; i++) *d++ = BLACK_U; for (i=0; i < npixels/4; i++) *d++ = BLACK_V; // Copy plane Y d = dest + yOffset * dstFrameWidth + xOffset; s = src; for (y = 0; y < srcFrameHeight; y++) { memcpy(d, s, srcFrameWidth); s += srcFrameWidth; d += dstFrameWidth; } // Copy plane U d = dest + npixels + (yOffset*dstFrameWidth/4) + xOffset/2; for (y = 0; y < srcFrameHeight/2; y++) { memcpy(d, s, srcFrameWidth/2); s += srcFrameWidth/2; d += dstFrameWidth/2; } // Copy plane V d = dest + npixels + npixels/4 + (yOffset*dstFrameWidth/4) + xOffset/2; for (y = 0; y < srcFrameHeight/2; y++) { memcpy(d, s, srcFrameWidth/2); s += srcFrameWidth/2; d += dstFrameWidth/2; } } else { // source is bigger than the destination. // #define FIX_FLOAT 12 unsigned int dx = (srcFrameWidth<>FIX_FLOAT) * srcFrameWidth; for (fx=0, x=0; x>FIX_FLOAT]; } } /* Copy Plane U */ src += srcFrameWidth*srcFrameHeight; for (fy=0, y=0; y>FIX_FLOAT) * srcFrameWidth/2; for (fx=0, x=0; x>FIX_FLOAT]; } } /* Copy Plane V */ src += srcFrameWidth*srcFrameHeight/4; for (fy=0, y=0; y>FIX_FLOAT) * srcFrameWidth/2; for (fx=0, x=0; x>FIX_FLOAT]; } } } } PSTANDARD_COLOUR_CONVERTER(YUV420P,YUV420P) { if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; if (srcFrameBuffer == dstFrameBuffer) return TRUE; if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight)) memcpy(dstFrameBuffer,srcFrameBuffer,srcFrameWidth*srcFrameHeight*3/2); else ResizeYUV420P(srcFrameBuffer, dstFrameBuffer); return TRUE; } /* * Format YUY2 or YUV422(non planar): * * off: 0 Y00 U00 Y01 V00 Y02 U01 Y03 V01 * off: 8 Y10 U10 Y11 V10 Y12 U11 Y13 V11 * off:16 Y20 U20 Y21 V20 Y22 U21 Y23 V21 * off:24 Y30 U30 Y31 V30 Y32 U31 Y33 V31 * length:32 bytes * * Format YUV420P: * off: 00 Y00 Y01 Y02 Y03 * off: 04 Y10 Y11 Y12 Y13 * off: 08 Y20 Y21 Y22 Y23 * off: 12 Y30 Y31 Y32 Y33 * off: 16 U00 U02 U20 U22 * off: 20 V00 V02 V20 V22 * * So, we loose some bit of information when converting YUY2 to YUV420 * */ PSTANDARD_COLOUR_CONVERTER(YUV422,YUV420P) { if ((srcFrameWidth | dstFrameWidth | srcFrameHeight | dstFrameHeight) & 1) { PTRACE(2,"PColCnv\tError in YUV422 to YUV420P converter, All size need to be pair."); return FALSE; } if ((srcFrameWidth==dstFrameWidth) && (srcFrameHeight==dstFrameHeight)) YUY2toYUV420PSameSize(srcFrameBuffer, dstFrameBuffer); else YUY2toYUV420PWithResize(srcFrameBuffer, dstFrameBuffer); if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } #define LIMIT(x) (unsigned char) ((x > 255) ? 255 : ((x < 0) ? 0 : x )) static inline int clip(int a, int limit) { return aRGB->YUV420P) #if USE_SBGGR8_NATIVE // kernels for Y conversion, normalised by 2^16 const int kR[]={1802,9667,1802,9667,19661,9667,1802,9667,1802}; const int kG1[]={7733,9830,7733,3604,7733,3604,7733,9830,7733}; const int kG2[]={7733,3604,7733,9830,7733,9830,7733,3604,7733}; const int kB[]={4915,9667,4915,9667,7209,9667,4915,9667,4915}; // const int kID[]={0,0,0,0,65536,0,0,0,0}; identity kernel, use to test int B, G, G1, G2, R; const int stride = srcFrameWidth; unsigned const int hSize =srcFrameHeight/2; unsigned const int vSize =srcFrameWidth/2; unsigned const int lastRow=srcFrameHeight-1; unsigned const int lastCol=srcFrameWidth-1; unsigned int i,j; const BYTE *sBayer = src; // Y = round( 0.256788 * R + 0.504129 * G + 0.097906 * B) + 16; // Y = round( 0.30 * R + 0.59 * G + 0.11 * B ) use this! // U = round(-0.148223 * R - 0.290993 * G + 0.439216 * B) + 128; // V = round( 0.439216 * R - 0.367788 * G - 0.071427 * B) + 128; // Compute U and V planes using EXACT values, reading 2x2 pixels at a time BYTE *dU = dst+srcFrameHeight*srcFrameWidth; BYTE *dV = dU+hSize*vSize; for (i=0; i> 17) + 128 ); *dV = (BYTE)( ( ( 57569 * R -24103*G -9362 * B) >> 17) + 128 ); sBayer+=2; dU++; dV++; } sBayer+=stride; // skip odd lines } // Compute Y plane BYTE *dY = dst; sBayer=src; const int * k; // kernel pointer int dxLeft, dxRight; // precalculated offsets, needed for first and last column const BYTE *sBayerTop, *sBayerBottom; for (i=0; i> 16); dY++; sBayer++; sBayerTop++; sBayerBottom++; dxLeft=-1; } } if (bytesReturned) *bytesReturned = srcFrameHeight*srcFrameWidth+2*hSize*vSize; return true; #else //USE_SBGGR8_NATIVE // shortest but less efficient (one malloc per conversion!) BYTE * tempDest=(BYTE*)malloc(3*srcFrameWidth*srcFrameHeight); SBGGR8toRGB(src, tempDest, NULL); BOOL r = RGBtoYUV420P(tempDest, dst, bytesReturned, 3, 2, 0); free(tempDest); return r; #endif //USE_SBGGR8_NATIVE } BOOL PStandardColourConverter::SBGGR8toRGB(const BYTE * src, BYTE * dst, PINDEX * bytesReturned) const { if (src == dst || verticalFlip) return FALSE; long int i; const BYTE *rawpt; BYTE *scanpt; long int size; rawpt = src; scanpt = dst; long int WIDTH = srcFrameWidth, HEIGHT = srcFrameHeight; size = WIDTH*HEIGHT; for ( i = 0; i < size; i++ ) { if ( (i/WIDTH) % 2 == 0 ) { if ( (i % 2) == 0 ) { /* B */ if ( (i > WIDTH) && ((i % WIDTH) > 0) ) { *scanpt++ = (BYTE) ((*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4); /* R */ *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt+1)+ *(rawpt+WIDTH)+*(rawpt-WIDTH))/4); /* G */ *scanpt++ = *rawpt; /* B */ } else { /* first line or left column */ *scanpt++ = *(rawpt+WIDTH+1); /* R */ *scanpt++ = (BYTE) ((*(rawpt+1)+*(rawpt+WIDTH))/2); /* G */ *scanpt++ = *rawpt; /* B */ } } else { /* (B)G */ if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) { *scanpt++ = (BYTE) ((*(rawpt+WIDTH)+*(rawpt-WIDTH))/2); /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt+1))/2); /* B */ } else { /* first line or right column */ *scanpt++ = *(rawpt+WIDTH); /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = *(rawpt-1); /* B */ } } } else { if ( (i % 2) == 0 ) { /* G(R) */ if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) { *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt+1))/2); /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = (BYTE) ((*(rawpt+WIDTH)+*(rawpt-WIDTH))/2); /* B */ } else { /* bottom line or left column */ *scanpt++ = *(rawpt+1); /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = *(rawpt-WIDTH); /* B */ } } else { /* R */ if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) { *scanpt++ = *rawpt; /* R */ *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt+1)+*(rawpt-WIDTH)+*(rawpt+WIDTH))/4); /* G */ *scanpt++ = (BYTE) ((*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4); /* B */ } else { /* bottom line or right column */ *scanpt++ = *rawpt; /* R */ *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt-WIDTH))/2); /* G */ *scanpt++ = *(rawpt-WIDTH-1); /* B */ } } } rawpt++; } if (bytesReturned) *bytesReturned = scanpt - dst; return TRUE; } #define SCALEBITS 12 #define ONE_HALF (1UL << (SCALEBITS - 1)) #define FIX(x) ((int) ((x) * (1UL<>SCALEBITS; g = (l+gd)>>SCALEBITS; b = (l+bd)>>SCALEBITS; BYTE * rgpPtr = dstImageFrame + rgbIncrement*pixpos[p]; rgpPtr[redOffset ] = LIMIT(r); rgpPtr[1 ] = LIMIT(g); rgpPtr[blueOffset] = LIMIT(b); if (rgbIncrement == 4) rgpPtr[3] = 0; } yplane += 2; dstImageFrame += rgbIncrement*2; uplane++; vplane++; } yplane += srcFrameWidth; if (verticalFlip) dstImageFrame -= 3*rgbIncrement*srcFrameWidth; else dstImageFrame += rgbIncrement*srcFrameWidth; } if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } PSTANDARD_COLOUR_CONVERTER(SBGGR8,RGB24) { return SBGGR8toRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned); } PSTANDARD_COLOUR_CONVERTER(SBGGR8,YUV420P) { return SBGGR8toYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned); } PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB24) { return YUV420PtoRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 0, 2); } PSTANDARD_COLOUR_CONVERTER(YUV420P,BGR24) { return YUV420PtoRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 2, 0); } PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB32) { return YUV420PtoRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 0, 2); } PSTANDARD_COLOUR_CONVERTER(YUV420P,BGR32) { return YUV420PtoRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 2, 0); } static void SwapRedAndBlueRow(const BYTE * srcRowPtr, BYTE * dstRowPtr, unsigned width, unsigned srcIncrement, unsigned dstIncrement) { for (unsigned x = 0; x < width; x++) { BYTE temp = srcRowPtr[0]; // Do it this way in case src and dst are same buffer dstRowPtr[0] = srcRowPtr[2]; dstRowPtr[1] = srcRowPtr[1]; dstRowPtr[2] = temp; srcRowPtr += srcIncrement; dstRowPtr += dstIncrement; } } BOOL PStandardColourConverter::SwapRedAndBlue(const BYTE * srcFrameBuffer, BYTE * dstFrameBuffer, PINDEX * bytesReturned, unsigned srcIncrement, unsigned dstIncrement) const { if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) return FALSE; unsigned srcRowSize = srcFrameBytes/srcFrameHeight; const BYTE * srcRowPtr = srcFrameBuffer; unsigned dstRowSize = dstFrameBytes/dstFrameHeight; BYTE * dstRowPtr = dstFrameBuffer; if (verticalFlip) { dstRowPtr += dstFrameHeight*dstRowSize; if (srcFrameBuffer == dstFrameBuffer) { PBYTEArray tempRow(PMAX(srcRowSize, dstRowSize)); unsigned halfHeight = (srcFrameHeight+1)/2; for (unsigned y = 0; y < halfHeight; y++) { dstRowPtr -= dstRowSize; SwapRedAndBlueRow(dstRowPtr, tempRow.GetPointer(), dstFrameWidth, srcIncrement, dstIncrement); SwapRedAndBlueRow(srcRowPtr, dstRowPtr, srcFrameWidth, srcIncrement, dstIncrement); memcpy((BYTE *)srcRowPtr, tempRow, srcRowSize); srcRowPtr += srcRowSize; } } else { for (unsigned y = 0; y < srcFrameHeight; y++) { dstRowPtr -= dstRowSize; SwapRedAndBlueRow(srcRowPtr, dstRowPtr, srcFrameWidth, srcIncrement, dstIncrement); srcRowPtr += srcRowSize; } } } else { for (unsigned y = 0; y < srcFrameHeight; y++) { SwapRedAndBlueRow(srcRowPtr, dstRowPtr, srcFrameWidth, srcIncrement, dstIncrement); srcRowPtr += srcRowSize; dstRowPtr += dstRowSize; } } if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } PSTANDARD_COLOUR_CONVERTER(RGB24,BGR24) { return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 3); } PSTANDARD_COLOUR_CONVERTER(BGR24,RGB24) { return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 3); } PSTANDARD_COLOUR_CONVERTER(RGB24,BGR32) { return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 4); } PSTANDARD_COLOUR_CONVERTER(BGR24,RGB32) { return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 4); } PSTANDARD_COLOUR_CONVERTER(RGB32,BGR24) { return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 3); } PSTANDARD_COLOUR_CONVERTER(BGR32,RGB24) { return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 3); } PSTANDARD_COLOUR_CONVERTER(RGB32,BGR32) { return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 4); } PSTANDARD_COLOUR_CONVERTER(BGR32,RGB32) { return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 4); } PSTANDARD_COLOUR_CONVERTER(RGB24,RGB32) { if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) return FALSE; // Go from bottom to top so can do in place conversion const BYTE * src = srcFrameBuffer+srcFrameBytes-1; BYTE * dst = dstFrameBuffer+dstFrameBytes-1; for (unsigned x = 0; x < srcFrameWidth; x++) { for (unsigned y = 0; y < srcFrameHeight; y++) { *dst-- = 0; for (unsigned p = 0; p < 3; p++) *dst-- = *src--; } } if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } PSTANDARD_COLOUR_CONVERTER(RGB32,RGB24) { if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) return FALSE; const BYTE * src = srcFrameBuffer; BYTE * dst = dstFrameBuffer; for (unsigned x = 0; x < srcFrameWidth; x++) { for (unsigned y = 0; y < srcFrameHeight; y++) { for (unsigned p = 0; p < 3; p++) *dst++ = *src++; src++; } } if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } // Consider a YUV420P image of 8x2 pixels. // // A plane of Y values A B C D E F G H // I J K L M N O P // // A plane of U values 1 2 3 4 // A plane of V values 1 2 3 4 .... // // The U1/V1 samples correspond to the ABIJ pixels. // U2/V2 samples correspond to the CDKL pixels. // // Consider a YUV411P image of 8x2 pixels. // // A plane of Y values as before. // // A plane of U values 1 2 // 3 4 // // A plane of V values 1 2 // 3 4 // // The U1/V1 samples correspond to the ABCD pixels. // U2/V2 samples correspond to the EFGH pixels. // // I choose to reoganize the U and V samples by using // using U1 for ABCD, U3 for EFGH, U2 for IJKL, U4 for MNOP // // Possibly discarding U2/U4 completely, or using the // average of U1 and U2 might be easier for compression // // TODO: // // - Inplace converter // - Resizing / padding / scaling converter // PSTANDARD_COLOUR_CONVERTER(YUV420P,YUV411P) { if (srcFrameBuffer == dstFrameBuffer) return FALSE; if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) return FALSE; // Copy over the Y plane. memcpy(dstFrameBuffer, srcFrameBuffer, srcFrameWidth*srcFrameHeight); unsigned linewidth = dstFrameWidth / 4; // Source data is the start of the U plane const BYTE* src = srcFrameBuffer + srcFrameWidth * srcFrameHeight; // Two output lines at a time BYTE *dst0 = dstFrameBuffer + dstFrameWidth * dstFrameHeight; BYTE *dst1 = dst0 + linewidth; unsigned x, y; // U plane for (y = 0; y < dstFrameHeight; y += 2) { for (x = 0; x < dstFrameWidth; x += 4) { *dst0++ = *src++; *dst1++ = *src++; } // Skip over the 2nd line we already did. dst0 += linewidth; dst1 = dst0 + linewidth; } // Source data is the start of the U plane src = srcFrameBuffer + srcFrameWidth * srcFrameHeight * 5 / 4; // Two output lines at a time dst0 = dstFrameBuffer + dstFrameWidth * dstFrameHeight * 5 / 4; dst1 = dst0 + linewidth; // V plane for (y = 0; y < dstFrameHeight; y += 2) { for (x = 0; x < dstFrameWidth; x += 4) { *dst0++ = *src++; *dst1++ = *src++; } // Skip over the 2nd line we already did. dst0 += linewidth; dst1 = dst0 + linewidth; } if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } // YUV411P to YUV420P conversion // // Consider YUV411P U plane (. = pixel) : // // A... B... C... D... // E... F... G... H... // I... J... K... L... // M... N... O... P... // // We map this to a YUV420P plane by // discarding odd rows, and doubling up // the even row samples: // // A.A. B.B. C.C. D.D. // .... .... .... .... // I.I. J.J. K.K. L.L. // .... .... .... .... // // TODO: // // - Inplace converter // - Resizing / padding / scaling converter // PSTANDARD_COLOUR_CONVERTER(YUV411P,YUV420P) { if (srcFrameBuffer == dstFrameBuffer) return FALSE; if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) return FALSE; // Copy over the Y plane. memcpy(dstFrameBuffer, srcFrameBuffer, srcFrameWidth*srcFrameHeight); unsigned linewidth = dstFrameWidth / 4; // Source data is the start of the U plane const BYTE* src = srcFrameBuffer + srcFrameWidth * srcFrameHeight; // Output line BYTE *dst0 = dstFrameBuffer + dstFrameWidth * dstFrameHeight; unsigned x, y; // U plane for (y = 0; y < dstFrameHeight; y += 2) { for (x = 0; x < dstFrameWidth; x += 4) { // Double up the horizontal samples *dst0++ = *src; *dst0++ = *src++; } // Skip over the 2nd line we are decimating src += linewidth; } // Source data is the start of the U plane src = srcFrameBuffer + srcFrameWidth * srcFrameHeight * 5 / 4; // Output line dst0 = dstFrameBuffer + dstFrameWidth * dstFrameHeight * 5 / 4; // V plane for (y = 0; y < dstFrameHeight; y += 2) { for (x = 0; x < dstFrameWidth; x += 4) { // Double up the samples horizontal samples *dst0++ = *src; *dst0++ = *src++; } // Skip over the 2nd source line we already did. src += linewidth; } if (bytesReturned != NULL) *bytesReturned = dstFrameBytes; return TRUE; } /* * Format UYVY or UYVY422(non planar) 4x4 * * off: 0 U00 Y00 V01 Y00 U02 Y01 V03 Y01 * off: 8 U10 Y10 V11 Y10 U12 Y11 V13 Y11 * off:16 U20 Y20 V21 Y20 U22 Y21 V23 Y21 * off:24 U30 Y30 V31 Y30 U32 Y31 V33 Y31 * length:32 bytes * * Format YUV420P: * off: 00 Y00 Y01 Y02 Y03 * off: 04 Y10 Y11 Y12 Y13 * off: 08 Y20 Y21 Y22 Y23 * off: 12 Y30 Y31 Y32 Y33 * off: 16 U00 U02 U20 U22 * off: 20 V00 V02 V20 V22 * * So, we loose some bit of information when converting UYVY to YUV420 * * NOTE: This algorithm works only if the width and the height is pair. */ void PStandardColourConverter::UYVY422toYUV420PSameSize(const BYTE *uyvy, BYTE *yuv420p) const { const BYTE *s; BYTE *y, *u, *v; unsigned int x, h; int npixels = srcFrameWidth * srcFrameHeight; s = uyvy; y = yuv420p; u = yuv420p + npixels; v = u + npixels/4; for (h=0; h>FIX_FLOAT; unsigned int yy2 = (fy+dy)>>FIX_FLOAT; const unsigned char *line1, *line2; unsigned char lastU, lastV; line1 = s + (yy*2*srcFrameWidth); line2 = s + (yy2*2*srcFrameWidth); lastU = line1[0]; lastV = line1[2]; for (fx=0, x=0; x>FIX_FLOAT)*2; if ( (xx&2) == 0) { *u++ = lastU = (line1[xx+0] + line2[xx+0])/2; *v++ = lastV = (line1[xx+2] + line2[xx+2])/2; } else { *u++ = lastU; *v++ = lastV = (line1[xx+0] + line2[xx+0])/2; } *y++ = line1[xx+1]; xx = ((fx+dx)>>FIX_FLOAT)*2; if ( (xx&2) == 0) lastU = (line1[xx+0] + line2[xx+0])/2; else lastV = (line1[xx+0] + line2[xx+0])/2; *y++ = line1[xx+1]; } /* Copy the second line without U&V */ for (fx=0, x=0; x>FIX_FLOAT)*2; *y++ = line2[xx+1]; } } /* end of for (fy=0, h=0; h