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