1 /*
2  * vconvert.cxx
3  *
4  * Classes to support streaming video input (grabbing) and output.
5  *
6  * Portable Windows Library
7  *
8  * Copyright (c) 1993-2000 Equivalence Pty. Ltd.
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Portable Windows Library.
21  *
22  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23  *
24  * Contributor(s): Derek Smithies (derek@indranet.co.nz)
25  *   Thorsten Westheider (thorsten.westheider@teleos-web.de)
26  *   Mark Cooke (mpc@star.sr.bham.ac.uk)
27  *
28  * $Revision: 26686 $
29  * $Author: rjongbloed $
30  * $Date: 2011-11-23 20:22:20 -0600 (Wed, 23 Nov 2011) $
31  */
32 
33 #include <ptlib.h>
34 
35 #if P_VIDEO
36 
37 #include <ptlib/video.h>
38 
39 #ifdef __GNUC__
40 #pragma implementation "vconvert.h"
41 #endif
42 
43 #include <ptlib/vconvert.h>
44 
45 #if  defined(__GNUC__) || defined(__sun)
46 #include "tinyjpeg.h"
47 #endif
48 
49 
50 #ifdef _MSC_VER
51 #pragma warning(disable : 4244)
52 #endif
53 
54 
55 #ifdef P_MEDIALIB
56 #include <mlib.h>
57 #endif
58 
59 static PColourConverterRegistration * RegisteredColourConvertersListHead = NULL;
60 
61 PSYNONYM_COLOUR_CONVERTER(SBGGR8, SBGGR8);
62 PSYNONYM_COLOUR_CONVERTER(Grey,   Grey);
63 PSYNONYM_COLOUR_CONVERTER(RGB24,  RGB24);
64 PSYNONYM_COLOUR_CONVERTER(BGR24,  BGR24);
65 PSYNONYM_COLOUR_CONVERTER(RGB32,  RGB32);
66 PSYNONYM_COLOUR_CONVERTER(BGR32,  BGR32);
67 PSYNONYM_COLOUR_CONVERTER(UYVY422,UYVY422);
68 PSYNONYM_COLOUR_CONVERTER(YUV411P,YUV411P);
69 PSYNONYM_COLOUR_CONVERTER(YUV420P,IYUV);
70 PSYNONYM_COLOUR_CONVERTER(IYUV,   YUV420P);
71 PSYNONYM_COLOUR_CONVERTER(YUV420P,I420);
72 PSYNONYM_COLOUR_CONVERTER(I420,   YUV420P);
73 
74 
75 class PStandardColourConverter : public PColourConverter
76 {
77   PCLASSINFO(PStandardColourConverter, PColourConverter);
78 
79   protected:
PStandardColourConverter(const PVideoFrameInfo & src,const PVideoFrameInfo & dst)80     PStandardColourConverter(
81       const PVideoFrameInfo & src,
82       const PVideoFrameInfo & dst
83     ) : PColourConverter(src, dst) { }
84 
85     bool SBGGR8toYUV420P(
86      const BYTE * srgb,
87       BYTE * rgb,
88       PINDEX * bytesReturned
89     );
90     bool SBGGR8toRGB(
91       const BYTE * srgb,
92       BYTE * rgb,
93       PINDEX * bytesReturned
94     ) const;
95     void GreytoYUV420PSameSize(
96       const BYTE * rgb,
97       BYTE * yuv
98     ) const;
99     void GreytoYUV420PWithCrop(
100       const BYTE * rgb,
101       BYTE * yuv
102     ) const;
103     bool GreytoYUV420P(
104       const BYTE * rgb,
105       BYTE * yuv,
106       PINDEX * bytesReturned
107     ) const;
108     void RGBtoYUV420PSameSize(
109       const BYTE * rgb,
110       BYTE * yuv,
111       unsigned rgbIncrement,
112       unsigned redOffset,
113       unsigned blueOffset
114     ) const;
115     void RGBtoYUV420PWithCrop(
116       const BYTE * rgb,
117       BYTE * yuv,
118       unsigned rgbIncrement,
119       unsigned redOffset,
120       unsigned blueOffset
121     ) const;
122     bool RGBtoYUV420P(
123       const BYTE * rgb,
124       BYTE * yuv,
125       PINDEX * bytesReturned,
126       unsigned rgbIncrement,
127       unsigned redOffset,
128       unsigned blueOffset
129     );
130     bool YUV420PtoRGB(
131       const BYTE * yuv,
132       BYTE * rgb,
133       PINDEX * bytesReturned,
134       unsigned rgbIncrement,
135       unsigned redOffset,
136       unsigned blueOffset
137     ) const;
138     PBoolean YUV420PtoRGB565(
139       const BYTE * yuv,
140       BYTE * rgb,
141       PINDEX * bytesReturned
142     ) const;
143     bool SwapRedAndBlue(
144       const BYTE * src,
145       BYTE * dst,
146       PINDEX * bytesReturned,
147       unsigned srcIncrement,
148       unsigned dstIncrement
149     ) const;
150     void UYVY422WithCrop(
151       const BYTE *src_uyvy,
152       BYTE *dst_uyvy
153     ) const;
154     void YUV422WithCrop(
155       const BYTE * src,
156       BYTE * dest,
157       bool centred
158     ) const;
159     void UYVY422toYUV420PSameSize(
160       const BYTE *uyvy,
161       BYTE *yuv420p
162     ) const;
163     void UYVY422toYUV420PWithCrop(
164       const BYTE *uyvy,
165       BYTE *yuv420p
166     ) const;
167     void YUY2toYUV420PSameSize(
168       const BYTE *yuy2,
169       BYTE *yuv420p
170     ) const;
171     void YUY2toYUV420PWithGrow(
172       const BYTE *yuy2,
173       BYTE *yuv420p
174     ) const;
175     void YUY2toYUV420PWithShrink(
176       const BYTE *yuy2,
177       BYTE *yuv420p
178     ) const;
179     bool MJPEGtoYUV420PSameSize(
180       const BYTE *yuy2,
181       BYTE *yuv420p
182     );
183 #if defined (__GNUC__) || defined (__sun)
184     bool MJPEGtoXXX(
185       const BYTE *mjpeg,
186             BYTE *output_data,
187             PINDEX *bytesReturned,
188             int format
189     );
190     bool MJPEGtoYUV420P(
191       const BYTE *mjpeg,
192       BYTE *yuv420p,
193       PINDEX *bytesReturned
194     );
195     bool MJPEGtoXXXSameSize(
196       const BYTE *yuy2,
197       BYTE *rgb,
198       int format
199     );
200 #endif
201 };
202 
203 
204 #define PSTANDARD_COLOUR_CONVERTER(from,to) \
205   PCOLOUR_CONVERTER2(P_##from##_##to,PStandardColourConverter,#from,#to)
206 
207 
208 #define BLACK_Y 0
209 #define BLACK_U 128
210 #define BLACK_V 128
211 
212 
213 #define new PNEW
214 
215 
216 ///////////////////////////////////////////////////////////////////////////////
217 // PColourConverter
218 
PColourConverterRegistration(const PString & srcColourFormat,const PString & destColourFormat)219 PColourConverterRegistration::PColourConverterRegistration(const PString & srcColourFormat,
220                                                            const PString & destColourFormat)
221   : PCaselessString(srcColourFormat+'\t'+destColourFormat)
222 {
223   PColourConverterRegistration * test = RegisteredColourConvertersListHead;
224   while (test != NULL) {
225     if (*test == *this)
226       return;
227     test = test->link;
228   }
229 
230   link = RegisteredColourConvertersListHead;
231   RegisteredColourConvertersListHead = this;
232 }
233 
234 
Create(const PVideoFrameInfo & src,const PVideoFrameInfo & dst)235 PColourConverter * PColourConverter::Create(const PVideoFrameInfo & src,
236                                             const PVideoFrameInfo & dst)
237 {
238   PString converterName = src.GetColourFormat() + '\t' + dst.GetColourFormat();
239 
240   PColourConverterRegistration * find = RegisteredColourConvertersListHead;
241   while (find != NULL) {
242     if (*find == converterName) {
243       return find->Create(src, dst);
244     }
245     find = find->link;
246   }
247 
248   PTRACE(2,"PColCnv\tCreate error. Did not find " << src.GetColourFormat() << "->" << dst.GetColourFormat());
249   return NULL;
250 }
251 
Create(const PString & srcColourFormat,const PString & destColourFormat,unsigned width,unsigned height)252 PColourConverter * PColourConverter::Create(const PString & srcColourFormat,
253                                             const PString & destColourFormat,
254                                             unsigned width,
255                                             unsigned height)
256 {
257   PVideoFrameInfo src;
258   src.SetColourFormat(srcColourFormat);
259   src.SetFrameSize(width, height);
260 
261   PVideoFrameInfo dst;
262   dst.SetColourFormat(destColourFormat);
263 
264   return Create(src, dst);
265 }
266 
267 
PColourConverter(const PString & srcColourFmt,const PString & dstColourFmt,unsigned width,unsigned height)268 PColourConverter::PColourConverter(const PString & srcColourFmt,
269                                    const PString & dstColourFmt,
270                                    unsigned width,
271                                    unsigned height)
272 {
273   Construct(PVideoFrameInfo(width, height, srcColourFmt),
274             PVideoFrameInfo(width, height, dstColourFmt));
275 }
276 
PColourConverter(const PVideoFrameInfo & src,const PVideoFrameInfo & dst)277 PColourConverter::PColourConverter(const PVideoFrameInfo & src,
278                                    const PVideoFrameInfo & dst)
279 {
280   Construct(src, dst);
281 }
282 
283 
Construct(const PVideoFrameInfo & src,const PVideoFrameInfo & dst)284 void PColourConverter::Construct(const PVideoFrameInfo & src, const PVideoFrameInfo & dst)
285 {
286 #ifndef P_MACOSX
287   jdec = NULL;
288 #endif
289 
290   srcColourFormat = src.GetColourFormat();
291   src.GetFrameSize(srcFrameWidth, srcFrameHeight);
292   srcFrameBytes = src.CalculateFrameBytes();
293 
294   dstColourFormat = dst.GetColourFormat();
295   dst.GetFrameSize(dstFrameWidth, dstFrameHeight);
296   dstFrameBytes = dst.CalculateFrameBytes();
297 
298   resizeMode = dst.GetResizeMode();
299 
300   verticalFlip = false;
301 
302   PTRACE(4,"PColCnv\tPColourConverter constructed: " << src << " -> " << dst);
303 }
304 
305 
PrintOn(ostream & strm) const306 void PColourConverter::PrintOn(ostream & strm) const
307 {
308   strm << srcColourFormat << ':' << srcFrameWidth << 'x' << srcFrameHeight << "->"
309        << dstColourFormat << ':' << dstFrameWidth << 'x' << dstFrameHeight << '/'
310        << resizeMode;
311 }
312 
313 
SetFrameSize(unsigned width,unsigned height)314 PBoolean PColourConverter::SetFrameSize(unsigned width, unsigned height)
315 {
316   bool ok1 = SetSrcFrameSize(width, height);
317   bool ok2 = SetDstFrameSize(width, height);
318   PTRACE(2,"PColCnv\tSetFrameSize: " << width << 'x' << height
319          << (ok1 && ok2 ? " OK" : " Failed"));
320   return ok1 && ok2;
321 }
322 
323 
SetSrcFrameInfo(const PVideoFrameInfo & info)324 PBoolean PColourConverter::SetSrcFrameInfo(const PVideoFrameInfo & info)
325 {
326   if (!PAssert(info.GetColourFormat() != GetSrcColourFormat(), "Cannot change colour format"))
327     return false;
328 
329   unsigned w, h;
330   return info.GetFrameSize(w, h) && SetSrcFrameSize(w, h);
331 }
332 
333 
SetDstFrameInfo(const PVideoFrameInfo & info)334 PBoolean PColourConverter::SetDstFrameInfo(const PVideoFrameInfo & info)
335 {
336   if (!PAssert(info.GetColourFormat() != GetDstColourFormat(), "Cannot change colour format"))
337     return false;
338 
339   SetResizeMode(info.GetResizeMode());
340 
341   unsigned w, h;
342   return info.GetFrameSize(w, h) && SetDstFrameSize(w, h);
343 }
344 
345 
GetSrcFrameInfo(PVideoFrameInfo & info)346 void PColourConverter::GetSrcFrameInfo(PVideoFrameInfo & info)
347 {
348   info.SetColourFormat(GetSrcColourFormat());
349   info.SetFrameSize(srcFrameWidth, srcFrameHeight);
350 }
351 
352 
GetDstFrameInfo(PVideoFrameInfo & info)353 void PColourConverter::GetDstFrameInfo(PVideoFrameInfo & info)
354 {
355   info.SetColourFormat(GetDstColourFormat());
356   info.SetFrameSize(dstFrameWidth, dstFrameHeight);
357 }
358 
359 
SetSrcFrameSize(unsigned width,unsigned height)360 PBoolean PColourConverter::SetSrcFrameSize(unsigned width, unsigned height)
361 {
362   if (srcFrameWidth == width && srcFrameHeight == height)
363     return true;
364 
365   srcFrameWidth = width;
366   srcFrameHeight = height;
367   srcFrameBytes = PVideoDevice::CalculateFrameBytes(srcFrameWidth, srcFrameHeight, srcColourFormat);
368   PTRACE(srcFrameBytes != 0 ? 6 : 2, "PColCnv\tSetSrcFrameSize "
369          << ((srcFrameBytes != 0) ? "Succeed": "Fail") << "ed, "
370          << srcColourFormat << ' ' << srcFrameWidth << 'x' << srcFrameHeight
371          << ", " << srcFrameBytes << " bytes.");
372 
373   return srcFrameBytes != 0;
374 }
375 
376 
SetDstFrameSize(unsigned width,unsigned height)377 PBoolean PColourConverter::SetDstFrameSize(unsigned width, unsigned height)
378 {
379   dstFrameWidth  = width;
380   dstFrameHeight = height;
381 
382   dstFrameBytes = PVideoDevice::CalculateFrameBytes(dstFrameWidth, dstFrameHeight, dstColourFormat);
383 
384   PTRACE(dstFrameBytes != 0 ? 6 : 2, "PColCnv\tSetDstFrameSize "
385          << ((dstFrameBytes != 0) ? "Succeed": "Fail") << "ed, "
386          << dstColourFormat << ' ' << dstFrameWidth << 'x' << dstFrameHeight
387          << ", " << dstFrameBytes << " bytes.");
388 
389   return dstFrameBytes != 0;
390 }
391 
SetDstFrameSize(unsigned width,unsigned height,PBoolean bScale)392 PBoolean PColourConverter::SetDstFrameSize(unsigned width, unsigned height, PBoolean bScale)
393 {
394   if (!SetDstFrameSize(width, height))
395     return false;
396 
397   if (bScale)
398     SetResizeMode(PVideoFrameInfo::eScale);
399   else
400     SetResizeMode(PVideoFrameInfo::eCropCentre);
401 
402   return true;
403 }
404 
GetSrcFrameSize(unsigned & width,unsigned & height) const405 PBoolean PColourConverter::GetSrcFrameSize(unsigned &width, unsigned &height) const
406 {
407   width = srcFrameWidth;
408   height = srcFrameHeight;
409   return true;
410 }
411 
412 
GetDstFrameSize(unsigned & width,unsigned & height) const413 PBoolean PColourConverter::GetDstFrameSize(unsigned &width, unsigned &height) const
414 {
415   width = dstFrameWidth;
416   height = dstFrameHeight;
417   return true;
418 }
419 
420 
ConvertInPlace(BYTE * frameBuffer,PINDEX * bytesReturned,PBoolean noIntermediateFrame)421 PBoolean PColourConverter::ConvertInPlace(BYTE * frameBuffer,
422                                       PINDEX * bytesReturned,
423                                       PBoolean noIntermediateFrame)
424 {
425   if (Convert(frameBuffer, frameBuffer, bytesReturned))
426     return true;
427 
428   if (noIntermediateFrame) {
429     PTRACE(2,"PColCnv\tError in ConvertInPlace, no intermediate frame available.");
430     return false;
431   }
432 
433   BYTE * intermediate = intermediateFrameStore.GetPointer(dstFrameBytes);
434   PINDEX bytes;
435   if (!Convert(frameBuffer, intermediate, &bytes))
436     return false;
437 
438   memcpy(frameBuffer, intermediate, bytes);
439   if (bytesReturned != NULL)
440     *bytesReturned = bytes;
441   return true;
442 }
443 
444 
445 #define RGB2Y(r, g, b, y) \
446   y=(BYTE)(((int)257*(r)  +(int)504*(g) +(int)98*(b))/1000)
447 
448 #define RGB2YUV(r, g, b, y, cb, cr) \
449   RGB2Y(r, g, b, y); \
450   cb=(BYTE)((-148*(r)  -291*(g) +439*(b))/1000 + 128); \
451   cr=(BYTE)(( 439*(r)  -368*(g) - 71*(b))/1000 + 128)
452 
453 
RGBtoYUV(unsigned r,unsigned g,unsigned b,unsigned & y,unsigned & u,unsigned & v)454 void PColourConverter::RGBtoYUV(unsigned   r, unsigned   g, unsigned   b,
455                                 unsigned & y, unsigned & u, unsigned & v)
456 {
457   RGB2YUV(r, g, b, y, u, v);
458 }
459 
460 
RGBtoYUV(unsigned r,unsigned g,unsigned b,BYTE & y,BYTE & u,BYTE & v)461 void PColourConverter::RGBtoYUV(unsigned r, unsigned g, unsigned b,
462                                 BYTE   & y, BYTE   & u, BYTE   & v)
463 {
464   RGB2YUV(r, g, b, y, u, v);
465 }
466 
467 
468 // Consider a YUV420P image of 4x4 pixels.
469 //
470 // A plane of Y values    A B C D
471 //                        E F G H
472 //                        I J K L
473 //                        M N O P
474 //
475 // A plane of U values    1 . 2 .
476 //                           . . . .
477 //                           3 . 4 .
478 //                        . . . .
479 //
480 // A plane of V values    1 . 2 .
481 //                           . . . .
482 //                        3 . 4 .
483 //                        . . . .
484 //
485 // YUV420P is stored as all Y (w*h), then U (w*h/4), then V
486 //   thus, a 4x4 image requires 24 bytes of storage.
487 //
488 // Grow and Shrink utilise the Variable Duty Cycle algorithm, currently
489 // no interpolation is used, just pixel dropping or doubling
490 
GrowYUV420P(unsigned srcX,unsigned srcY,unsigned srcWidth,unsigned srcHeight,unsigned srcFrameWidth,const BYTE * srcYUV,unsigned dstX,unsigned dstY,unsigned dstWidth,unsigned dstHeight,unsigned dstFrameWidth,BYTE * dstYUV)491 static void GrowYUV420P(unsigned srcX, unsigned srcY, unsigned srcWidth, unsigned srcHeight,
492                         unsigned srcFrameWidth, const BYTE * srcYUV,
493                         unsigned dstX, unsigned dstY, unsigned dstWidth, unsigned dstHeight,
494                         unsigned dstFrameWidth, BYTE * dstYUV)
495 {
496   const BYTE * srcPtr = srcYUV + srcY * srcFrameWidth + srcX;
497   BYTE * dstPtr = dstYUV + dstY * dstFrameWidth + dstX;
498 
499   unsigned repeatRow = 0;
500   for (unsigned y = 0; y < srcHeight; y++) {
501 
502     const BYTE * srcPixel = srcPtr;
503     BYTE * dstPixel = dstPtr;
504     unsigned repeatPixel = 0;
505 
506     for (unsigned x = 0; x < srcWidth; x++) {
507       do {
508         *dstPixel++ = *srcPixel;
509         repeatPixel += srcWidth;
510       } while (repeatPixel < dstWidth);
511       repeatPixel -= dstWidth;
512 
513       srcPixel++;
514     }
515 
516     BYTE * repeatPtr = dstPtr;
517 
518     repeatRow += srcHeight;
519     while (repeatRow < dstHeight) {
520       dstPtr += dstFrameWidth;
521       memcpy(dstPtr, repeatPtr, dstWidth);
522       repeatRow += srcHeight;
523     }
524     repeatRow -= dstHeight;
525 
526     srcPtr += srcFrameWidth;
527     dstPtr += dstFrameWidth;
528   }
529 }
530 
531 
ShrinkYUV420P(unsigned srcX,unsigned srcY,unsigned srcWidth,unsigned srcHeight,unsigned srcFrameWidth,const BYTE * srcYUV,unsigned dstX,unsigned dstY,unsigned dstWidth,unsigned dstHeight,unsigned dstFrameWidth,BYTE * dstYUV)532 static void ShrinkYUV420P(unsigned srcX, unsigned srcY, unsigned srcWidth, unsigned srcHeight,
533                           unsigned srcFrameWidth, const BYTE * srcYUV,
534                           unsigned dstX, unsigned dstY, unsigned dstWidth, unsigned dstHeight,
535                           unsigned dstFrameWidth, BYTE * dstYUV)
536 {
537   const BYTE * srcPtr = srcYUV + srcY * srcFrameWidth + srcX;
538   BYTE * dstPtr = dstYUV + dstY * dstFrameWidth + dstX;
539 
540   unsigned repeatRow = 0;
541   for (unsigned y = 0; y < dstHeight; y++) {
542 
543     const BYTE * srcPixel = srcPtr;
544     BYTE * dstPixel = dstPtr;
545     unsigned repeatPixel = 0;
546 
547     for (unsigned x = 0; x < dstWidth; x++) {
548       *dstPixel++ = *srcPixel;
549 
550       do {
551         srcPixel++;
552         repeatPixel += dstWidth;
553       } while (repeatPixel < srcWidth);
554       repeatPixel -= srcWidth;
555     }
556 
557     do {
558       srcPtr += srcFrameWidth;
559       repeatRow += dstHeight;
560     } while (repeatRow < srcHeight);
561     repeatRow -= srcHeight;
562 
563     dstPtr += dstFrameWidth;
564   }
565 }
566 
567 
CropYUV420P(unsigned srcX,unsigned srcY,unsigned srcWidth,unsigned srcHeight,unsigned srcFrameWidth,const BYTE * srcYUV,unsigned dstX,unsigned dstY,unsigned,unsigned,unsigned dstFrameWidth,BYTE * dstYUV)568 static void CropYUV420P(unsigned srcX, unsigned srcY, unsigned srcWidth, unsigned srcHeight,
569                           unsigned srcFrameWidth, const BYTE * srcYUV,
570                           unsigned dstX, unsigned dstY, unsigned , unsigned ,
571                           unsigned dstFrameWidth, BYTE * dstYUV)
572 {
573   const BYTE * srcPtr = srcYUV + srcY * srcFrameWidth + srcX;
574   BYTE * dstPtr = dstYUV + dstY * dstFrameWidth + dstX;
575   for (unsigned y = 0; y < srcHeight; y++) {
576     memcpy(dstPtr, srcPtr, srcWidth);
577     srcPtr += srcFrameWidth;
578     dstPtr += dstFrameWidth;
579   }
580 }
581 
582 
ValidateDimensions(unsigned srcFrameWidth,unsigned srcFrameHeight,unsigned dstFrameWidth,unsigned dstFrameHeight)583 static bool ValidateDimensions(unsigned srcFrameWidth, unsigned srcFrameHeight, unsigned dstFrameWidth, unsigned dstFrameHeight)
584 {
585   if (srcFrameWidth == 0 || dstFrameWidth == 0 || srcFrameHeight == 0 || dstFrameHeight == 0) {
586     PTRACE(2,"PColCnv\tDimensions cannot be zero: "
587            << srcFrameWidth << 'x' << srcFrameHeight << " -> " << dstFrameWidth << 'x' << dstFrameHeight);
588     return false;
589   }
590 
591   if ((srcFrameWidth | dstFrameWidth | srcFrameHeight | dstFrameHeight) & 1) {
592     PTRACE(2,"PColCnv\tDimensions must be even: "
593            << srcFrameWidth << 'x' << srcFrameHeight << " -> " << dstFrameWidth << 'x' << dstFrameHeight);
594     return false;
595   }
596 
597   if (srcFrameWidth <= dstFrameWidth && srcFrameHeight <= dstFrameHeight)
598     return true;
599 
600   if (srcFrameWidth >= dstFrameWidth && srcFrameHeight >= dstFrameHeight)
601     return true;
602 
603   PTRACE(2,"PColCnv\tCannot do one dimension shrinking and the other one growing: "
604          << srcFrameWidth << 'x' << srcFrameHeight << " -> " << dstFrameWidth << 'x' << dstFrameHeight);
605   return false;
606 }
607 
608 
CopyYUV420P(unsigned srcX,unsigned srcY,unsigned srcWidth,unsigned srcHeight,unsigned srcFrameWidth,unsigned srcFrameHeight,const BYTE * srcYUV,unsigned dstX,unsigned dstY,unsigned dstWidth,unsigned dstHeight,unsigned dstFrameWidth,unsigned dstFrameHeight,BYTE * dstYUV,PVideoFrameInfo::ResizeMode resizeMode)609 bool PColourConverter::CopyYUV420P(unsigned srcX, unsigned srcY, unsigned srcWidth, unsigned srcHeight,
610                                    unsigned srcFrameWidth, unsigned srcFrameHeight, const BYTE * srcYUV,
611                                    unsigned dstX, unsigned dstY, unsigned dstWidth, unsigned dstHeight,
612                                    unsigned dstFrameWidth, unsigned dstFrameHeight, BYTE * dstYUV,
613                                    PVideoFrameInfo::ResizeMode resizeMode)
614 {
615   if (srcX == 0 && srcY == 0 && dstX == 0 && dstY == 0 &&
616       srcWidth == dstWidth && srcHeight == dstHeight &&
617       srcFrameWidth == dstFrameWidth && srcFrameHeight == dstFrameHeight &&
618       srcWidth == srcFrameWidth && srcHeight == srcFrameHeight) {
619     memcpy(dstYUV, srcYUV, srcFrameWidth*srcFrameHeight*3/2);
620     return true;
621   }
622 
623   if (srcFrameWidth == 0 || srcFrameHeight == 0 ||
624       dstFrameWidth == 0 || dstFrameHeight == 0 ||
625       !ValidateDimensions(srcWidth, srcHeight, dstWidth, dstHeight) ||
626       srcX + srcWidth > srcFrameWidth ||
627       srcY + srcHeight > srcFrameHeight ||
628       dstX + dstWidth > dstFrameWidth ||
629       dstY + dstHeight > dstFrameHeight) {
630     PAssertAlways(PInvalidParameter);
631     return false;
632   }
633 
634   void (*rowFunction)(unsigned srcX, unsigned srcY, unsigned srcWidth, unsigned srcHeight,
635                       unsigned srcFrameWidth, const BYTE * srcYUV,
636                       unsigned dstX, unsigned dstY, unsigned dstWidth, unsigned dstHeight,
637                       unsigned dstFrameWidth, BYTE * dstYUV) = CropYUV420P;
638 
639   switch (resizeMode) {
640     case PVideoFrameInfo::eScale :
641       if (srcWidth > dstWidth)
642         rowFunction = ShrinkYUV420P;
643       else if (srcWidth < dstWidth)
644         rowFunction = GrowYUV420P;
645       break;
646 
647     case PVideoFrameInfo::eCropTopLeft :
648       if (srcWidth < dstWidth) {
649         FillYUV420P(dstX + srcWidth, dstY, dstWidth - srcWidth, dstHeight, dstFrameWidth, dstFrameHeight, dstYUV, 0, 0, 0);
650         if (srcHeight < dstHeight)
651           FillYUV420P(dstX, dstY + srcHeight, dstWidth, dstHeight - srcHeight, dstFrameWidth, dstFrameHeight, dstYUV, 0, 0, 0);
652         dstWidth = srcWidth;
653         dstHeight = srcHeight;
654       }
655       else {
656         srcWidth = dstWidth;
657         srcHeight = dstHeight;
658       }
659       break;
660 
661     case PVideoFrameInfo::eCropCentre :
662       if (srcWidth < dstWidth) {
663         unsigned deltaX = (dstWidth - srcWidth)/2;
664         unsigned deltaY = (dstHeight - srcHeight)/2;
665         FillYUV420P(dstX, dstY, deltaX, dstHeight, dstFrameWidth, dstFrameHeight, dstYUV, 0, 0, 0);
666         FillYUV420P(dstX+deltaX+srcWidth, dstY, deltaX, dstHeight, dstFrameWidth, dstFrameHeight, dstYUV, 0, 0, 0);
667         if (srcHeight < dstHeight) {
668           FillYUV420P(dstX+deltaX, dstY, srcWidth, deltaY, dstFrameWidth, dstFrameHeight, dstYUV, 0, 0, 0);
669           FillYUV420P(dstX+deltaX, dstY+deltaY+srcHeight, srcWidth, deltaY, dstFrameWidth, dstFrameHeight, dstYUV, 0, 0, 0);
670         }
671         dstX += deltaX;
672         dstY += deltaY;
673         dstWidth = srcWidth;
674         dstHeight = srcHeight;
675       }
676       else {
677         srcX += (srcWidth - dstWidth)/2;
678         srcY += (srcHeight - dstHeight)/2;
679         srcWidth = dstWidth;
680         srcHeight = dstHeight;
681       }
682       break;
683 
684     default :
685       PAssertAlways(PInvalidParameter);
686       return false;
687   }
688 
689   // Copy plane Y
690   rowFunction(srcX, srcY, srcWidth, srcHeight, srcFrameWidth, srcYUV,
691               dstX, dstY, dstWidth, dstHeight, dstFrameWidth, dstYUV);
692 
693   srcYUV += srcFrameWidth*srcFrameHeight;
694   dstYUV += dstFrameWidth*dstFrameHeight;
695 
696   // U & V planes half size
697   srcX /= 2;
698   srcY /= 2;
699   dstX /= 2;
700   dstY /= 2;
701   srcWidth /= 2;
702   srcHeight /= 2;
703   dstWidth /= 2;
704   dstHeight /= 2;
705   srcFrameWidth /= 2;
706   srcFrameHeight /= 2;
707   dstFrameWidth /= 2;
708   dstFrameHeight /= 2;
709 
710   // Copy plane U
711   rowFunction(srcX, srcY, srcWidth, srcHeight, srcFrameWidth, srcYUV,
712               dstX, dstY, dstWidth, dstHeight, dstFrameWidth, dstYUV);
713 
714   srcYUV += srcFrameWidth*srcFrameHeight;
715   dstYUV += dstFrameWidth*dstFrameHeight;
716 
717   // Copy plane V
718   rowFunction(srcX, srcY, srcWidth, srcHeight, srcFrameWidth, srcYUV,
719               dstX, dstY, dstWidth, dstHeight, dstFrameWidth, dstYUV);
720   return true;
721 }
722 
723 
FillYUV420P(unsigned x,unsigned y,int width,int height,unsigned frameWidth,unsigned frameHeight,BYTE * yuv,unsigned r,unsigned g,unsigned b)724 bool PColourConverter::FillYUV420P(unsigned x, unsigned y, int width, int height,
725                                    unsigned frameWidth, unsigned frameHeight, BYTE * yuv,
726                                    unsigned r, unsigned g, unsigned b)
727 {
728   if (frameWidth == 0 || frameHeight == 0 || x + width > frameWidth || y + height > frameHeight) {
729     PAssertAlways(PInvalidParameter);
730     return false;
731   }
732 
733   unsigned Y, Cb, Cr;
734   PColourConverter::RGBtoYUV(r, g, b, Y, Cb, Cr);
735 
736   x &= 0xfffffffe; // Make sure is even
737 
738   int offset       = ( y * frameWidth ) + x;
739   int colourOffset = ( (y * frameWidth) >> 2) + (x >> 1);
740 
741   unsigned char * Yptr  = yuv + offset;
742   unsigned char * CbPtr = yuv + (frameWidth * frameHeight) + colourOffset;
743   unsigned char * CrPtr = yuv + (frameWidth * frameHeight) + (frameWidth * frameHeight/4)  + colourOffset;
744 
745   int halfRectWidth  = width/2;
746   int halfFrameWidth = frameWidth/2;
747 
748   for (int dy = 0; dy < height; dy += 2) {
749     memset(Yptr, Y, width);
750     Yptr += frameWidth;
751     memset(Yptr, Y, width);
752     Yptr += frameWidth;
753 
754     memset(CbPtr, Cb, halfRectWidth);
755     memset(CrPtr, Cr, halfRectWidth);
756 
757     CbPtr += halfFrameWidth;
758     CrPtr += halfFrameWidth;
759   }
760 
761   return true;
762 }
763 
764 
765 ///////////////////////////////////////////////////////////////////////////////
766 
PSynonymColourRegistration(const char * srcFmt,const char * dstFmt)767 PSynonymColourRegistration::PSynonymColourRegistration(const char * srcFmt,
768                                                        const char * dstFmt)
769   : PColourConverterRegistration(srcFmt,dstFmt)
770 {
771 }
772 
773 
Create(const PVideoFrameInfo & src,const PVideoFrameInfo & dst) const774 PColourConverter * PSynonymColourRegistration::Create(const PVideoFrameInfo & src,
775                                                       const PVideoFrameInfo & dst) const
776 {
777   return new PSynonymColour(src, dst);
778 }
779 
Convert(const BYTE * srcFrameBuffer,BYTE * dstFrameBuffer,unsigned int __srcFrameBytes,PINDEX * bytesReturned)780 PBoolean PSynonymColour::Convert(const BYTE *srcFrameBuffer,
781                              BYTE *dstFrameBuffer,
782                              unsigned int __srcFrameBytes,
783                              PINDEX * bytesReturned)
784 {
785   srcFrameBytes = __srcFrameBytes;
786   return Convert(srcFrameBuffer, dstFrameBuffer, bytesReturned);
787 }
788 
Convert(const BYTE * srcFrameBuffer,BYTE * dstFrameBuffer,PINDEX * bytesReturned)789 PBoolean PSynonymColour::Convert(const BYTE *srcFrameBuffer,
790                              BYTE *dstFrameBuffer,
791                              PINDEX * bytesReturned)
792 {
793   if ((srcFrameWidth != dstFrameWidth) || (srcFrameHeight != dstFrameHeight)) {
794     PTRACE(2,"PColCnv\tCannot do synonym conversion, source and destination size not equal: " << *this);
795     return false;
796   }
797 
798   if (verticalFlip) {
799     PINDEX rowSize = dstFrameBytes/srcFrameHeight;
800     if (rowSize*srcFrameHeight != dstFrameBytes) {
801       PTRACE(2,"PColCnv\tCannot do synonym conversion, frame does not have equal scan lines: " << *this);
802       return false;
803     }
804 
805     if (srcFrameBuffer != dstFrameBuffer) {
806       const BYTE * srcRowPtr = srcFrameBuffer;
807       BYTE * dstRowPtr = dstFrameBuffer + srcFrameHeight*rowSize;
808       for (unsigned y = 0; y < srcFrameHeight; y++) {
809         dstRowPtr -= rowSize;
810         memcpy(dstRowPtr, srcRowPtr, rowSize);
811         srcRowPtr += rowSize;
812       }
813     }
814     else {
815       BYTE * rowPtr1 = dstFrameBuffer;
816       BYTE * rowPtr2 = dstFrameBuffer + srcFrameHeight*rowSize;
817       PBYTEArray temp(rowSize);
818       for (unsigned y = 0; y < srcFrameHeight; y += 2) {
819         rowPtr2 -= rowSize;
820         memcpy(temp.GetPointer(), rowPtr1, rowSize);
821         memcpy(rowPtr1, rowPtr2, rowSize);
822         memcpy(rowPtr2, temp.GetPointer(), rowSize);
823         rowPtr1 += rowSize;
824       }
825     }
826   }
827   else {
828     if (srcFrameBuffer != dstFrameBuffer)
829       memcpy(dstFrameBuffer, srcFrameBuffer, dstFrameBytes);
830   }
831 
832   if (bytesReturned != NULL)
833     *bytesReturned = dstFrameBytes;
834 
835   return true;
836 }
837 
838 ///////////////////////////////////////////////////////////////////////////////
839 
840 #define greytoy(r, y) y=r
841 #define greytoyuv(r, y, u, v) greytoy(r,y); u=BLACK_U; v=BLACK_V
842 
GreytoYUV420PSameSize(const BYTE * grey,BYTE * yuv) const843 void PStandardColourConverter::GreytoYUV420PSameSize(const BYTE * grey, BYTE * yuv) const
844 {
845   const unsigned planeSize = srcFrameWidth*srcFrameHeight;
846   const unsigned halfWidth = srcFrameWidth >> 1;
847 
848   // get pointers to the data
849   BYTE * yplane  = yuv;
850   BYTE * uplane  = yuv + planeSize;
851   BYTE * vplane  = yuv + planeSize + (planeSize >> 2);
852   const BYTE * greyIndex = grey;
853 
854   for (unsigned y = 0; y < srcFrameHeight; y++) {
855     BYTE * yline  = yplane + (y * srcFrameWidth);
856     BYTE * uline  = uplane + ((y >> 1) * halfWidth);
857     BYTE * vline  = vplane + ((y >> 1) * halfWidth);
858 
859     if (verticalFlip)
860       greyIndex = grey + srcFrameWidth*(srcFrameHeight-1-y);
861 
862     for (unsigned x = 0; x < srcFrameWidth; x+=2) {
863       greytoy(*greyIndex, *yline);
864       greyIndex++;
865       yline++;
866       greytoyuv(*greyIndex, *yline, *uline, *vline);
867       greyIndex++;
868       yline++;
869       uline++;
870       vline++;
871     }
872   }
873 }
874 
875 
876 // Simple crop/pad version.  Image aligned to top-left
877 // and cropped / padded with black borders as required.
GreytoYUV420PWithCrop(const BYTE * grey,BYTE * yuv) const878 void PStandardColourConverter::GreytoYUV420PWithCrop(const BYTE * grey, BYTE * yuv) const
879 {
880   int planeSize = dstFrameWidth*dstFrameHeight;
881   const int halfWidth = dstFrameWidth >> 1;
882   unsigned min_width, min_height;
883 
884   min_width  = (dstFrameWidth  < srcFrameWidth)  ? dstFrameWidth  : srcFrameWidth;
885   min_height = (dstFrameHeight < srcFrameHeight) ? dstFrameHeight : srcFrameHeight;
886 
887   // get pointers to the data
888   BYTE * yplane  = yuv;
889   BYTE * uplane  = yuv + planeSize;
890   BYTE * vplane  = yuv + planeSize + (planeSize >> 2);
891   const BYTE * greyIndex = grey;
892 
893   for (unsigned y = 0; y < min_height; y++)
894   {
895     BYTE * yline  = yplane + (y * dstFrameWidth);
896     BYTE * uline  = uplane + ((y >> 1) * halfWidth);
897     BYTE * vline  = vplane + ((y >> 1) * halfWidth);
898 
899     if (verticalFlip)
900       greyIndex = grey + srcFrameWidth*(min_height-1-y);
901 
902     for (unsigned x = 0; x < min_width; x+=2)
903     {
904       greytoy(*greyIndex, *yline);
905       greyIndex++;
906       yline++;
907       greytoyuv(*greyIndex, *yline, *uline, *vline);
908       greyIndex++;
909       yline++;
910       uline++;
911       vline++;
912     }
913 
914     // Crop if source width > dest width
915     if (srcFrameWidth > dstFrameWidth)
916       greyIndex += srcFrameWidth - dstFrameWidth;
917 
918     // Pad if dest width < source width
919     if (dstFrameWidth > srcFrameWidth) {
920       memset(yline, BLACK_Y, dstFrameWidth - srcFrameWidth);
921       memset(uline, BLACK_U, (dstFrameWidth - srcFrameWidth)>>1);
922       memset(vline, BLACK_V, (dstFrameWidth - srcFrameWidth)>>1);
923     }
924   }
925 
926   // Pad if dest height > source height
927   if (dstFrameHeight > srcFrameHeight) {
928     BYTE * yline  = yplane + (srcFrameHeight * dstFrameWidth);
929     BYTE * uline  = uplane + ((srcFrameHeight >> 1) * halfWidth);
930     BYTE * vline  = vplane + ((srcFrameHeight >> 1) * halfWidth);
931     unsigned fill = (dstFrameHeight - srcFrameHeight) * dstFrameWidth;
932 
933     memset(yline, BLACK_Y, fill);
934     memset(uline, BLACK_U, fill >> 2);
935     memset(vline, BLACK_V, fill >> 2);
936   }
937 }
938 
939 
GreytoYUV420P(const BYTE * grey,BYTE * yuv,PINDEX * bytesReturned) const940 bool PStandardColourConverter::GreytoYUV420P(const BYTE * grey, BYTE * yuv, PINDEX * bytesReturned) const
941 {
942   if (grey == yuv) {
943     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
944     return false;
945   }
946 
947   if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight))
948     GreytoYUV420PSameSize(grey, yuv);
949   else
950     GreytoYUV420PWithCrop(grey, yuv);
951 
952   if (bytesReturned != NULL)
953     *bytesReturned = dstFrameBytes;
954 
955   return true;
956 }
957 
958 
RGBtoYUV420PSameSize(const BYTE * rgb,BYTE * yuv,unsigned rgbIncrement,unsigned redOffset,unsigned blueOffset) const959 void PStandardColourConverter::RGBtoYUV420PSameSize(const BYTE * rgb,
960                                                     BYTE * yuv,
961                                                     unsigned rgbIncrement,
962                                                     unsigned redOffset,
963                                                     unsigned blueOffset) const
964 {
965   const unsigned planeSize = srcFrameWidth*srcFrameHeight;
966   const unsigned halfWidth = srcFrameWidth >> 1;
967 
968   // get pointers to the data
969   BYTE * yplane  = yuv;
970   BYTE * uplane  = yuv + planeSize;
971   BYTE * vplane  = yuv + planeSize + (planeSize >> 2);
972   const BYTE * rgbIndex = rgb;
973 
974   for (unsigned y = 0; y < srcFrameHeight; y++) {
975     BYTE * yline  = yplane + (y * srcFrameWidth);
976     BYTE * uline  = uplane + ((y >> 1) * halfWidth);
977     BYTE * vline  = vplane + ((y >> 1) * halfWidth);
978 
979     if (verticalFlip)
980       rgbIndex = rgb + (srcFrameWidth*(srcFrameHeight-1-y)*rgbIncrement);
981 
982     for (unsigned x = 0; x < srcFrameWidth; x+=2) {
983       RGB2Y(rgbIndex[redOffset], rgbIndex[1], rgbIndex[blueOffset], *yline);
984       rgbIndex += rgbIncrement;
985       yline++;
986       RGB2YUV(rgbIndex[redOffset], rgbIndex[1], rgbIndex[blueOffset], *yline, *uline, *vline);
987       rgbIndex += rgbIncrement;
988       yline++;
989       uline++;
990       vline++;
991     }
992   }
993 }
994 
995 
996 // Simple crop/pad version.  Image aligned to top-left
997 // and cropped / padded with black borders as required.
RGBtoYUV420PWithCrop(const BYTE * rgb,BYTE * yuv,unsigned rgbIncrement,unsigned redOffset,unsigned blueOffset) const998 void PStandardColourConverter::RGBtoYUV420PWithCrop(const BYTE * rgb,
999                                                     BYTE * yuv,
1000                                                     unsigned rgbIncrement,
1001                                                     unsigned redOffset,
1002                                                     unsigned blueOffset) const
1003 {
1004   int planeSize = dstFrameWidth*dstFrameHeight;
1005   const int halfWidth = dstFrameWidth >> 1;
1006   unsigned min_width, min_height;
1007 
1008   min_width  = (dstFrameWidth  < srcFrameWidth)  ? dstFrameWidth  : srcFrameWidth;
1009   min_height = (dstFrameHeight < srcFrameHeight) ? dstFrameHeight : srcFrameHeight;
1010 
1011   // get pointers to the data
1012   BYTE * yplane  = yuv;
1013   BYTE * uplane  = yuv + planeSize;
1014   BYTE * vplane  = yuv + planeSize + (planeSize >> 2);
1015   const BYTE * rgbIndex = rgb;
1016 
1017   for (unsigned y = 0; y < min_height; y++)
1018   {
1019     BYTE * yline  = yplane + (y * dstFrameWidth);
1020     BYTE * uline  = uplane + ((y >> 1) * halfWidth);
1021     BYTE * vline  = vplane + ((y >> 1) * halfWidth);
1022 
1023     if (verticalFlip)
1024       rgbIndex = rgb + (srcFrameWidth*(min_height-1-y)*rgbIncrement);
1025 
1026     for (unsigned x = 0; x < min_width; x+=2) {
1027       RGB2Y(rgbIndex[redOffset], rgbIndex[1], rgbIndex[blueOffset], *yline);
1028       rgbIndex += rgbIncrement;
1029       yline++;
1030       RGB2YUV(rgbIndex[redOffset], rgbIndex[1], rgbIndex[blueOffset], *yline, *uline, *vline);
1031       rgbIndex += rgbIncrement;
1032       yline++;
1033       uline++;
1034       vline++;
1035     }
1036 
1037     // Crop if source width > dest width
1038     if (srcFrameWidth > dstFrameWidth)
1039       rgbIndex += rgbIncrement * (srcFrameWidth - dstFrameWidth);
1040 
1041     // Pad if dest width < source width
1042     if (dstFrameWidth > srcFrameWidth) {
1043       memset(yline, BLACK_Y, dstFrameWidth - srcFrameWidth);
1044       memset(uline, BLACK_U, (dstFrameWidth - srcFrameWidth)>>1);
1045       memset(vline, BLACK_V, (dstFrameWidth - srcFrameWidth)>>1);
1046     }
1047   }
1048 
1049   // Pad if dest height > source height
1050   if (dstFrameHeight > srcFrameHeight) {
1051     BYTE * yline  = yplane + (srcFrameHeight * dstFrameWidth);
1052     BYTE * uline  = uplane + ((srcFrameHeight >> 1) * halfWidth);
1053     BYTE * vline  = vplane + ((srcFrameHeight >> 1) * halfWidth);
1054     unsigned fill = (dstFrameHeight - srcFrameHeight) * dstFrameWidth;
1055 
1056     memset(yline, BLACK_Y, fill);
1057     memset(uline, BLACK_U, fill >> 2);
1058     memset(vline, BLACK_V, fill >> 2);
1059   }
1060 }
1061 
1062 
RGBtoYUV420P(const BYTE * rgb,BYTE * yuv,PINDEX * bytesReturned,unsigned rgbIncrement,unsigned redOffset,unsigned blueOffset)1063 bool PStandardColourConverter::RGBtoYUV420P(const BYTE * rgb,
1064                                             BYTE * yuv,
1065                                             PINDEX * bytesReturned,
1066                                             unsigned rgbIncrement,
1067                                             unsigned redOffset,
1068                                             unsigned blueOffset)
1069 {
1070   if (rgb == yuv) {
1071     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
1072     return false;
1073   }
1074 
1075   if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight))
1076     RGBtoYUV420PSameSize(rgb, yuv, rgbIncrement, redOffset, blueOffset);
1077   else if (resizeMode == PVideoFrameInfo::eCropTopLeft)
1078     RGBtoYUV420PWithCrop(rgb, yuv, rgbIncrement, redOffset, blueOffset);
1079   else {
1080     unsigned intermediateSize = PVideoFrameInfo::CalculateFrameBytes(srcFrameWidth, srcFrameHeight, dstColourFormat);
1081     RGBtoYUV420PSameSize(rgb, intermediateFrameStore.GetPointer(intermediateSize), rgbIncrement, redOffset, blueOffset);
1082     CopyYUV420P(0, 0, srcFrameWidth, srcFrameHeight, srcFrameWidth, srcFrameHeight, intermediateFrameStore,
1083                 0, 0, dstFrameWidth, dstFrameHeight, dstFrameWidth, dstFrameHeight, yuv, resizeMode);
1084   }
1085 
1086   if (bytesReturned != NULL)
1087     *bytesReturned = dstFrameBytes;
1088 
1089   return true;
1090 }
1091 
1092 
PSTANDARD_COLOUR_CONVERTER(Grey,YUV420P)1093 PSTANDARD_COLOUR_CONVERTER(Grey,YUV420P)
1094 {
1095   return GreytoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned);
1096 }
1097 
1098 
PSTANDARD_COLOUR_CONVERTER(RGB24,YUV420P)1099 PSTANDARD_COLOUR_CONVERTER(RGB24,YUV420P)
1100 {
1101   return RGBtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3,  0, 2);
1102 }
1103 
1104 
PSTANDARD_COLOUR_CONVERTER(BGR24,YUV420P)1105 PSTANDARD_COLOUR_CONVERTER(BGR24,YUV420P)
1106 {
1107   return RGBtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3,  2, 0);
1108 }
1109 
1110 
PSTANDARD_COLOUR_CONVERTER(RGB32,YUV420P)1111 PSTANDARD_COLOUR_CONVERTER(RGB32,YUV420P)
1112 {
1113   return RGBtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 0, 2);
1114 }
1115 
1116 
PSTANDARD_COLOUR_CONVERTER(BGR32,YUV420P)1117 PSTANDARD_COLOUR_CONVERTER(BGR32,YUV420P)
1118 {
1119   return RGBtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 2, 0);
1120 }
1121 
1122 /*
1123  * Format YUY2 or YUV422(non planar):
1124  *
1125  * off: 0  Y00 U00 Y01 V00 Y02 U01 Y03 V01
1126  * off: 8  Y10 U10 Y11 V10 Y12 U11 Y13 V11
1127  * off:16  Y20 U20 Y21 V20 Y22 U21 Y23 V21
1128  * off:24  Y30 U30 Y31 V30 Y32 U31 Y33 V31
1129  * length:32 bytes
1130  *
1131  * Format YUV420P:
1132  * off: 00  Y00 Y01 Y02 Y03
1133  * off: 04  Y10 Y11 Y12 Y13
1134  * off: 08  Y20 Y21 Y22 Y23
1135  * off: 12  Y30 Y31 Y32 Y33
1136  * off: 16  U00 U02 U20 U22
1137  * off: 20  V00 V02 V20 V22
1138  *
1139  * So, we loose some bit of information when converting YUY2 to YUV420
1140  *
1141  * NOTE: This algorithm works only if the width and the height is pair.
1142  */
YUY2toYUV420PSameSize(const BYTE * yuy2,BYTE * yuv420p) const1143 void  PStandardColourConverter::YUY2toYUV420PSameSize(const BYTE *yuy2, BYTE *yuv420p) const
1144 {
1145   const BYTE *s;
1146   BYTE *y, *u, *v;
1147   unsigned int x, h;
1148   int npixels = srcFrameWidth * srcFrameHeight;
1149 
1150   s = yuy2;
1151   y = yuv420p;
1152   u = yuv420p + npixels;
1153   v = u + npixels/4;
1154 
1155   for (h=0; h<srcFrameHeight; h+=2) {
1156 
1157      /* Copy the first line keeping all information */
1158      for (x=0; x<srcFrameWidth; x+=2) {
1159         *y++ = *s++;
1160         *u++ = *s++;
1161         *y++ = *s++;
1162         *v++ = *s++;
1163      }
1164      /* Copy the second line discarding u and v information */
1165      for (x=0; x<srcFrameWidth; x+=2) {
1166         *y++ = *s++;
1167         s++;
1168         *y++ = *s++;
1169         s++;
1170      }
1171   }
1172 }
1173 
1174 /*
1175  * Format YUY2 or YUV422(non planar):
1176  *
1177  * off: 0  Y00 U00 Y01 V00 Y02 U01 Y03 V01
1178  * off: 8  Y10 U10 Y11 V10 Y12 U11 Y13 V11
1179  * off:16  Y20 U20 Y21 V20 Y22 U21 Y23 V21
1180  * off:24  Y30 U30 Y31 V30 Y32 U31 Y33 V31
1181  * length:32 bytes
1182  *
1183  * Format YUV420P:
1184  * off: 00  Y00 Y01 Y02 Y03
1185  * off: 04  Y10 Y11 Y12 Y13
1186  * off: 08  Y20 Y21 Y22 Y23
1187  * off: 12  Y30 Y31 Y32 Y33
1188  * off: 16  U00 U02 U20 U22
1189  * off: 20  V00 V02 V20 V22
1190  *
1191  * So, we loose some bit of information when converting YUY2 to YUV420
1192  *
1193  * NOTE: This algorithm works only if the width and the height are even numbers.
1194  */
YUY2toYUV420PWithGrow(const BYTE * yuy2,BYTE * yuv420p) const1195 void PStandardColourConverter::YUY2toYUV420PWithGrow(const BYTE *yuy2, BYTE *yuv420p) const
1196 {
1197   const BYTE *s;
1198   BYTE *y, *u, *v;
1199   unsigned int x, h;
1200   unsigned int npixels = dstFrameWidth * dstFrameHeight;
1201 
1202   s = yuy2;
1203   y = yuv420p;
1204   u = yuv420p + npixels;
1205   v = u + npixels/4;
1206 
1207   // dest is bigger than the source. No subsampling.
1208   // Place the src in the middle of the destination.
1209   unsigned int yOffset = (dstFrameHeight - srcFrameHeight)/2;
1210   unsigned int xOffset = (dstFrameWidth - srcFrameWidth)/2;
1211   unsigned int bpixels = yOffset * dstFrameWidth;
1212 
1213   /* Top border */
1214   memset(y, BLACK_Y, bpixels);   y += bpixels;
1215   memset(u, BLACK_U, bpixels/4); u += bpixels/4;
1216   memset(v, BLACK_V, bpixels/4); v += bpixels/4;
1217 
1218   for (h=0; h<srcFrameHeight; h+=2)
1219   {
1220     /* Left border */
1221     memset(y, BLACK_Y, xOffset);   y += xOffset;
1222     memset(u, BLACK_U, xOffset/2); u += xOffset/2;
1223     memset(v, BLACK_V, xOffset/2); v += xOffset/2;
1224 
1225     /* Copy the first line keeping all information */
1226     for (x=0; x<srcFrameWidth; x+=2)
1227     {
1228       *y++ = *s++;
1229       *u++ = *s++;
1230       *y++ = *s++;
1231       *v++ = *s++;
1232     }
1233     /* Right and Left border */
1234     for (x=0; x<xOffset*2; x++)
1235       *y++ = BLACK_Y;
1236 
1237     /* Copy the second line discarding u and v information */
1238     for (x=0; x<srcFrameWidth; x+=2)
1239     {
1240       *y++ = *s++;
1241       s++;
1242       *y++ = *s++;
1243       s++;
1244     }
1245     /* Fill the border with black (right side) */
1246     memset(y, BLACK_Y, xOffset);        y += xOffset;
1247     memset(u, BLACK_U, xOffset/2);        u += xOffset/2;
1248     memset(v, BLACK_V, xOffset/2);        v += xOffset/2;
1249   }
1250   memset(y, BLACK_Y, bpixels);
1251   memset(u, BLACK_U, bpixels/4);
1252   memset(v, BLACK_V, bpixels/4);
1253 }
1254 
1255 
YUY2toYUV420PWithShrink(const BYTE * yuy2,BYTE * yuv420p) const1256 void PStandardColourConverter::YUY2toYUV420PWithShrink(const BYTE *yuy2, BYTE *yuv420p) const
1257 {
1258   const BYTE *s;
1259   BYTE *y, *u, *v;
1260   unsigned int x, h;
1261   unsigned int npixels = dstFrameWidth * dstFrameHeight;
1262 
1263   s = yuy2;
1264   y = yuv420p;
1265   u = yuv420p + npixels;
1266   v = u + npixels/4;
1267 
1268   // source is bigger than the destination
1269   // We are doing linear interpolation to find value.
1270   // Note this algorithm only works if dst is an even multple of src
1271   unsigned int dx = srcFrameWidth/dstFrameWidth;
1272   unsigned int dy = srcFrameHeight/dstFrameHeight;
1273   unsigned int fy, fx;
1274 
1275   for (fy=0, h=0; h<dstFrameHeight; h+=2, fy+=dy*2)
1276   {
1277     /* Copy the first line with U&V */
1278     unsigned int yy = fy;
1279     unsigned int yy2 = (fy+dy);
1280     const unsigned char *line1, *line2;
1281     unsigned char lastU, lastV;
1282 
1283     line1 = s + (yy*2*srcFrameWidth);
1284     line2 = s + (yy2*2*srcFrameWidth);
1285     lastU = line1[1];
1286     lastV = line1[3];
1287     for (fx=0, x=0; x<dstFrameWidth; x+=2, fx+=dx*2)
1288     {
1289       unsigned int xx = fx*2;
1290       *y++ = line1[xx];
1291       if ( (xx&2) == 0)
1292       {
1293         *u++ = lastU = (line1[xx+1] + line2[xx+1])/2;
1294         *v++ = lastV = (line1[xx+3] + line2[xx+3])/2;
1295       }
1296       else
1297       {
1298         *u++ = lastU;
1299         *v++ = lastV = (line1[xx+1] + line2[xx+1])/2;
1300       }
1301 
1302       xx = (fx+dx);
1303       *y++ = line1[xx];
1304       if ( (xx&2) == 0)
1305         lastU = (line1[xx+1] + line2[xx+1])/2;
1306       else
1307         lastV = (line1[xx+3] + line2[xx+3])/2;
1308     }
1309 
1310     /* Copy the second line without U&V */
1311     for (fx=0, x=0; x<dstFrameWidth; x++, fx+=dx)
1312     {
1313       unsigned int xx = fx*2;
1314       *y++ = line2[xx];
1315     }
1316   } /* end of for (fy=0, h=0; h<dstFrameHeight; h+=2, fy+=dy*2) */
1317 }
1318 
1319 
PSTANDARD_COLOUR_CONVERTER(YUY2,YUV420P)1320 PSTANDARD_COLOUR_CONVERTER(YUY2,YUV420P)
1321 {
1322   if (!ValidateDimensions(srcFrameWidth, srcFrameHeight, dstFrameWidth, dstFrameHeight))
1323     return false;
1324 
1325   if (dstFrameWidth == srcFrameWidth)
1326     YUY2toYUV420PSameSize(srcFrameBuffer, dstFrameBuffer);
1327   else if (dstFrameWidth < srcFrameWidth)
1328     YUY2toYUV420PWithShrink(srcFrameBuffer, dstFrameBuffer);
1329   else
1330     YUY2toYUV420PWithGrow(srcFrameBuffer, dstFrameBuffer);
1331 
1332   if (bytesReturned != NULL)
1333     *bytesReturned = dstFrameBytes;
1334 
1335   return true;
1336 }
1337 
1338 // Consider a YUV422P image of 8x2 pixels.
1339 //
1340 // A plane of Y values    A B C D E F G H
1341 //                        I J K L M N O P
1342 //
1343 // A plane of U values    1 . 2 . 3 . 4 .
1344 //                        5 . 6 . 7 . 8 .
1345 //
1346 // A plane of V values    1 . 2 . 3 . 4 .
1347 //                        5 . 6 . 7 . 8 .
1348 //
1349 // YUV422 is stored as Y U Y V
1350 //   thus, a 4x4 image requires 32 bytes of storage.
1351 //
1352 // Image has two possible transformations.
1353 //        padded                 (src smaller than dst)
1354 //        subsampled and padded  (src bigger than dst)
1355 
YUV422WithCrop(const BYTE * src,BYTE * dest,bool centred) const1356 void PStandardColourConverter::YUV422WithCrop(const BYTE * src, BYTE * dest, bool centred) const
1357 {
1358   DWORD *result = (DWORD *)dest;
1359   DWORD black   = (DWORD)(BLACK_U<<24) + (BLACK_Y<<16) + (BLACK_U<<8) + BLACK_Y;
1360   unsigned maxIndex    = dstFrameWidth*dstFrameHeight/2;
1361 
1362   if ( (dstFrameWidth*dstFrameHeight) > (srcFrameWidth*srcFrameHeight) ) {
1363     for (unsigned i = 0; i < maxIndex; i++)
1364       *result++ = black;
1365 
1366     //dest is bigger than the source. No subsampling.
1367     //Place the src in the middle of the destination.
1368     unsigned yOffset = centred ? dstFrameHeight - srcFrameHeight : 0;
1369     unsigned xOffset = centred ? dstFrameWidth - srcFrameWidth : 0;
1370 
1371     BYTE *s_ptr,*d_ptr;
1372     d_ptr = (yOffset * dstFrameWidth) + xOffset + dest;
1373     s_ptr = (BYTE *)src;
1374     for (unsigned y = 0; y < srcFrameHeight; y++) {
1375       memcpy(d_ptr,s_ptr, srcFrameWidth*2);
1376       d_ptr += 2*dstFrameWidth;
1377       s_ptr += 2*srcFrameWidth;
1378     }
1379   } else {
1380     // source is bigger than the destination.
1381     //
1382     unsigned subSample  = 1 + (srcFrameHeight/dstFrameHeight) ;
1383     unsigned yOffset    = dstFrameHeight - (srcFrameHeight/subSample);
1384     unsigned xOffset    = dstFrameWidth - (srcFrameWidth/subSample);
1385     unsigned subSample2 = subSample*2;
1386 
1387     DWORD *s_ptr = (DWORD * )src;
1388     DWORD *d_ptr = (DWORD *) dest + ((yOffset * dstFrameWidth) + xOffset)/4 ;
1389     DWORD *sl_ptr, *dl_ptr;
1390 
1391     for (unsigned y = 0; y < srcFrameHeight; y+= subSample) {
1392       sl_ptr = s_ptr;
1393       dl_ptr = d_ptr;
1394       for (unsigned x = 0; x < srcFrameWidth; x+= subSample2) {
1395         *dl_ptr++ = *sl_ptr;
1396         sl_ptr += subSample;
1397       }
1398       d_ptr += dstFrameWidth/2;
1399       s_ptr += srcFrameWidth*subSample/2;
1400     }
1401   }
1402 }
1403 
1404 
PSTANDARD_COLOUR_CONVERTER(YUV422,YUV422)1405 PSTANDARD_COLOUR_CONVERTER(YUV422,YUV422)
1406 {
1407   if (bytesReturned != NULL)
1408     *bytesReturned = dstFrameBytes;
1409 
1410   if (srcFrameBuffer == dstFrameBuffer)
1411     return true;
1412 
1413   if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight))
1414     memcpy(dstFrameBuffer,srcFrameBuffer,srcFrameWidth*srcFrameHeight*2);
1415   else
1416     YUV422WithCrop(srcFrameBuffer, dstFrameBuffer, resizeMode == PVideoFrameInfo::eCropCentre);
1417 
1418   return true;
1419 }
1420 
1421 
PSTANDARD_COLOUR_CONVERTER(YUV420P,YUV420P)1422 PSTANDARD_COLOUR_CONVERTER(YUV420P,YUV420P)
1423 {
1424   if (bytesReturned != NULL)
1425     *bytesReturned = dstFrameBytes;
1426 
1427   if (srcFrameBuffer == dstFrameBuffer) {
1428     if (srcFrameWidth == dstFrameWidth && srcFrameHeight == dstFrameHeight)
1429       return true;
1430     if(srcFrameWidth < dstFrameWidth || srcFrameHeight < dstFrameHeight) {
1431       PTRACE(2,"PColCnv\tCannot do in place conversion, increasing image size.");
1432       return false;
1433     }
1434   }
1435 
1436   return CopyYUV420P(0, 0, srcFrameWidth, srcFrameHeight, srcFrameWidth, srcFrameHeight, srcFrameBuffer,
1437                      0, 0, dstFrameWidth, dstFrameHeight, dstFrameWidth, dstFrameHeight, dstFrameBuffer,
1438                      resizeMode);
1439 }
1440 
1441 /*
1442  * Format YUY2 or YUV422(non planar):
1443  *
1444  * off: 0  Y00 U00 Y01 V00 Y02 U01 Y03 V01
1445  * off: 8  Y10 U10 Y11 V10 Y12 U11 Y13 V11
1446  * off:16  Y20 U20 Y21 V20 Y22 U21 Y23 V21
1447  * off:24  Y30 U30 Y31 V30 Y32 U31 Y33 V31
1448  * length:32 bytes
1449  *
1450  * Format YUV420P:
1451  * off: 00  Y00 Y01 Y02 Y03
1452  * off: 04  Y10 Y11 Y12 Y13
1453  * off: 08  Y20 Y21 Y22 Y23
1454  * off: 12  Y30 Y31 Y32 Y33
1455  * off: 16  U00 U02 U20 U22
1456  * off: 20  V00 V02 V20 V22
1457  *
1458  * So, we loose some bit of information when converting YUY2 to YUV420
1459  *
1460  */
PSTANDARD_COLOUR_CONVERTER(YUV422,YUV420P)1461 PSTANDARD_COLOUR_CONVERTER(YUV422,YUV420P)
1462 {
1463   if (!ValidateDimensions(srcFrameWidth, srcFrameHeight, dstFrameWidth, dstFrameHeight))
1464     return false;
1465 
1466   if (dstFrameWidth == srcFrameWidth)
1467     YUY2toYUV420PSameSize(srcFrameBuffer, dstFrameBuffer);
1468   else if (dstFrameWidth < srcFrameWidth)
1469     YUY2toYUV420PWithShrink(srcFrameBuffer, dstFrameBuffer);
1470   else
1471     YUY2toYUV420PWithGrow(srcFrameBuffer, dstFrameBuffer);
1472 
1473   if (bytesReturned != NULL)
1474     *bytesReturned = dstFrameBytes;
1475 
1476   return true;
1477 }
1478 
1479 
1480 #define LIMIT(x) (unsigned char) ((x > 255) ? 255 : ((x < 0) ? 0 : x ))
clip(int a,int limit)1481 static inline int clip(int a, int limit) {
1482   return a<limit?a:limit;
1483 }
1484 
SBGGR8toYUV420P(const BYTE * src,BYTE * dst,PINDEX * bytesReturned)1485 bool PStandardColourConverter::SBGGR8toYUV420P(const BYTE * src, BYTE * dst, PINDEX * bytesReturned)
1486 {
1487 #define USE_SBGGR8_NATIVE 1 // set to 0 to use the double conversion algorithm (Bayer->RGB->YUV420P)
1488 
1489 #if USE_SBGGR8_NATIVE
1490 if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight))
1491 {
1492   // kernels for Y conversion, normalised by 2^16
1493   const int kR[]={1802,9667,1802,9667,19661,9667,1802,9667,1802};
1494   const int kG1[]={7733,9830,7733,3604,7733,3604,7733,9830,7733};
1495   const int kG2[]={7733,3604,7733,9830,7733,9830,7733,3604,7733};
1496   const int kB[]={4915,9667,4915,9667,7209,9667,4915,9667,4915};
1497   //  const int kID[]={0,0,0,0,65536,0,0,0,0}; identity kernel, use to test
1498 
1499   int B, G, G1, G2, R;
1500   const int stride = srcFrameWidth;
1501   unsigned const int hSize =srcFrameHeight/2;
1502   unsigned const int vSize =srcFrameWidth/2;
1503   unsigned const int lastRow=srcFrameHeight-1;
1504   unsigned const int lastCol=srcFrameWidth-1;
1505   unsigned int i,j;
1506   const BYTE *sBayer = src;
1507 
1508   //  Y = round( 0.256788 * R + 0.504129 * G + 0.097906 * B) +  16;
1509   //  Y = round( 0.30 * R + 0.59 * G + 0.11 * B ) use this!
1510   //  U = round(-0.148223 * R - 0.290993 * G + 0.439216 * B) + 128;
1511   //  V = round( 0.439216 * R - 0.367788 * G - 0.071427 * B) + 128;
1512 
1513   // Compute U and V planes using EXACT values, reading 2x2 pixels at a time
1514   BYTE *dU = dst+srcFrameHeight*srcFrameWidth;
1515   BYTE *dV = dU+hSize*vSize;
1516   for (i=0; i<hSize; i++) {
1517     for (j=0; j<vSize; j++) {
1518       B=sBayer[0];
1519       G1=sBayer[1];
1520       G2=sBayer[stride];
1521       R=sBayer[stride+1];
1522       G=G1+G2;
1523       *dU = (BYTE)( ( (-19428 * R -19071*G +57569 * B) >> 17) + 128 );
1524       *dV = (BYTE)( ( ( 57569 * R -24103*G -9362 * B) >> 17) + 128 );
1525       sBayer+=2;
1526       dU++;
1527       dV++;
1528     }
1529     sBayer+=stride; // skip odd lines
1530   }
1531   // Compute Y plane
1532   BYTE *dY = dst;
1533   sBayer=src;
1534   const int * k; // kernel pointer
1535   int dxLeft, dxRight; // precalculated offsets, needed for first and last column
1536   const BYTE *sBayerTop, *sBayerBottom;
1537   for (i=0; i<srcFrameHeight; i++) {
1538     // Pointer to previous row, to the next if we are on the first one
1539     sBayerTop=sBayer+(i?(-stride):stride);
1540     // Pointer to next row, to the previous one if we are on the last
1541     sBayerBottom=sBayer+((i<lastRow)?stride:(-stride));
1542     // offset to previous column, to the next if we are on the first col
1543     dxLeft=1;
1544     for (j=0; j<srcFrameWidth; j++) {
1545       // offset to next column, to previous if we are on the last one
1546       dxRight=j<lastCol?1:(-1);
1547       // find the proper kernel according to the current pixel color
1548       if ( (i ^ j) & 1)  k=(j&1)?kG1:kG2; // green 1 or green 2
1549       else if (!(i & 1))  k=kB; // blue
1550       else /* if (!(j & 1)) */ k=kR; // red
1551 
1552       // apply the proper kernel to this pixel and surrounding ones
1553       *dY= (BYTE)(clip( (k[0])*(int)sBayerTop[dxLeft]+
1554       (k[1])*(int)(*sBayerTop)+
1555       (k[2])*(int)sBayerTop[dxRight]+
1556       (k[3])*(int)sBayer[dxLeft]+
1557       (k[4])*(int)(*sBayer)+
1558       (k[5])*(int)sBayer[dxRight]+
1559       (k[6])*(int)sBayerBottom[dxLeft]+
1560       (k[7])*(int)(*sBayerBottom)+
1561       (k[8])*(int)sBayerBottom[dxRight], (1<<24)) >> 16);
1562       dY++;
1563       sBayer++;
1564       sBayerTop++;
1565       sBayerBottom++;
1566       dxLeft=-1;
1567     }
1568   }
1569 
1570   if (bytesReturned)
1571     *bytesReturned = srcFrameHeight*srcFrameWidth+2*hSize*vSize;
1572 
1573   return true;
1574 }
1575 else
1576 #endif //USE_SBGGR8_NATIVE
1577 {
1578   // shortest but less efficient (one malloc per conversion!)
1579   BYTE * tempDest=(BYTE*)malloc(3*srcFrameWidth*srcFrameHeight);
1580   SBGGR8toRGB(src, tempDest, NULL);
1581   bool r = RGBtoYUV420P(tempDest, dst, bytesReturned, 3, 0, 2);
1582   free(tempDest);
1583   return r;
1584 }
1585 }
1586 
SBGGR8toRGB(const BYTE * src,BYTE * dst,PINDEX * bytesReturned) const1587 bool PStandardColourConverter::SBGGR8toRGB(const BYTE * src,
1588                                            BYTE       * dst,
1589                                            PINDEX     * bytesReturned) const
1590 {
1591   if (src == dst) {
1592     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
1593     return false;
1594   }
1595 
1596   if (verticalFlip) {
1597     PTRACE(2,"PColCnv\tCannot do vertical flip, not implemented.");
1598     return false;
1599   }
1600 
1601   long int i;
1602   const BYTE *rawpt;
1603   BYTE *scanpt;
1604   long int size;
1605 
1606   rawpt = src;
1607   scanpt = dst;
1608   long int WIDTH = srcFrameWidth, HEIGHT = srcFrameHeight;
1609   size = WIDTH*HEIGHT;
1610 
1611   for ( i = 0; i < size; i++ ) {
1612     if ( (i/WIDTH) % 2 == 0 ) {
1613       if ( (i % 2) == 0 ) {
1614         /* B */
1615         if ( (i > WIDTH) && ((i % WIDTH) > 0) ) {
1616           *scanpt++ = (BYTE) ((*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4);  /* R */
1617           *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt+1)+ *(rawpt+WIDTH)+*(rawpt-WIDTH))/4);  /* G */
1618           *scanpt++ = *rawpt;         /* B */
1619         } else {
1620           /* first line or left column */
1621           *scanpt++ = *(rawpt+WIDTH+1);   /* R */
1622           *scanpt++ = (BYTE) ((*(rawpt+1)+*(rawpt+WIDTH))/2); /* G */
1623           *scanpt++ = *rawpt;       /* B */
1624         }
1625       } else {
1626         /* (B)G */
1627         if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) {
1628           *scanpt++ = (BYTE) ((*(rawpt+WIDTH)+*(rawpt-WIDTH))/2); /* R */
1629           *scanpt++ = *rawpt;         /* G */
1630           *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt+1))/2);   /* B */
1631         } else {
1632           /* first line or right column */
1633           *scanpt++ = *(rawpt+WIDTH); /* R */
1634           *scanpt++ = *rawpt;   /* G */
1635           *scanpt++ = *(rawpt-1); /* B */
1636         }
1637       }
1638     } else {
1639       if ( (i % 2) == 0 ) {
1640         /* G(R) */
1641         if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) {
1642           *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt+1))/2);   /* R */
1643           *scanpt++ = *rawpt;         /* G */
1644           *scanpt++ = (BYTE) ((*(rawpt+WIDTH)+*(rawpt-WIDTH))/2); /* B */
1645         } else {
1646           /* bottom line or left column */
1647           *scanpt++ = *(rawpt+1);   /* R */
1648           *scanpt++ = *rawpt;         /* G */
1649           *scanpt++ = *(rawpt-WIDTH);   /* B */
1650         }
1651       } else {
1652         /* R */
1653         if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) {
1654           *scanpt++ = *rawpt;         /* R */
1655           *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt+1)+*(rawpt-WIDTH)+*(rawpt+WIDTH))/4);  /* G */
1656           *scanpt++ = (BYTE) ((*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4);  /* B */
1657         } else {
1658           /* bottom line or right column */
1659           *scanpt++ = *rawpt;        /* R */
1660           *scanpt++ = (BYTE) ((*(rawpt-1)+*(rawpt-WIDTH))/2);  /* G */
1661           *scanpt++ = *(rawpt-WIDTH-1);    /* B */
1662         }
1663       }
1664     }
1665     rawpt++;
1666   }
1667 
1668   if (bytesReturned)
1669     *bytesReturned = scanpt - dst;
1670 
1671   return true;
1672 }
1673 
1674 #define SCALEBITS 12
1675 #define ONE_HALF  (1UL << (SCALEBITS - 1))
1676 #define FIX(x)    ((int) ((x) * (1UL<<SCALEBITS) + 0.5))
1677 
1678 /*
1679  * Please note when converting colorspace from YUV to RGB.
1680  * Not all YUV have the same colorspace.
1681  *
1682  * For instance Jpeg use this formula
1683  * YCbCr is defined per CCIR 601-1, except that Cb and Cr are
1684  * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
1685  * The conversion equations to be implemented are therefore
1686  *      Y  =  0.29900 * R + 0.58700 * G + 0.11400 * B
1687  *      Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B  + CENTERJSAMPLE
1688  *      Cr =  0.50000 * R - 0.41869 * G - 0.08131 * B  + CENTERJSAMPLE
1689  * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
1690  * So
1691  * R = Y + 1.402 (Cr-128)
1692  * G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
1693  * B = Y + 1.772 (Cb-128)
1694  *
1695  */
YUV420PtoRGB(const BYTE * srcFrameBuffer,BYTE * dstFrameBuffer,PINDEX * bytesReturned,unsigned rgbIncrement,unsigned redOffset,unsigned blueOffset) const1696 bool PStandardColourConverter::YUV420PtoRGB(const BYTE * srcFrameBuffer,
1697                                             BYTE * dstFrameBuffer,
1698                                             PINDEX * bytesReturned,
1699                                             unsigned rgbIncrement,
1700                                             unsigned redOffset,
1701                                             unsigned blueOffset) const
1702 {
1703   if (srcFrameBuffer == dstFrameBuffer) {
1704     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
1705     return false;
1706   }
1707 
1708   static const unsigned greenOffset = 1;
1709 
1710   unsigned height = PMIN(srcFrameHeight, dstFrameHeight)&(UINT_MAX-1); // Must be even
1711   unsigned width = PMIN(srcFrameWidth, dstFrameWidth)&(UINT_MAX-1);
1712 
1713   unsigned    yplanesize = srcFrameWidth*srcFrameHeight;
1714   const BYTE *yplane     = srcFrameBuffer;        // 1 byte Y (luminance) for each pixel
1715   const BYTE *uplane     = yplane+yplanesize;     // 1 byte U for a block of 4 pixels
1716   const BYTE *vplane     = uplane+(yplanesize/4); // 1 byte V for a block of 4 pixels
1717 
1718   BYTE * dstScanLine   = dstFrameBuffer;
1719 
1720 #ifdef P_MEDIALIB
1721   const BYTE *y0;
1722   const BYTE *y1;
1723   const BYTE *cb;
1724   const BYTE *cr;
1725   unsigned int   x,p;
1726 
1727   for(int i = 0; i < srcFrameHeight; i += 2) {
1728     p = i*srcFrameWidth;
1729     x = p/4;
1730     y0 = yplane + p;
1731     y1 = y0 + srcFrameWidth;
1732     cb = uplane + x;
1733     cr = vplane + x;
1734     mlib_VideoColorJFIFYCC2RGB420_Nearest(dstFrameBuffer,
1735                                           dstFrameBuffer+3*dstFrameWidth,
1736                                           y0, y1, cb, cr,
1737                                           srcFrameWidth);
1738     dstFrameBuffer += 6*dstFrameWidth;
1739   }
1740 #else
1741 
1742   unsigned int srcPixpos[4] = { 0, 1, srcFrameWidth, srcFrameWidth + 1 };
1743   unsigned int dstPixpos[4] = { 0, rgbIncrement, dstFrameWidth*rgbIncrement, (dstFrameWidth+1)*rgbIncrement };
1744 
1745   if (verticalFlip) {
1746     dstScanLine += (dstFrameHeight - 2) * dstFrameWidth * rgbIncrement;
1747     dstPixpos[0] = dstPixpos[2];
1748     dstPixpos[1] = dstPixpos[3];
1749     dstPixpos[2] = 0;
1750     dstPixpos[3] = rgbIncrement;
1751   }
1752 
1753   for (unsigned y = 0; y < height; y += 2)
1754   {
1755     BYTE * dstPixelGroup = dstScanLine;
1756     for (unsigned x = 0; x < width; x += 2)
1757     {
1758       // The RGB value without luminance
1759       long cb = *uplane-128;
1760       long cr = *vplane-128;
1761       long rd = FIX(1.40200) * cr + ONE_HALF;
1762       long gd = -FIX(0.34414) * cb -FIX(0.71414) * cr + ONE_HALF;
1763       long bd = FIX(1.77200) * cb + ONE_HALF;
1764 
1765       // Add luminance to each of the 4 pixels
1766 
1767       for (unsigned p = 0; p < 4; p++)
1768       {
1769         int yvalue = yplane[srcPixpos[p]];
1770 
1771         int l = yvalue << SCALEBITS;
1772 
1773         int r = (l+rd)>>SCALEBITS;
1774         int g = (l+gd)>>SCALEBITS;
1775         int b = (l+bd)>>SCALEBITS;
1776 
1777         BYTE * rgpPtr = dstPixelGroup + dstPixpos[p];
1778         rgpPtr[redOffset]   = LIMIT(r);
1779         rgpPtr[greenOffset] = LIMIT(g);
1780         rgpPtr[blueOffset]  = LIMIT(b);
1781         if (rgbIncrement == 4)
1782           rgpPtr[3] = 0;
1783       }
1784 
1785       yplane += 2;
1786       dstPixelGroup += rgbIncrement*2;
1787 
1788       uplane++;
1789       vplane++;
1790     }
1791 
1792     yplane += srcFrameWidth;
1793 
1794     dstScanLine += (verticalFlip?-2:2)*rgbIncrement*dstFrameWidth;
1795   }
1796 
1797   if (bytesReturned != NULL)
1798     *bytesReturned = dstFrameBytes;
1799 #endif
1800 
1801   return true;
1802 }
1803 
1804 
YUV420PtoRGB565(const BYTE * srcFrameBuffer,BYTE * dstFrameBuffer,PINDEX * bytesReturned) const1805 PBoolean PStandardColourConverter::YUV420PtoRGB565(const BYTE * srcFrameBuffer,
1806                                             BYTE * dstFrameBuffer,
1807                                             PINDEX * bytesReturned) const
1808 {
1809   if (srcFrameBuffer == dstFrameBuffer) {
1810     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
1811     return false;
1812   }
1813 
1814   static const unsigned rgbIncrement = 2;
1815 
1816   unsigned height = PMIN(srcFrameHeight, dstFrameHeight)&(UINT_MAX-1); // Must be even
1817   unsigned width = PMIN(srcFrameWidth, dstFrameWidth)&(UINT_MAX-1);
1818 
1819   unsigned    yplanesize = srcFrameWidth*srcFrameHeight;
1820   const BYTE *yplane     = srcFrameBuffer;        // 1 byte Y (luminance) for each pixel
1821   const BYTE *uplane     = yplane+yplanesize;     // 1 byte U for a block of 4 pixels
1822   const BYTE *vplane     = uplane+(yplanesize/4); // 1 byte V for a block of 4 pixels
1823 
1824   BYTE * dstScanLine   = dstFrameBuffer;
1825 
1826   unsigned int srcPixpos[4] = { 0, 1, srcFrameWidth, srcFrameWidth + 1 };
1827   unsigned int dstPixpos[4] = { 0, rgbIncrement, dstFrameWidth*rgbIncrement, (dstFrameWidth+1)*rgbIncrement };
1828 
1829   if (verticalFlip) {
1830     dstScanLine += (dstFrameHeight - 2) * dstFrameWidth * rgbIncrement;
1831     dstPixpos[0] = dstFrameWidth*rgbIncrement;
1832     dstPixpos[1] = (dstFrameWidth +1)*rgbIncrement;
1833     dstPixpos[2] = 0;
1834     dstPixpos[3] = 1*rgbIncrement;
1835   }
1836 
1837   for (unsigned y = 0; y < height; y += 2)
1838   {
1839     BYTE * dstPixelGroup = dstScanLine;
1840     for (unsigned x = 0; x < width; x += 2)
1841     {
1842       // The RGB value without luminance
1843       long cb = *uplane-128;
1844       long cr = *vplane-128;
1845       long rd = FIX(1.40200) * cr + ONE_HALF;
1846       long gd = -FIX(0.34414) * cb -FIX(0.71414) * cr + ONE_HALF;
1847       long bd = FIX(1.77200) * cb + ONE_HALF;
1848 
1849       // Add luminance to each of the 4 pixels
1850 
1851       for (unsigned p = 0; p < 4; p++)
1852       {
1853         int yvalue = *(yplane + srcPixpos[p]);
1854 
1855         int l = yvalue << SCALEBITS;
1856 
1857         int r = (l+rd)>>SCALEBITS;
1858         int g = (l+gd)>>SCALEBITS;
1859         int b = (l+bd)>>SCALEBITS;
1860 
1861         BYTE * rgpPtr = dstPixelGroup + dstPixpos[p];
1862         WORD r16,g16,b16;
1863         WORD color;
1864         WORD *colorptr=NULL;
1865 
1866         r16 = ( (LIMIT(r)) >> 3) & 0x001f;
1867         g16 = ( (LIMIT(g)) >> 2) & 0x003f;
1868         b16 = ( (LIMIT(b)) >> 3) & 0x001f;
1869         color = ((r16 << 11) & (0xf800))
1870           | ((g16 << 5 ) & (0x07e0))
1871           | ((b16 ) & (0x001f));
1872         colorptr = (WORD *)(rgpPtr);
1873         *colorptr = color;
1874       }
1875 
1876       yplane += 2;
1877       dstPixelGroup += rgbIncrement*2;
1878 
1879       uplane++;
1880       vplane++;
1881     }
1882 
1883     yplane += srcFrameWidth;
1884 
1885     dstScanLine += (verticalFlip?-2:2)*rgbIncrement*dstFrameWidth;
1886   }
1887 
1888   if (bytesReturned != NULL)
1889     *bytesReturned = dstFrameBytes;
1890 
1891   return true;
1892 }
1893 
1894 
PSTANDARD_COLOUR_CONVERTER(SBGGR8,RGB24)1895 PSTANDARD_COLOUR_CONVERTER(SBGGR8,RGB24)
1896 {
1897   return SBGGR8toRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned);
1898 }
1899 
PSTANDARD_COLOUR_CONVERTER(SBGGR8,YUV420P)1900 PSTANDARD_COLOUR_CONVERTER(SBGGR8,YUV420P)
1901 {
1902   return SBGGR8toYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned);
1903 }
1904 
PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB24)1905 PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB24)
1906 {
1907   return YUV420PtoRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 0, 2);
1908 }
1909 
PSTANDARD_COLOUR_CONVERTER(YUV420P,BGR24)1910 PSTANDARD_COLOUR_CONVERTER(YUV420P,BGR24)
1911 {
1912   return YUV420PtoRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 2, 0);
1913 }
1914 
PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB32)1915 PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB32)
1916 {
1917   return YUV420PtoRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 0, 2);
1918 }
1919 
PSTANDARD_COLOUR_CONVERTER(YUV420P,BGR32)1920 PSTANDARD_COLOUR_CONVERTER(YUV420P,BGR32)
1921 {
1922   return YUV420PtoRGB(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 2, 0);
1923 }
1924 
1925 
PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB565)1926 PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB565)
1927 {
1928   return YUV420PtoRGB565(srcFrameBuffer, dstFrameBuffer, bytesReturned);
1929 }
1930 
PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB16)1931 PSTANDARD_COLOUR_CONVERTER(YUV420P,RGB16)
1932 {
1933   return YUV420PtoRGB565(srcFrameBuffer, dstFrameBuffer, bytesReturned);
1934 }
1935 
1936 
SwapRedAndBlueRow(const BYTE * srcRowPtr,BYTE * dstRowPtr,unsigned width,unsigned srcIncrement,unsigned dstIncrement)1937 static void SwapRedAndBlueRow(const BYTE * srcRowPtr,
1938                               BYTE * dstRowPtr,
1939                               unsigned width,
1940                               unsigned srcIncrement,
1941                               unsigned dstIncrement)
1942 {
1943   for (unsigned x = 0; x < width; x++) {
1944     BYTE temp = srcRowPtr[0]; // Do it this way in case src and dst are same buffer
1945     dstRowPtr[0] = srcRowPtr[2];
1946     dstRowPtr[1] = srcRowPtr[1];
1947     dstRowPtr[2] = temp;
1948 
1949     srcRowPtr += srcIncrement;
1950     dstRowPtr += dstIncrement;
1951   }
1952 }
1953 
SwapRedAndBlue(const BYTE * srcFrameBuffer,BYTE * dstFrameBuffer,PINDEX * bytesReturned,unsigned srcIncrement,unsigned dstIncrement) const1954 bool PStandardColourConverter::SwapRedAndBlue(const BYTE * srcFrameBuffer,
1955                                               BYTE * dstFrameBuffer,
1956                                               PINDEX * bytesReturned,
1957                                               unsigned srcIncrement,
1958                                               unsigned dstIncrement) const
1959 {
1960   if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) {
1961     PTRACE(2,"PColCnv\tCannot do different sized RGB swap, not implemented.");
1962     return false;
1963   }
1964 
1965   unsigned srcRowSize = srcFrameBytes/srcFrameHeight;
1966   const BYTE * srcRowPtr = srcFrameBuffer;
1967 
1968   unsigned dstRowSize = dstFrameBytes/dstFrameHeight;
1969   BYTE * dstRowPtr = dstFrameBuffer;
1970 
1971   if (verticalFlip) {
1972     dstRowPtr += dstFrameHeight*dstRowSize;
1973 
1974     if (srcFrameBuffer == dstFrameBuffer) {
1975       PBYTEArray tempRow(PMAX(srcRowSize, dstRowSize));
1976       unsigned halfHeight = (srcFrameHeight+1)/2;
1977       for (unsigned y = 0; y < halfHeight; y++) {
1978         dstRowPtr -= dstRowSize;
1979         SwapRedAndBlueRow(dstRowPtr, tempRow.GetPointer(), dstFrameWidth, srcIncrement, dstIncrement);
1980         SwapRedAndBlueRow(srcRowPtr, dstRowPtr, srcFrameWidth, srcIncrement, dstIncrement);
1981         memcpy((BYTE *)srcRowPtr, tempRow, srcRowSize);
1982         srcRowPtr += srcRowSize;
1983       }
1984     }
1985     else {
1986       for (unsigned y = 0; y < srcFrameHeight; y++) {
1987         dstRowPtr -= dstRowSize;
1988         SwapRedAndBlueRow(srcRowPtr, dstRowPtr, srcFrameWidth, srcIncrement, dstIncrement);
1989         srcRowPtr += srcRowSize;
1990       }
1991     }
1992   }
1993   else {
1994     for (unsigned y = 0; y < srcFrameHeight; y++) {
1995       SwapRedAndBlueRow(srcRowPtr, dstRowPtr, srcFrameWidth, srcIncrement, dstIncrement);
1996       srcRowPtr += srcRowSize;
1997       dstRowPtr += dstRowSize;
1998     }
1999   }
2000 
2001   if (bytesReturned != NULL)
2002     *bytesReturned = dstFrameBytes;
2003   return true;
2004 }
2005 
2006 
PSTANDARD_COLOUR_CONVERTER(RGB24,BGR24)2007 PSTANDARD_COLOUR_CONVERTER(RGB24,BGR24)
2008 {
2009   return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 3);
2010 }
2011 
2012 
PSTANDARD_COLOUR_CONVERTER(BGR24,RGB24)2013 PSTANDARD_COLOUR_CONVERTER(BGR24,RGB24)
2014 {
2015   return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 3);
2016 }
2017 
2018 
PSTANDARD_COLOUR_CONVERTER(RGB24,BGR32)2019 PSTANDARD_COLOUR_CONVERTER(RGB24,BGR32)
2020 {
2021   return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 4);
2022 }
2023 
2024 
PSTANDARD_COLOUR_CONVERTER(BGR24,RGB32)2025 PSTANDARD_COLOUR_CONVERTER(BGR24,RGB32)
2026 {
2027   return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 3, 4);
2028 }
2029 
2030 
PSTANDARD_COLOUR_CONVERTER(RGB32,BGR24)2031 PSTANDARD_COLOUR_CONVERTER(RGB32,BGR24)
2032 {
2033   return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 3);
2034 }
2035 
2036 
PSTANDARD_COLOUR_CONVERTER(BGR32,RGB24)2037 PSTANDARD_COLOUR_CONVERTER(BGR32,RGB24)
2038 {
2039   return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 3);
2040 }
2041 
2042 
PSTANDARD_COLOUR_CONVERTER(RGB32,BGR32)2043 PSTANDARD_COLOUR_CONVERTER(RGB32,BGR32)
2044 {
2045   return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 4);
2046 }
2047 
2048 
PSTANDARD_COLOUR_CONVERTER(BGR32,RGB32)2049 PSTANDARD_COLOUR_CONVERTER(BGR32,RGB32)
2050 {
2051   return SwapRedAndBlue(srcFrameBuffer, dstFrameBuffer, bytesReturned, 4, 4);
2052 }
2053 
2054 
PSTANDARD_COLOUR_CONVERTER(RGB24,RGB32)2055 PSTANDARD_COLOUR_CONVERTER(RGB24,RGB32)
2056 {
2057   if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) {
2058     PTRACE(2,"PColCnv\tCannot do RGB 24/32 conversion on different sized image, not implemented.");
2059     return false;
2060   }
2061 
2062   // Go from bottom to top so can do in place conversion
2063   const BYTE * src = srcFrameBuffer+srcFrameBytes-1;
2064   BYTE * dst = dstFrameBuffer+dstFrameBytes-1;
2065 
2066   for (unsigned x = 0; x < srcFrameWidth; x++) {
2067     for (unsigned y = 0; y < srcFrameHeight; y++) {
2068       *dst-- = 0;
2069       for (unsigned p = 0; p < 3; p++)
2070         *dst-- = *src--;
2071     }
2072   }
2073 
2074   if (bytesReturned != NULL)
2075     *bytesReturned = dstFrameBytes;
2076   return true;
2077 }
2078 
2079 
PSTANDARD_COLOUR_CONVERTER(RGB32,RGB24)2080 PSTANDARD_COLOUR_CONVERTER(RGB32,RGB24)
2081 {
2082   if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) {
2083     PTRACE(2,"PColCnv\tCannot do RGB 32/24 conversion on different sized image, not implemented.");
2084     return false;
2085   }
2086 
2087   const BYTE * src = srcFrameBuffer;
2088   BYTE * dst = dstFrameBuffer;
2089 
2090   for (unsigned x = 0; x < srcFrameWidth; x++) {
2091     for (unsigned y = 0; y < srcFrameHeight; y++) {
2092       for (unsigned p = 0; p < 3; p++)
2093         *dst++ = *src++;
2094       src++;
2095     }
2096   }
2097 
2098   if (bytesReturned != NULL)
2099     *bytesReturned = dstFrameBytes;
2100   return true;
2101 }
2102 
2103 
2104 // Consider a YUV420P image of 8x2 pixels.
2105 //
2106 // A plane of Y values    A B C D E F G H
2107 //                        I J K L M N O P
2108 //
2109 // A plane of U values    1   2   3   4
2110 // A plane of V values    1   2   3   4 ....
2111 //
2112 // The U1/V1 samples correspond to the ABIJ pixels.
2113 //     U2/V2 samples correspond to the CDKL pixels.
2114 //
2115 // Consider a YUV411P image of 8x2 pixels.
2116 //
2117 // A plane of Y values as before.
2118 //
2119 // A plane of U values    1       2
2120 //                        3       4
2121 //
2122 // A plane of V values    1       2
2123 //                        3       4
2124 //
2125 // The U1/V1 samples correspond to the ABCD pixels.
2126 //     U2/V2 samples correspond to the EFGH pixels.
2127 //
2128 // I choose to reoganize the U and V samples by using
2129 // using U1 for ABCD, U3 for EFGH, U2 for IJKL, U4 for MNOP
2130 //
2131 // Possibly discarding U2/U4 completely, or using the
2132 // average of U1 and U2 might be easier for compression
2133 //
2134 // TODO:
2135 //
2136 // - Inplace converter
2137 // - Resizing / padding / scaling converter
2138 //
PSTANDARD_COLOUR_CONVERTER(YUV420P,YUV411P)2139 PSTANDARD_COLOUR_CONVERTER(YUV420P,YUV411P)
2140 {
2141   if (srcFrameBuffer == dstFrameBuffer) {
2142     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
2143     return false;
2144   }
2145 
2146   if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) {
2147     PTRACE(2,"PColCnv\tCannot do YUV 420/411 conversion on different sized image, not implemented.");
2148     return false;
2149   }
2150 
2151   // Copy over the Y plane.
2152   memcpy(dstFrameBuffer, srcFrameBuffer, srcFrameWidth*srcFrameHeight);
2153 
2154   unsigned linewidth = dstFrameWidth / 4;
2155 
2156   // Source data is the start of the U plane
2157   const BYTE* src = srcFrameBuffer + srcFrameWidth * srcFrameHeight;
2158 
2159   // Two output lines at a time
2160   BYTE *dst0 = dstFrameBuffer + dstFrameWidth * dstFrameHeight;
2161   BYTE *dst1 = dst0 + linewidth;
2162 
2163   unsigned x, y;
2164 
2165   // U plane
2166   for (y = 0; y < dstFrameHeight; y += 2) {
2167     for (x = 0; x < dstFrameWidth; x += 4) {
2168       *dst0++ = *src++;
2169       *dst1++ = *src++;
2170     }
2171 
2172     // Skip over the 2nd line we already did.
2173     dst0 += linewidth;
2174     dst1 = dst0 + linewidth;
2175   }
2176 
2177   // Source data is the start of the U plane
2178   src = srcFrameBuffer + srcFrameWidth * srcFrameHeight * 5 / 4;
2179 
2180   // Two output lines at a time
2181   dst0 = dstFrameBuffer + dstFrameWidth * dstFrameHeight * 5 / 4;
2182   dst1 = dst0 + linewidth;
2183 
2184   // V plane
2185   for (y = 0; y < dstFrameHeight; y += 2) {
2186     for (x = 0; x < dstFrameWidth; x += 4) {
2187       *dst0++ = *src++;
2188       *dst1++ = *src++;
2189     }
2190 
2191     // Skip over the 2nd line we already did.
2192     dst0 += linewidth;
2193     dst1 = dst0 + linewidth;
2194   }
2195 
2196   if (bytesReturned != NULL)
2197     *bytesReturned = dstFrameBytes;
2198 
2199   return true;
2200 }
2201 
2202 
2203 // YUV411P to YUV420P conversion
2204 //
2205 // Consider YUV411P U plane (. = pixel) :
2206 //
2207 // A... B... C... D...
2208 // E... F... G... H...
2209 // I... J... K... L...
2210 // M... N... O... P...
2211 //
2212 // We map this to a YUV420P plane by
2213 // discarding odd rows, and doubling up
2214 // the even row samples:
2215 //
2216 // A.A. B.B. C.C. D.D.
2217 // .... .... .... ....
2218 // I.I. J.J. K.K. L.L.
2219 // .... .... .... ....
2220 //
2221 // TODO:
2222 //
2223 // - Inplace converter
2224 // - Resizing / padding / scaling converter
2225 //
PSTANDARD_COLOUR_CONVERTER(YUV411P,YUV420P)2226 PSTANDARD_COLOUR_CONVERTER(YUV411P,YUV420P)
2227 {
2228   if (srcFrameBuffer == dstFrameBuffer) {
2229     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
2230     return false;
2231   }
2232 
2233   if ((dstFrameWidth != srcFrameWidth) || (dstFrameHeight != srcFrameHeight)) {
2234     PTRACE(2,"PColCnv\tCannot do YUV 411/420 conversion on different sized image, not implemented.");
2235     return false;
2236   }
2237 
2238   // Copy over the Y plane.
2239   memcpy(dstFrameBuffer, srcFrameBuffer, srcFrameWidth*srcFrameHeight);
2240 
2241   unsigned linewidth = dstFrameWidth / 4;
2242 
2243   // Source data is the start of the U plane
2244   const BYTE* src = srcFrameBuffer + srcFrameWidth * srcFrameHeight;
2245 
2246   // Output line
2247   BYTE *dst0 = dstFrameBuffer + dstFrameWidth * dstFrameHeight;
2248 
2249   unsigned x, y;
2250 
2251   // U plane
2252   for (y = 0; y < dstFrameHeight; y += 2) {
2253     for (x = 0; x < dstFrameWidth; x += 4) {
2254 
2255       // Double up the horizontal samples
2256       *dst0++ = *src;
2257       *dst0++ = *src++;
2258     }
2259 
2260     // Skip over the 2nd line we are decimating
2261     src += linewidth;
2262   }
2263 
2264   // Source data is the start of the U plane
2265   src = srcFrameBuffer + srcFrameWidth * srcFrameHeight * 5 / 4;
2266 
2267   // Output line
2268   dst0 = dstFrameBuffer + dstFrameWidth * dstFrameHeight * 5 / 4;
2269 
2270   // V plane
2271   for (y = 0; y < dstFrameHeight; y += 2) {
2272     for (x = 0; x < dstFrameWidth; x += 4) {
2273 
2274       // Double up the samples horizontal samples
2275       *dst0++ = *src;
2276       *dst0++ = *src++;
2277     }
2278 
2279     // Skip over the 2nd source line we already did.
2280     src += linewidth;
2281   }
2282 
2283   if (bytesReturned != NULL)
2284     *bytesReturned = dstFrameBytes;
2285 
2286   return true;
2287 }
2288 
2289 
2290 /*
2291  * Format UYVY or UYVY422(non planar) 4x4
2292  *
2293  * off: 0  U00 Y00 V01 Y00 U02 Y01 V03 Y01
2294  * off: 8  U10 Y10 V11 Y10 U12 Y11 V13 Y11
2295  * off:16  U20 Y20 V21 Y20 U22 Y21 V23 Y21
2296  * off:24  U30 Y30 V31 Y30 U32 Y31 V33 Y31
2297  * length:32 bytes
2298  */
PSTANDARD_COLOUR_CONVERTER(UYVY422,UYVY422)2299 PSTANDARD_COLOUR_CONVERTER(UYVY422, UYVY422)
2300 {
2301   if (bytesReturned != NULL)
2302     *bytesReturned = dstFrameBytes;
2303 
2304   if (srcFrameBuffer == dstFrameBuffer) {
2305     if (srcFrameWidth == dstFrameWidth && srcFrameHeight == dstFrameHeight)
2306       return true;
2307     if(srcFrameWidth < dstFrameWidth || srcFrameHeight < dstFrameHeight) {
2308       PTRACE(2,"PColCnv\tCannot do in place conversion, increasing image size.");
2309       return false;
2310     }
2311   }
2312 
2313   if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight))
2314     memcpy(dstFrameBuffer,srcFrameBuffer,srcFrameWidth*srcFrameHeight*2);
2315   else
2316     UYVY422WithCrop(srcFrameBuffer, dstFrameBuffer);
2317 
2318   return true;
2319 }
2320 
2321 /*
2322  * Format UYVY (or UYVY422) non planar (4x4)
2323  *
2324  * off: 0  U00 Y00 V01 Y00 U02 Y01 V03 Y01
2325  * off: 8  U10 Y10 V11 Y10 U12 Y11 V13 Y11
2326  * off:16  U20 Y20 V21 Y20 U22 Y21 V23 Y21
2327  * off:24  U30 Y30 V31 Y30 U32 Y31 V33 Y31
2328  * length:32 bytes
2329  *
2330  * NOTE: This algorithm works only if the width and the height is pair.
2331  */
UYVY422WithCrop(const BYTE * src_uyvy,BYTE * dst_uyvy) const2332 void PStandardColourConverter::UYVY422WithCrop(const BYTE *src_uyvy, BYTE *dst_uyvy) const
2333 {
2334   const BYTE *s;
2335   BYTE *d;
2336   unsigned int x, h;
2337   unsigned int npixels = dstFrameWidth * dstFrameHeight;
2338 
2339   s = src_uyvy;
2340   d = dst_uyvy;
2341 
2342   if ( (srcFrameWidth * srcFrameHeight) < npixels ) {
2343 
2344      // dest is bigger than the source. No subsampling.
2345      // Place the src in the middle of the destination.
2346      unsigned int yOffset = (dstFrameHeight - srcFrameHeight)/2;
2347      unsigned int xOffset = (dstFrameWidth - srcFrameWidth)/2;
2348 
2349      /* Top border */
2350      for (h=0; h<yOffset; h++)
2351       {
2352         for (x=0; x<dstFrameWidth/2; x++)
2353          {
2354            *d++ = BLACK_U;
2355            *d++ = BLACK_Y;
2356            *d++ = BLACK_V;
2357            *d++ = BLACK_Y;
2358          }
2359       }
2360 
2361      for (h=0; h<srcFrameHeight; h+=2)
2362       {
2363         /* Left border */
2364                for (x=0; x<xOffset/2; x++)
2365          {
2366            *d++ = BLACK_U;
2367            *d++ = BLACK_Y;
2368            *d++ = BLACK_V;
2369            *d++ = BLACK_Y;
2370          }
2371 
2372         /* Copy the first line keeping all information */
2373         memcpy(d, s, srcFrameWidth*2);
2374         d += srcFrameWidth*2;
2375         /* Right and Left border */
2376                for (x=0; x<xOffset/2; x++)
2377          {
2378            *d++ = BLACK_U;
2379            *d++ = BLACK_Y;
2380            *d++ = BLACK_V;
2381            *d++ = BLACK_Y;
2382          }
2383       }
2384      for (h=0; h<yOffset; h++)
2385       {
2386         for (x=0; x<dstFrameWidth/2; x++)
2387          {
2388            *d++ = BLACK_U;
2389            *d++ = BLACK_Y;
2390            *d++ = BLACK_V;
2391            *d++ = BLACK_Y;
2392          }
2393       }
2394 
2395   } else {
2396 
2397      /* FIXME */
2398 
2399    }
2400 
2401 }
2402 
2403 
2404 /*
2405  * Format UYVY or UYVY422(non planar) 4x4
2406  *
2407  * off: 0  U00 Y00 V01 Y00 U02 Y01 V03 Y01
2408  * off: 8  U10 Y10 V11 Y10 U12 Y11 V13 Y11
2409  * off:16  U20 Y20 V21 Y20 U22 Y21 V23 Y21
2410  * off:24  U30 Y30 V31 Y30 U32 Y31 V33 Y31
2411  * length:32 bytes
2412  *
2413  * Format YUV420P:
2414  * off: 00  Y00 Y01 Y02 Y03
2415  * off: 04  Y10 Y11 Y12 Y13
2416  * off: 08  Y20 Y21 Y22 Y23
2417  * off: 12  Y30 Y31 Y32 Y33
2418  * off: 16  U00 U02 U20 U22
2419  * off: 20  V00 V02 V20 V22
2420  *
2421  * So, we loose some bit of information when converting UYVY to YUV420
2422  *
2423  * NOTE: This algorithm works only if the width and the height is pair.
2424  */
UYVY422toYUV420PSameSize(const BYTE * uyvy,BYTE * yuv420p) const2425 void  PStandardColourConverter::UYVY422toYUV420PSameSize(const BYTE *uyvy, BYTE *yuv420p) const
2426 {
2427   const BYTE *s;
2428   BYTE *y, *u, *v;
2429   unsigned int x, h;
2430   int npixels = srcFrameWidth * srcFrameHeight;
2431 
2432   s = uyvy;
2433   y = yuv420p;
2434   u = yuv420p + npixels;
2435   v = u + npixels/4;
2436 
2437   for (h=0; h<srcFrameHeight; h+=2) {
2438 
2439      /* Copy the first line keeping all information */
2440      for (x=0; x<srcFrameWidth; x+=2) {
2441         *u++ = *s++;
2442         *y++ = *s++;
2443         *v++ = *s++;
2444         *y++ = *s++;
2445      }
2446      /* Copy the second line discarding u and v information */
2447      for (x=0; x<srcFrameWidth; x+=2) {
2448         s++;
2449         *y++ = *s++;
2450         s++;
2451         *y++ = *s++;
2452      }
2453   }
2454 }
2455 
2456 
2457 /*
2458  * Format UYVY (or UYVY422) non planar (4x4)
2459  *
2460  * off: 0  U00 Y00 V01 Y00 U02 Y01 V03 Y01
2461  * off: 8  U10 Y10 V11 Y10 U12 Y11 V13 Y11
2462  * off:16  U20 Y20 V21 Y20 U22 Y21 V23 Y21
2463  * off:24  U30 Y30 V31 Y30 U32 Y31 V33 Y31
2464  * length:32 bytes
2465  *
2466  * Format YUV420P:
2467  * off: 00  Y00 Y01 Y02 Y03
2468  * off: 04  Y10 Y11 Y12 Y13
2469  * off: 08  Y20 Y21 Y22 Y23
2470  * off: 12  Y30 Y31 Y32 Y33
2471  * off: 16  U00 U02 U20 U22
2472  * off: 20  V00 V02 V20 V22
2473  *
2474  * So, we loose some bit of information when converting YUY2 to YUV420
2475  *
2476  * NOTE: This algorithm works only if the width and the height is pair.
2477  */
UYVY422toYUV420PWithCrop(const BYTE * uyvy,BYTE * yuv420p) const2478 void PStandardColourConverter::UYVY422toYUV420PWithCrop(const BYTE *uyvy, BYTE *yuv420p) const
2479 {
2480   const BYTE *s;
2481   BYTE *y, *u, *v;
2482   unsigned int x, h;
2483   unsigned int npixels = dstFrameWidth * dstFrameHeight;
2484 
2485   s = uyvy;
2486   y = yuv420p;
2487   u = yuv420p + npixels;
2488   v = u + npixels/4;
2489 
2490   if ( (srcFrameWidth * srcFrameHeight) < npixels ) {
2491 
2492      // dest is bigger than the source. No subsampling.
2493      // Place the src in the middle of the destination.
2494      unsigned int yOffset = (dstFrameHeight - srcFrameHeight)/2;
2495      unsigned int xOffset = (dstFrameWidth - srcFrameWidth)/2;
2496      unsigned int bpixels = yOffset * dstFrameWidth;
2497 
2498      /* Top border */
2499      memset(y, BLACK_Y, bpixels);        y += bpixels;
2500      memset(u, BLACK_U, bpixels/4);        u += bpixels/4;
2501      memset(v, BLACK_V, bpixels/4);        v += bpixels/4;
2502 
2503      for (h=0; h<srcFrameHeight; h+=2)
2504       {
2505         /* Left border */
2506         memset(y, BLACK_Y, xOffset);        y += xOffset;
2507         memset(u, BLACK_U, xOffset/2);        u += xOffset/2;
2508         memset(v, BLACK_V, xOffset/2);        v += xOffset/2;
2509 
2510         /* Copy the first line keeping all information */
2511         for (x=0; x<srcFrameWidth; x+=2)
2512          {
2513            *u++ = *s++;
2514            *y++ = *s++;
2515            *v++ = *s++;
2516            *y++ = *s++;
2517          }
2518         /* Right and Left border */
2519         for (x=0; x<xOffset*2; x++)
2520           *y++ = BLACK_Y;
2521 
2522         /* Copy the second line discarding u and v information */
2523         for (x=0; x<srcFrameWidth; x+=2)
2524          {
2525            s++;
2526            *y++ = *s++;
2527            s++;
2528            *y++ = *s++;
2529          }
2530         /* Fill the border with black (right side) */
2531         memset(y, BLACK_Y, xOffset);        y += xOffset;
2532         memset(u, BLACK_U, xOffset/2);        u += xOffset/2;
2533         memset(v, BLACK_V, xOffset/2);        v += xOffset/2;
2534       }
2535      memset(y, BLACK_Y, bpixels);
2536      memset(u, BLACK_U, bpixels/4);
2537      memset(v, BLACK_V, bpixels/4);
2538 
2539 
2540   } else {
2541 
2542      // source is bigger than the destination
2543      // We are doing linear interpolation to find value.
2544 #define FIX_FLOAT       12
2545      unsigned int dx = (srcFrameWidth<<FIX_FLOAT)/dstFrameWidth;
2546      unsigned int dy = (srcFrameHeight<<FIX_FLOAT)/dstFrameHeight;
2547      unsigned int fy, fx;
2548 
2549      for (fy=0, h=0; h<dstFrameHeight; h+=2, fy+=dy*2)
2550       {
2551         /* Copy the first line with U&V */
2552         unsigned int yy = fy>>FIX_FLOAT;
2553         unsigned int yy2 = (fy+dy)>>FIX_FLOAT;
2554         const unsigned char *line1, *line2;
2555         unsigned char lastU, lastV;
2556 
2557         line1 = s + (yy*2*srcFrameWidth);
2558         line2 = s + (yy2*2*srcFrameWidth);
2559         lastU = line1[0];
2560         lastV = line1[2];
2561         for (fx=0, x=0; x<dstFrameWidth; x+=2, fx+=dx*2)
2562          {
2563            unsigned int xx = (fx>>FIX_FLOAT)*2;
2564            if ( (xx&2) == 0)
2565             {
2566               *u++ = lastU = (line1[xx+0] + line2[xx+0])/2;
2567               *v++ = lastV = (line1[xx+2] + line2[xx+2])/2;
2568             }
2569            else
2570             {
2571               *u++ = lastU;
2572               *v++ = lastV = (line1[xx+0] + line2[xx+0])/2;
2573             }
2574            *y++ = line1[xx+1];
2575            xx = ((fx+dx)>>FIX_FLOAT)*2;
2576            if ( (xx&2) == 0)
2577              lastU = (line1[xx+0] + line2[xx+0])/2;
2578            else
2579              lastV = (line1[xx+0] + line2[xx+0])/2;
2580            *y++ = line1[xx+1];
2581          }
2582 
2583         /* Copy the second line without U&V */
2584         for (fx=0, x=0; x<dstFrameWidth; x++, fx+=dx)
2585          {
2586            unsigned int xx = (fx>>FIX_FLOAT)*2;
2587            *y++ = line2[xx+1];
2588          }
2589       } /* end of for (fy=0, h=0; h<dstFrameHeight; h+=2, fy+=dy*2) */
2590 
2591    }
2592 
2593 }
2594 
2595 
2596 /*
2597  * The following functions converts video from IEEE 1394 cameras into
2598  * YUV420P format. The video format of IEEE 1394 cameras can be found
2599  *  at Section 2.1.3 of
2600 http://www.1394ta.org/Download/Technology/Specifications/2000/IIDC_Spec_v1_30.pdf
2601  * 320x240 and 160x120 resolutions are used.
2602  *
2603  */
PSTANDARD_COLOUR_CONVERTER(UYVY422,YUV420P)2604 PSTANDARD_COLOUR_CONVERTER(UYVY422,YUV420P)
2605 {
2606   if (srcFrameBuffer == dstFrameBuffer) {
2607     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
2608     return false;
2609   }
2610 
2611   if ((srcFrameWidth==dstFrameWidth) && (srcFrameHeight==dstFrameHeight))
2612     UYVY422toYUV420PSameSize(srcFrameBuffer, dstFrameBuffer);
2613   else
2614     UYVY422toYUV420PWithCrop(srcFrameBuffer, dstFrameBuffer);
2615 
2616   if (bytesReturned != NULL)
2617     *bytesReturned = dstFrameBytes;
2618 
2619   return true;
2620 }
2621 
PSTANDARD_COLOUR_CONVERTER(UYV444,YUV420P)2622 PSTANDARD_COLOUR_CONVERTER(UYV444,YUV420P)
2623 {
2624   if (srcFrameBuffer == dstFrameBuffer) {
2625     PTRACE(2,"PColCnv\tCannot do in-place conversion, not implemented.");
2626     return false;
2627   }
2628 
2629   unsigned int row,column;
2630   unsigned char *y = dstFrameBuffer;  //Initialise y,u,v here, to stop compiler warnings.
2631   unsigned char *u = dstFrameBuffer + dstFrameWidth*dstFrameHeight;
2632   unsigned char *v = dstFrameBuffer + dstFrameWidth*(dstFrameHeight + dstFrameHeight/4);
2633   const unsigned char *src = srcFrameBuffer;
2634 
2635   for(row=0; row < PMIN(srcFrameHeight, dstFrameHeight); row+=2) {
2636     y = dstFrameBuffer + dstFrameWidth*row;
2637     u = dstFrameBuffer + dstFrameWidth*dstFrameHeight + dstFrameWidth*row/4;
2638     v = dstFrameBuffer + dstFrameWidth*(dstFrameHeight + dstFrameHeight/4) + dstFrameWidth*row/4;
2639     src = srcFrameBuffer + row*srcFrameWidth*3;
2640     for(column=0; column < PMIN(srcFrameWidth, dstFrameWidth); column+=2) {
2641       *(u++) = (unsigned char)(((unsigned int)src[0] + src[3] + src[srcFrameWidth*3] + src[3+srcFrameWidth*3])/4);
2642       *(y++) = src[1];
2643       *(v++) = (unsigned char)(((unsigned int)src[2] + src[5] + src[srcFrameWidth*3] +src[3+srcFrameWidth*3])/4);
2644       *(y++) = src[4];
2645       src += 6;
2646     }
2647     for(column = PMIN(srcFrameWidth, dstFrameWidth);
2648   column < dstFrameWidth; column+=2) {
2649       *(u++) = BLACK_U;
2650       *(y++) = BLACK_Y;
2651       *(v++) = BLACK_V;
2652       *(y++) = BLACK_Y;
2653     }
2654     y = dstFrameBuffer + dstFrameWidth*(row+1);
2655     src = srcFrameBuffer + (row+1)*srcFrameWidth*3;
2656     for(column=0; column < PMIN(srcFrameWidth, dstFrameWidth); column++) {
2657       src++;
2658       *(y++) = *(src++);
2659       src++;
2660     }
2661     for(column = PMIN(srcFrameWidth, dstFrameWidth);
2662   column < dstFrameWidth; column++)
2663       *(y++) = BLACK_Y;
2664   }
2665   for(row = PMIN(srcFrameHeight, dstFrameHeight);
2666       row<dstFrameHeight; row+=2) {
2667     for(column = 0; column < dstFrameWidth; column+=2) {
2668       *(u++) = BLACK_U;
2669       *(y++) = BLACK_Y;
2670       *(v++) = BLACK_V;
2671       *(y++) = BLACK_Y;
2672     }
2673     for(column = 0; column < dstFrameWidth; column+=2) {
2674       *(y++) = BLACK_Y;
2675       *(y++) = BLACK_Y;
2676     }
2677   }
2678   if (bytesReturned != NULL)
2679     *bytesReturned = dstFrameBytes;
2680   return true;
2681 }
2682 
2683 #if  defined (__GNUC__) || defined (__sun)
2684 #ifndef P_MACOSX
2685 /*
2686  * Convert a MJPEG Buffer to one plane pixel format (RGB24, BGR24, GRAY)
2687  * image need to be same size.
2688  */
MJPEGtoXXXSameSize(const BYTE * mjpeg,BYTE * rgb,int format)2689 bool PStandardColourConverter::MJPEGtoXXXSameSize(const BYTE *mjpeg, BYTE *rgb, int format)
2690 {
2691   BYTE *components[1];
2692 
2693   struct jdec_private *jdec;
2694 
2695   components[0] = rgb;
2696 
2697   jdec = tinyjpeg_init();
2698 
2699   if (jdec == NULL) {
2700      PTRACE(2, "PColCnv\tJpeg error: Can't allocate memory");
2701      return false;
2702   }
2703   tinyjpeg_set_flags(jdec, TINYJPEG_FLAGS_MJPEG_TABLE);
2704   tinyjpeg_set_components(jdec, components, 1);
2705   if (tinyjpeg_parse_header(jdec, mjpeg, srcFrameBytes) < 0) {
2706      PTRACE(2, "PColCnv\tJpeg error: " << tinyjpeg_get_errorstring(jdec));
2707      free(jdec);
2708      return false;
2709   }
2710   if (tinyjpeg_decode(jdec, format) < 0) {
2711      PTRACE(2, "PColCnv\tJpeg error: " << tinyjpeg_get_errorstring(jdec));
2712      free(jdec);
2713      return false;
2714   }
2715 
2716   free(jdec);
2717   return true;
2718 }
2719 
2720 
MJPEGtoXXX(const BYTE * mjpeg,BYTE * output_data,PINDEX * bytesReturned,int format)2721 bool PStandardColourConverter::MJPEGtoXXX(const BYTE *mjpeg,
2722                                           BYTE *output_data,
2723                                           PINDEX *bytesReturned,
2724                                           int format)
2725 {
2726   if ((srcFrameWidth | dstFrameWidth | srcFrameHeight | dstFrameHeight) & 0xf) {
2727     PTRACE(2,"PColCnv\tError MJPEG decoder need width and height to be a multiple of 16");
2728     return false;
2729   }
2730 
2731   if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight)) {
2732      if (MJPEGtoXXXSameSize(mjpeg, output_data, format) == false)
2733        return false;
2734   } else {
2735      /* not efficient (convert then resize) */
2736      /* TODO: */
2737      return false;
2738   }
2739 
2740   if (bytesReturned != NULL)
2741     *bytesReturned = dstFrameBytes;
2742 
2743   return true;
2744 
2745 }
2746 
PSTANDARD_COLOUR_CONVERTER(MJPEG,RGB24)2747 PSTANDARD_COLOUR_CONVERTER(MJPEG,RGB24)
2748 {
2749   return MJPEGtoXXX(srcFrameBuffer, dstFrameBuffer, bytesReturned, TINYJPEG_FMT_RGB24);
2750 }
2751 
PSTANDARD_COLOUR_CONVERTER(MJPEG,BGR24)2752 PSTANDARD_COLOUR_CONVERTER(MJPEG,BGR24)
2753 {
2754   return MJPEGtoXXX(srcFrameBuffer, dstFrameBuffer, bytesReturned, TINYJPEG_FMT_BGR24);
2755 }
2756 
PSTANDARD_COLOUR_CONVERTER(MJPEG,Grey)2757 PSTANDARD_COLOUR_CONVERTER(MJPEG,Grey)
2758 {
2759   return MJPEGtoXXX(srcFrameBuffer, dstFrameBuffer, bytesReturned, TINYJPEG_FMT_GREY);
2760 }
2761 
PSTANDARD_COLOUR_CONVERTER(JPEG,RGB24)2762 PSTANDARD_COLOUR_CONVERTER(JPEG,RGB24)
2763 {
2764   return MJPEGtoXXX(srcFrameBuffer, dstFrameBuffer, bytesReturned, TINYJPEG_FMT_RGB24);
2765 }
2766 
PSTANDARD_COLOUR_CONVERTER(JPEG,BGR24)2767 PSTANDARD_COLOUR_CONVERTER(JPEG,BGR24)
2768 {
2769   return MJPEGtoXXX(srcFrameBuffer, dstFrameBuffer, bytesReturned, TINYJPEG_FMT_BGR24);
2770 }
2771 
PSTANDARD_COLOUR_CONVERTER(JPEG,Grey)2772 PSTANDARD_COLOUR_CONVERTER(JPEG,Grey)
2773 {
2774   return MJPEGtoXXX(srcFrameBuffer, dstFrameBuffer, bytesReturned, TINYJPEG_FMT_GREY);
2775 }
2776 
2777 /*
2778  * Convert a MJPEG Buffer to YUV420P
2779  * image need to be same size.
2780  */
MJPEGtoYUV420PSameSize(const BYTE * mjpeg,BYTE * yuv420p)2781 bool PStandardColourConverter::MJPEGtoYUV420PSameSize(const BYTE *mjpeg, BYTE *yuv420p)
2782 {
2783   BYTE *components[4];
2784   struct jdec_private *jdec;
2785 
2786   int npixels = srcFrameWidth * srcFrameHeight;
2787 
2788   components[0] = yuv420p;
2789   components[1] = yuv420p + npixels;
2790   components[2] = yuv420p + npixels + npixels/4;
2791   components[3] = NULL;
2792 
2793   jdec = tinyjpeg_init();
2794 
2795   if (jdec == NULL) {
2796     PTRACE(2, "PColCnv\tJpeg error: Can't allocate memory");
2797     return false;
2798   }
2799   tinyjpeg_set_flags(jdec, TINYJPEG_FLAGS_MJPEG_TABLE);
2800   tinyjpeg_set_components(jdec, components, 4);
2801   if (tinyjpeg_parse_header(jdec, mjpeg, srcFrameBytes) < 0) {
2802      PTRACE(2, "PColCnv\tJpeg error: " << tinyjpeg_get_errorstring(jdec));
2803      free(jdec);
2804      return false;
2805   }
2806   if (tinyjpeg_decode(jdec, TINYJPEG_FMT_YUV420P) < 0) {
2807      PTRACE(2, "PColCnv\tJpeg error: " << tinyjpeg_get_errorstring(jdec));
2808      free(jdec);
2809      return false;
2810   }
2811 
2812   free(jdec);
2813   return true;
2814 }
2815 
2816 /*
2817  * Convert a MJPEG or JPEG buffer to YUV420P
2818  *
2819  */
MJPEGtoYUV420P(const BYTE * mjpeg,BYTE * yuv420p,PINDEX * bytesReturned)2820 bool PStandardColourConverter::MJPEGtoYUV420P(const BYTE *mjpeg,
2821                                               BYTE *yuv420p,
2822                                               PINDEX *bytesReturned)
2823 {
2824   if ((srcFrameWidth | dstFrameWidth | srcFrameHeight | dstFrameHeight) & 0xf) {
2825     PTRACE(2,"PColCnv\tError in MJPEG to YUV420P converter, All size need to be a multiple of 16.");
2826     return false;
2827   }
2828 
2829   if ((srcFrameWidth == dstFrameWidth) && (srcFrameHeight == dstFrameHeight)) {
2830 
2831      PTRACE(2,"PColCnv\tMJPEG to YUV420P\n");
2832      if (MJPEGtoYUV420PSameSize(mjpeg, yuv420p) == false)
2833        return false;
2834 
2835   } else {
2836      /* Very not efficient */
2837      unsigned int frameBytes = srcFrameWidth * srcFrameHeight * 3 / 2;
2838      BYTE *intermed = intermediateFrameStore.GetPointer(frameBytes);
2839      MJPEGtoYUV420PSameSize(mjpeg, intermed);
2840      CopyYUV420P(0, 0, srcFrameWidth, srcFrameHeight, srcFrameWidth, srcFrameHeight, intermed,
2841                  0, 0, dstFrameWidth, dstFrameHeight, dstFrameWidth, dstFrameHeight, yuv420p,
2842                      resizeMode);
2843   }
2844 
2845   if (bytesReturned != NULL)
2846     *bytesReturned = dstFrameBytes;
2847 
2848   return true;
2849 }
2850 
2851 /*
2852  * MJPEG to YUV420P
2853  */
PSTANDARD_COLOUR_CONVERTER(MJPEG,YUV420P)2854 PSTANDARD_COLOUR_CONVERTER(MJPEG,YUV420P)
2855 {
2856   return MJPEGtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned);
2857 }
2858 
2859 /*
2860  * JPEG to YUV420P
2861  */
PSTANDARD_COLOUR_CONVERTER(JPEG,YUV420P)2862 PSTANDARD_COLOUR_CONVERTER(JPEG,YUV420P)
2863 {
2864   return MJPEGtoYUV420P(srcFrameBuffer, dstFrameBuffer, bytesReturned);
2865 }
2866 
2867 
2868 #endif // P_MACOSX
2869 #endif // __GNUC__
2870 
2871 #ifdef _MSC_VER
2872 #pragma warning(default : 4244)
2873 #endif
2874 
2875 #endif
2876 
2877 // End Of File ///////////////////////////////////////////////////////////////
2878