Coverage Report

Created: 2025-06-16 07:00

/src/imagemagick/coders/yuv.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                            Y   Y  U   U  V   V                              %
7
%                             Y Y   U   U  V   V                              %
8
%                              Y    U   U  V   V                              %
9
%                              Y    U   U   V V                               %
10
%                              Y     UUU     V                                %
11
%                                                                             %
12
%                                                                             %
13
%            Read/Write Raw CCIR 601 4:1:1 or 4:2:2 Image Format              %
14
%                                                                             %
15
%                              Software Design                                %
16
%                                   Cristy                                    %
17
%                                 July 1992                                   %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/script/license.php                               %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
*/
38

39
/*
40
  Include declarations.
41
*/
42
#include "MagickCore/studio.h"
43
#include "MagickCore/blob.h"
44
#include "MagickCore/blob-private.h"
45
#include "MagickCore/cache.h"
46
#include "MagickCore/colorspace.h"
47
#include "MagickCore/constitute.h"
48
#include "MagickCore/exception.h"
49
#include "MagickCore/exception-private.h"
50
#include "MagickCore/geometry.h"
51
#include "MagickCore/image.h"
52
#include "MagickCore/image-private.h"
53
#include "MagickCore/list.h"
54
#include "MagickCore/magick.h"
55
#include "MagickCore/memory_.h"
56
#include "MagickCore/monitor.h"
57
#include "MagickCore/monitor-private.h"
58
#include "MagickCore/pixel-accessor.h"
59
#include "MagickCore/resize.h"
60
#include "MagickCore/quantum-private.h"
61
#include "MagickCore/static.h"
62
#include "MagickCore/string_.h"
63
#include "MagickCore/module.h"
64
#include "MagickCore/utility.h"
65

66
/*
67
  Forward declarations.
68
*/
69
static MagickBooleanType
70
  WriteYUVImage(const ImageInfo *,Image *,ExceptionInfo *);
71

72
/*
73
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74
%                                                                             %
75
%                                                                             %
76
%                                                                             %
77
%   R e a d Y U V I m a g e                                                   %
78
%                                                                             %
79
%                                                                             %
80
%                                                                             %
81
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82
%
83
%  ReadYUVImage() reads an image with digital YUV (CCIR 601 4:1:1, plane
84
%  or partition interlaced, or 4:2:2 plane, partition interlaced or
85
%  noninterlaced) bytes and returns it.  It allocates the memory necessary
86
%  for the new Image structure and returns a pointer to the new image.
87
%
88
%  The format of the ReadYUVImage method is:
89
%
90
%      Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
91
%
92
%  A description of each parameter follows:
93
%
94
%    o image_info: the image info.
95
%
96
%    o exception: return any errors or warnings in this structure.
97
%
98
*/
99
static Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
100
2.29k
{
101
2.29k
  Image
102
2.29k
    *chroma_image,
103
2.29k
    *image,
104
2.29k
    *resize_image;
105
106
2.29k
  InterlaceType
107
2.29k
    interlace;
108
109
2.29k
  MagickBooleanType
110
2.29k
    status;
111
112
2.29k
  ssize_t
113
2.29k
    x;
114
115
2.29k
  Quantum
116
2.29k
    *q;
117
118
2.29k
  unsigned char
119
2.29k
    *p;
120
121
2.29k
  ssize_t
122
2.29k
    count,
123
2.29k
    horizontal_factor,
124
2.29k
    vertical_factor,
125
2.29k
    y;
126
127
2.29k
  size_t
128
2.29k
    length,
129
2.29k
    quantum;
130
131
2.29k
  unsigned char
132
2.29k
    *scanline;
133
134
  /*
135
    Allocate image structure.
136
  */
137
2.29k
  assert(image_info != (const ImageInfo *) NULL);
138
2.29k
  assert(image_info->signature == MagickCoreSignature);
139
2.29k
  assert(exception != (ExceptionInfo *) NULL);
140
2.29k
  assert(exception->signature == MagickCoreSignature);
141
2.29k
  if (IsEventLogging() != MagickFalse)
142
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
143
0
      image_info->filename);
144
2.29k
  image=AcquireImage(image_info,exception);
145
2.29k
  if ((image->columns == 0) || (image->rows == 0))
146
2.29k
    ThrowReaderException(OptionError,"MustSpecifyImageSize");
147
2
  status=SetImageExtent(image,image->columns,image->rows,exception);
148
2
  if (status == MagickFalse)
149
1
    return(DestroyImageList(image));
150
1
  quantum=(size_t) (image->depth <= 8 ? 1 : 2);
151
1
  interlace=image_info->interlace;
152
1
  horizontal_factor=2;
153
1
  vertical_factor=2;
154
1
  if (image_info->sampling_factor != (char *) NULL)
155
0
    {
156
0
      GeometryInfo
157
0
        geometry_info;
158
159
0
      MagickStatusType
160
0
        flags;
161
162
0
      flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
163
0
      if ((flags & RhoValue) != 0)
164
0
        horizontal_factor=(ssize_t) geometry_info.rho;
165
0
      vertical_factor=horizontal_factor;
166
0
      if ((flags & SigmaValue) != 0)
167
0
        vertical_factor=(ssize_t) geometry_info.sigma;
168
0
      if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
169
0
          (vertical_factor != 1) && (vertical_factor != 2))
170
0
        ThrowReaderException(CorruptImageError,"UnexpectedSamplingFactor");
171
0
    }
172
1
  if ((interlace == UndefinedInterlace) ||
173
1
      ((interlace == NoInterlace) && (vertical_factor == 2)))
174
1
    {
175
1
      interlace=NoInterlace;    /* CCIR 4:2:2 */
176
1
      if (vertical_factor == 2)
177
1
        interlace=PlaneInterlace; /* CCIR 4:1:1 */
178
1
    }
179
1
  if (interlace != PartitionInterlace)
180
1
    {
181
      /*
182
        Open image file.
183
      */
184
1
      status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
185
1
      if (status == MagickFalse)
186
1
        {
187
1
          image=DestroyImageList(image);
188
1
          return((Image *) NULL);
189
1
        }
190
0
      if (DiscardBlobBytes(image,(MagickSizeType) image->offset) == MagickFalse)
191
0
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
192
0
          image->filename);
193
0
    }
194
  /*
195
    Allocate memory for a scanline.
196
  */
197
0
  if (interlace == NoInterlace)
198
0
    scanline=(unsigned char *) AcquireQuantumMemory((size_t) (2UL*
199
0
      image->columns+2UL),(size_t) quantum*sizeof(*scanline));
200
0
  else
201
0
    scanline=(unsigned char *) AcquireQuantumMemory(image->columns,
202
0
      (size_t) quantum*sizeof(*scanline));
203
0
  if (scanline == (unsigned char *) NULL)
204
0
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
205
0
  status=MagickTrue;
206
0
  do
207
0
  {
208
0
    chroma_image=CloneImage(image,(size_t) ((ssize_t) image->columns+
209
0
      (ssize_t) horizontal_factor-1)/(size_t) horizontal_factor,(size_t)
210
0
      (image->rows+(size_t) vertical_factor-1)/
211
0
      (size_t) vertical_factor,MagickTrue,exception);
212
0
    if (chroma_image == (Image *) NULL)
213
0
      {
214
0
        scanline=(unsigned char *) RelinquishMagickMemory(scanline); 
215
0
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
216
0
      }
217
    /*
218
      Convert raster image to pixel packets.
219
    */
220
0
    if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
221
0
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
222
0
        break;
223
0
    status=SetImageExtent(image,image->columns,image->rows,exception);
224
0
    if (status == MagickFalse)
225
0
      break;
226
0
    if (interlace == PartitionInterlace)
227
0
      {
228
0
        AppendImageFormat("Y",image->filename);
229
0
        status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
230
0
        if (status == MagickFalse)
231
0
          {
232
0
            scanline=(unsigned char *) RelinquishMagickMemory(scanline); 
233
0
            image=DestroyImageList(image);
234
0
            return((Image *) NULL);
235
0
          }
236
0
      }
237
0
    for (y=0; y < (ssize_t) image->rows; y++)
238
0
    {
239
0
      Quantum
240
0
        *chroma_pixels;
241
242
0
      if (interlace == NoInterlace)
243
0
        {
244
0
          if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
245
0
            {
246
0
              length=2*quantum*image->columns;
247
0
              count=ReadBlob(image,length,scanline);
248
0
              if (count != (ssize_t) length)
249
0
                {
250
0
                  status=MagickFalse;
251
0
                  ThrowFileException(exception,CorruptImageError,
252
0
                    "UnexpectedEndOfFile",image->filename);
253
0
                  break;
254
0
                }
255
0
            }
256
0
          p=scanline;
257
0
          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
258
0
          if (q == (Quantum *) NULL)
259
0
            break;
260
0
          chroma_pixels=QueueAuthenticPixels(chroma_image,0,y,
261
0
            chroma_image->columns,1,exception);
262
0
          if (chroma_pixels == (Quantum *) NULL)
263
0
            break;
264
0
          for (x=0; x < (ssize_t) image->columns; x+=2)
265
0
          {
266
0
            SetPixelRed(chroma_image,0,chroma_pixels);
267
0
            if (quantum == 1)
268
0
              SetPixelGreen(chroma_image,ScaleCharToQuantum(*p++),
269
0
                chroma_pixels);
270
0
            else
271
0
              {
272
0
                SetPixelGreen(chroma_image,ScaleShortToQuantum(((*p) << 8) |
273
0
                  *(p+1)),chroma_pixels);
274
0
                p+=(ptrdiff_t) 2;
275
0
              }
276
0
            if (quantum == 1)
277
0
              SetPixelRed(image,ScaleCharToQuantum(*p++),q);
278
0
            else
279
0
              {
280
0
                SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
281
0
                p+=(ptrdiff_t) 2;
282
0
              }
283
0
            SetPixelGreen(image,0,q);
284
0
            SetPixelBlue(image,0,q);
285
0
            q+=(ptrdiff_t) GetPixelChannels(image);
286
0
            SetPixelGreen(image,0,q);
287
0
            SetPixelBlue(image,0,q);
288
0
            if (quantum == 1)
289
0
              SetPixelBlue(chroma_image,ScaleCharToQuantum(*p++),chroma_pixels);
290
0
            else
291
0
              {
292
0
                SetPixelBlue(chroma_image,ScaleShortToQuantum(((*p) << 8) |
293
0
                  *(p+1)),chroma_pixels);
294
0
                p+=(ptrdiff_t) 2;
295
0
              }
296
0
            if (quantum == 1)
297
0
              SetPixelRed(image,ScaleCharToQuantum(*p++),q);
298
0
            else
299
0
              {
300
0
                SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
301
0
                p+=(ptrdiff_t) 2;
302
0
              }
303
0
            chroma_pixels+=(ptrdiff_t) GetPixelChannels(chroma_image);
304
0
            q+=(ptrdiff_t) GetPixelChannels(image);
305
0
          }
306
0
        }
307
0
      else
308
0
        {
309
0
          if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
310
0
            {
311
0
              length=quantum*image->columns;
312
0
              count=ReadBlob(image,length,scanline);
313
0
              if (count != (ssize_t) length)
314
0
                {
315
0
                  status=MagickFalse;
316
0
                  ThrowFileException(exception,CorruptImageError,
317
0
                    "UnexpectedEndOfFile",image->filename);
318
0
                  break;
319
0
                }
320
0
            }
321
0
          p=scanline;
322
0
          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
323
0
          if (q == (Quantum *) NULL)
324
0
            break;
325
0
          for (x=0; x < (ssize_t) image->columns; x++)
326
0
          {
327
0
            if (quantum == 1)
328
0
              SetPixelRed(image,ScaleCharToQuantum(*p++),q);
329
0
            else
330
0
              {
331
0
                SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
332
0
                p+=(ptrdiff_t) 2;
333
0
              }
334
0
            SetPixelGreen(image,0,q);
335
0
            SetPixelBlue(image,0,q);
336
0
            q+=(ptrdiff_t) GetPixelChannels(image);
337
0
          }
338
0
        }
339
0
      if (SyncAuthenticPixels(image,exception) == MagickFalse)
340
0
        break;
341
0
      if (interlace == NoInterlace)
342
0
        if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
343
0
          break;
344
0
      if (image->previous == (Image *) NULL)
345
0
        {
346
0
          status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
347
0
            image->rows);
348
0
          if (status == MagickFalse)
349
0
            break;
350
0
        }
351
0
    }
352
0
    if (interlace == PartitionInterlace)
353
0
      {
354
0
        if (CloseBlob(image) == MagickFalse)
355
0
          break;
356
0
        AppendImageFormat("U",image->filename);
357
0
        status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
358
0
        if (status == MagickFalse)
359
0
          {
360
0
            scanline=(unsigned char *) RelinquishMagickMemory(scanline); 
361
0
            image=DestroyImageList(image);
362
0
            return((Image *) NULL);
363
0
          }
364
0
      }
365
0
    if (interlace != NoInterlace)
366
0
      {
367
0
        for (y=0; y < (ssize_t) chroma_image->rows; y++)
368
0
        {
369
0
          length=quantum*chroma_image->columns;
370
0
          count=ReadBlob(image,length,scanline);
371
0
          if (count != (ssize_t) length)
372
0
            {
373
0
              status=MagickFalse;
374
0
              ThrowFileException(exception,CorruptImageError,
375
0
                "UnexpectedEndOfFile",image->filename);
376
0
              break;
377
0
            }
378
0
          p=scanline;
379
0
          q=QueueAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
380
0
            exception);
381
0
          if (q == (Quantum *) NULL)
382
0
            break;
383
0
          for (x=0; x < (ssize_t) chroma_image->columns; x++)
384
0
          {
385
0
            SetPixelRed(chroma_image,0,q);
386
0
            if (quantum == 1)
387
0
              SetPixelGreen(chroma_image,ScaleCharToQuantum(*p++),q);
388
0
            else
389
0
              {
390
0
                SetPixelGreen(chroma_image,ScaleShortToQuantum(((*p) << 8) |
391
0
                  *(p+1)),q);
392
0
                p+=(ptrdiff_t) 2;
393
0
              }
394
0
            SetPixelBlue(chroma_image,0,q);
395
0
            q+=(ptrdiff_t) GetPixelChannels(chroma_image);
396
0
          }
397
0
          if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
398
0
            break;
399
0
        }
400
0
      if (interlace == PartitionInterlace)
401
0
        {
402
0
          if (CloseBlob(image) == MagickFalse)
403
0
            break;
404
0
          AppendImageFormat("V",image->filename);
405
0
          status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
406
0
          if (status == MagickFalse)
407
0
            {
408
0
              scanline=(unsigned char *) RelinquishMagickMemory(scanline); 
409
0
              image=DestroyImageList(image);
410
0
              return((Image *) NULL);
411
0
            }
412
0
        }
413
0
      for (y=0; y < (ssize_t) chroma_image->rows; y++)
414
0
      {
415
0
        length=quantum*chroma_image->columns;
416
0
        count=ReadBlob(image,length,scanline);
417
0
        if (count != (ssize_t) length)
418
0
          {
419
0
            status=MagickFalse;
420
0
            ThrowFileException(exception,CorruptImageError,
421
0
              "UnexpectedEndOfFile",image->filename);
422
0
            break;
423
0
          }
424
0
        p=scanline;
425
0
        q=GetAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
426
0
          exception);
427
0
        if (q == (Quantum *) NULL)
428
0
          break;
429
0
        for (x=0; x < (ssize_t) chroma_image->columns; x++)
430
0
        {
431
0
          if (quantum == 1)
432
0
            SetPixelBlue(chroma_image,ScaleCharToQuantum(*p++),q);
433
0
          else
434
0
            {
435
0
              SetPixelBlue(chroma_image,ScaleShortToQuantum(((*p) << 8) |
436
0
                *(p+1)),q);
437
0
              p+=(ptrdiff_t) 2;
438
0
            }
439
0
          q+=(ptrdiff_t) GetPixelChannels(chroma_image);
440
0
        }
441
0
        if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
442
0
          break;
443
0
      }
444
0
    }
445
    /*
446
      Scale image.
447
    */
448
0
    resize_image=ResizeImage(chroma_image,image->columns,image->rows,
449
0
      TriangleFilter,exception);
450
0
    chroma_image=DestroyImage(chroma_image);
451
0
    if (resize_image == (Image *) NULL)
452
0
      {
453
0
        scanline=(unsigned char *) RelinquishMagickMemory(scanline);
454
0
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
455
0
      }
456
0
    for (y=0; y < (ssize_t) image->rows; y++)
457
0
    {
458
0
      const Quantum
459
0
        *chroma_pixels;
460
461
0
      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
462
0
      chroma_pixels=GetVirtualPixels(resize_image,0,y,resize_image->columns,1,
463
0
        exception);
464
0
      if ((q == (Quantum *) NULL) ||
465
0
          (chroma_pixels == (const Quantum *) NULL))
466
0
        break;
467
0
      for (x=0; x < (ssize_t) image->columns; x++)
468
0
      {
469
0
        SetPixelGreen(image,GetPixelGreen(resize_image,chroma_pixels),q);
470
0
        SetPixelBlue(image,GetPixelBlue(resize_image,chroma_pixels),q);
471
0
        chroma_pixels+=(ptrdiff_t) GetPixelChannels(resize_image);
472
0
        q+=(ptrdiff_t) GetPixelChannels(image);
473
0
      }
474
0
      if (SyncAuthenticPixels(image,exception) == MagickFalse)
475
0
        break;
476
0
    }
477
0
    resize_image=DestroyImage(resize_image);
478
0
    if (SetImageColorspace(image,YCbCrColorspace,exception) == MagickFalse)
479
0
      break;
480
0
    if (interlace == PartitionInterlace)
481
0
      (void) CopyMagickString(image->filename,image_info->filename,
482
0
        MagickPathExtent);
483
0
    if (EOFBlob(image) != MagickFalse)
484
0
      {
485
0
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
486
0
          image->filename);
487
0
        break;
488
0
      }
489
    /*
490
      Proceed to next image.
491
    */
492
0
    if (image_info->number_scenes != 0)
493
0
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
494
0
        break;
495
0
    if (interlace == NoInterlace)
496
0
      count=ReadBlob(image,(size_t) (2*quantum*image->columns),scanline);
497
0
    else
498
0
      count=ReadBlob(image,(size_t) quantum*image->columns,scanline);
499
0
    if (count != 0)
500
0
      {
501
        /*
502
          Allocate next image structure.
503
        */
504
0
        AcquireNextImage(image_info,image,exception);
505
0
        if (GetNextImageInList(image) == (Image *) NULL)
506
0
          {
507
0
            status=MagickFalse;
508
0
            break;
509
0
          }
510
0
        image=SyncNextImageInList(image);
511
0
        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
512
0
          GetBlobSize(image));
513
0
        if (status == MagickFalse)
514
0
          break;
515
0
      }
516
0
  } while (count != 0);
517
0
  scanline=(unsigned char *) RelinquishMagickMemory(scanline);
518
0
  if (CloseBlob(image) == MagickFalse)
519
0
    status=MagickFalse;
520
0
  if (status == MagickFalse)
521
0
    return(DestroyImageList(image));
522
0
  return(GetFirstImageInList(image));
523
0
}
524

525
/*
526
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
527
%                                                                             %
528
%                                                                             %
529
%                                                                             %
530
%   R e g i s t e r Y U V I m a g e                                           %
531
%                                                                             %
532
%                                                                             %
533
%                                                                             %
534
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535
%
536
%  RegisterYUVImage() adds attributes for the YUV image format to
537
%  the list of supported formats.  The attributes include the image format
538
%  tag, a method to read and/or write the format, whether the format
539
%  supports the saving of more than one frame to the same file or blob,
540
%  whether the format supports native in-memory I/O, and a brief
541
%  description of the format.
542
%
543
%  The format of the RegisterYUVImage method is:
544
%
545
%      size_t RegisterYUVImage(void)
546
%
547
*/
548
ModuleExport size_t RegisterYUVImage(void)
549
9
{
550
9
  MagickInfo
551
9
    *entry;
552
553
9
  entry=AcquireMagickInfo("YUV","YUV","CCIR 601 4:1:1 or 4:2:2");
554
9
  entry->decoder=(DecodeImageHandler *) ReadYUVImage;
555
9
  entry->encoder=(EncodeImageHandler *) WriteYUVImage;
556
9
  entry->flags^=CoderAdjoinFlag;
557
9
  entry->flags|=CoderRawSupportFlag;
558
9
  (void) RegisterMagickInfo(entry);
559
9
  return(MagickImageCoderSignature);
560
9
}
561

562
/*
563
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
564
%                                                                             %
565
%                                                                             %
566
%                                                                             %
567
%   U n r e g i s t e r Y U V I m a g e                                       %
568
%                                                                             %
569
%                                                                             %
570
%                                                                             %
571
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572
%
573
%  UnregisterYUVImage() removes format registrations made by the
574
%  YUV module from the list of supported formats.
575
%
576
%  The format of the UnregisterYUVImage method is:
577
%
578
%      UnregisterYUVImage(void)
579
%
580
*/
581
ModuleExport void UnregisterYUVImage(void)
582
0
{
583
0
  (void) UnregisterMagickInfo("YUV");
584
0
}
585

586
/*
587
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
588
%                                                                             %
589
%                                                                             %
590
%                                                                             %
591
%   W r i t e Y U V I m a g e                                                 %
592
%                                                                             %
593
%                                                                             %
594
%                                                                             %
595
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
596
%
597
%  WriteYUVImage() writes an image to a file in the digital YUV
598
%  (CCIR 601 4:1:1, plane or partition interlaced, or 4:2:2 plane, partition
599
%  interlaced or noninterlaced) bytes and returns it.
600
%
601
%  The format of the WriteYUVImage method is:
602
%
603
%      MagickBooleanType WriteYUVImage(const ImageInfo *image_info,
604
%        Image *image,ExceptionInfo *exception)
605
%
606
%  A description of each parameter follows.
607
%
608
%    o image_info: the image info.
609
%
610
%    o image:  The image.
611
%
612
%    o exception: return any errors or warnings in this structure.
613
%
614
*/
615
static MagickBooleanType WriteYUVImage(const ImageInfo *image_info,Image *image,
616
  ExceptionInfo *exception)
617
0
{
618
0
  Image
619
0
    *chroma_image,
620
0
    *yuv_image;
621
622
0
  InterlaceType
623
0
    interlace;
624
625
0
  MagickBooleanType
626
0
    status;
627
628
0
  MagickOffsetType
629
0
    scene;
630
631
0
  const Quantum
632
0
    *p,
633
0
    *s;
634
635
0
  ssize_t
636
0
    x;
637
638
0
  size_t
639
0
    height,
640
0
    number_scenes,
641
0
    quantum,
642
0
    width;
643
644
0
  ssize_t
645
0
    horizontal_factor,
646
0
    vertical_factor,
647
0
    y;
648
649
0
  assert(image_info != (const ImageInfo *) NULL);
650
0
  assert(image_info->signature == MagickCoreSignature);
651
0
  assert(image != (Image *) NULL);
652
0
  assert(image->signature == MagickCoreSignature);
653
0
  if (IsEventLogging() != MagickFalse)
654
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
655
0
  quantum=(size_t) (image->depth <= 8 ? 1 : 2);
656
0
  interlace=image->interlace;
657
0
  horizontal_factor=2;
658
0
  vertical_factor=2;
659
0
  if (image_info->sampling_factor != (char *) NULL)
660
0
    {
661
0
      GeometryInfo
662
0
        geometry_info;
663
664
0
      MagickStatusType
665
0
        flags;
666
667
0
      flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
668
0
      if ((flags & RhoValue) != 0)
669
0
        horizontal_factor=(ssize_t) geometry_info.rho;
670
0
      vertical_factor=horizontal_factor;
671
0
      if ((flags & SigmaValue) != 0)
672
0
        vertical_factor=(ssize_t) geometry_info.sigma;
673
0
      if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
674
0
          (vertical_factor != 1) && (vertical_factor != 2))
675
0
        ThrowWriterException(CorruptImageError,"UnexpectedSamplingFactor");
676
0
    }
677
0
  if ((interlace == UndefinedInterlace) ||
678
0
      ((interlace == NoInterlace) && (vertical_factor == 2)))
679
0
    {
680
0
      interlace=NoInterlace;    /* CCIR 4:2:2 */
681
0
      if (vertical_factor == 2)
682
0
        interlace=PlaneInterlace; /* CCIR 4:1:1 */
683
0
    }
684
0
  if (interlace != PartitionInterlace)
685
0
    {
686
      /*
687
        Open output image file.
688
      */
689
0
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
690
0
      if (status == MagickFalse)
691
0
        return(status);
692
0
    }
693
0
  else
694
0
    {
695
0
      AppendImageFormat("Y",image->filename);
696
0
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
697
0
      if (status == MagickFalse)
698
0
        return(status);
699
0
    }
700
0
  scene=0;
701
0
  number_scenes=GetImageListLength(image);
702
0
  do
703
0
  {
704
    /*
705
      Sample image to an even width and height, if necessary.
706
    */
707
0
    image->depth=(size_t) (quantum == 1 ? 8 : 16);
708
0
    width=image->columns+(image->columns & (unsigned int) (horizontal_factor
709
0
      -1));
710
0
    height=image->rows+(image->rows & (unsigned int) (vertical_factor-1));
711
0
    yuv_image=ResizeImage(image,width,height,TriangleFilter,exception);
712
0
    if (yuv_image == (Image *) NULL)
713
0
      {
714
0
        (void) CloseBlob(image);
715
0
        return(MagickFalse);
716
0
      }
717
0
    (void) TransformImageColorspace(yuv_image,YCbCrColorspace,exception);
718
    /*
719
      Downsample image.
720
    */
721
0
    chroma_image=ResizeImage(image,(size_t) ((ssize_t) width/horizontal_factor),
722
0
      (size_t) ((ssize_t) height/vertical_factor),TriangleFilter,exception);
723
0
    if (chroma_image == (Image *) NULL)
724
0
      {
725
0
        (void) CloseBlob(image);
726
0
        return(MagickFalse);
727
0
      }
728
0
    (void) TransformImageColorspace(chroma_image,YCbCrColorspace,exception);
729
0
    if (interlace == NoInterlace)
730
0
      {
731
        /*
732
          Write noninterlaced YUV.
733
        */
734
0
        for (y=0; y < (ssize_t) yuv_image->rows; y++)
735
0
        {
736
0
          p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,exception);
737
0
          if (p == (const Quantum *) NULL)
738
0
            break;
739
0
          s=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
740
0
            exception);
741
0
          if (s == (const Quantum *) NULL)
742
0
            break;
743
0
          for (x=0; x < (ssize_t) yuv_image->columns; x+=2)
744
0
          {
745
0
            if (quantum == 1)
746
0
              {
747
0
                (void) WriteBlobByte(image,ScaleQuantumToChar(
748
0
                  GetPixelGreen(yuv_image,s)));
749
0
                (void) WriteBlobByte(image,ScaleQuantumToChar(
750
0
                  GetPixelRed(yuv_image,p)));
751
0
                p+=(ptrdiff_t) GetPixelChannels(yuv_image);
752
0
                (void) WriteBlobByte(image,ScaleQuantumToChar(
753
0
                  GetPixelBlue(yuv_image,s)));
754
0
                (void) WriteBlobByte(image,ScaleQuantumToChar(
755
0
                  GetPixelRed(yuv_image,p)));
756
0
              }
757
0
            else
758
0
              {
759
0
                (void) WriteBlobByte(image,ScaleQuantumToChar(
760
0
                  GetPixelGreen(yuv_image,s)));
761
0
                (void) WriteBlobShort(image,ScaleQuantumToShort(
762
0
                  GetPixelRed(yuv_image,p)));
763
0
                p+=(ptrdiff_t) GetPixelChannels(yuv_image);
764
0
                (void) WriteBlobByte(image,ScaleQuantumToChar(
765
0
                  GetPixelBlue(yuv_image,s)));
766
0
                (void) WriteBlobShort(image,ScaleQuantumToShort(
767
0
                  GetPixelRed(yuv_image,p)));
768
0
              }
769
0
            p+=(ptrdiff_t) GetPixelChannels(yuv_image);
770
0
            s++;
771
0
          }
772
0
          if (image->previous == (Image *) NULL)
773
0
            {
774
0
              status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
775
0
                image->rows);
776
0
              if (status == MagickFalse)
777
0
                break;
778
0
            }
779
0
        }
780
0
        yuv_image=DestroyImage(yuv_image);
781
0
      }
782
0
    else
783
0
      {
784
        /*
785
          Initialize Y channel.
786
        */
787
0
        for (y=0; y < (ssize_t) yuv_image->rows; y++)
788
0
        {
789
0
          p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,exception);
790
0
          if (p == (const Quantum *) NULL)
791
0
            break;
792
0
          for (x=0; x < (ssize_t) yuv_image->columns; x++)
793
0
          {
794
0
            if (quantum == 1)
795
0
              (void) WriteBlobByte(image,ScaleQuantumToChar(
796
0
                GetPixelRed(yuv_image,p)));
797
0
            else
798
0
              (void) WriteBlobShort(image,ScaleQuantumToShort(
799
0
                GetPixelRed(yuv_image,p)));
800
0
            p+=(ptrdiff_t) GetPixelChannels(yuv_image);
801
0
          }
802
0
          if (image->previous == (Image *) NULL)
803
0
            {
804
0
              status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
805
0
                image->rows);
806
0
              if (status == MagickFalse)
807
0
                break;
808
0
            }
809
0
        }
810
0
        yuv_image=DestroyImage(yuv_image);
811
0
        if (image->previous == (Image *) NULL)
812
0
          {
813
0
            status=SetImageProgress(image,SaveImageTag,1,3);
814
0
            if (status == MagickFalse)
815
0
              break;
816
0
          }
817
        /*
818
          Initialize U channel.
819
        */
820
0
        if (interlace == PartitionInterlace)
821
0
          {
822
0
            if (CloseBlob(image) == MagickFalse)
823
0
              break;
824
0
            AppendImageFormat("U",image->filename);
825
0
            status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
826
0
            if (status == MagickFalse)
827
0
              return(status);
828
0
          }
829
0
        for (y=0; y < (ssize_t) chroma_image->rows; y++)
830
0
        {
831
0
          p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
832
0
            exception);
833
0
          if (p == (const Quantum *) NULL)
834
0
            break;
835
0
          for (x=0; x < (ssize_t) chroma_image->columns; x++)
836
0
          {
837
0
            if (quantum == 1)
838
0
              (void) WriteBlobByte(image,ScaleQuantumToChar(
839
0
                GetPixelGreen(chroma_image,p)));
840
0
            else
841
0
              (void) WriteBlobShort(image,ScaleQuantumToShort(
842
0
                GetPixelGreen(chroma_image,p)));
843
0
            p+=(ptrdiff_t) GetPixelChannels(chroma_image);
844
0
          }
845
0
        }
846
0
        if (image->previous == (Image *) NULL)
847
0
          {
848
0
            status=SetImageProgress(image,SaveImageTag,2,3);
849
0
            if (status == MagickFalse)
850
0
              break;
851
0
          }
852
        /*
853
          Initialize V channel.
854
        */
855
0
        if (interlace == PartitionInterlace)
856
0
          {
857
0
            if (CloseBlob(image) == MagickFalse)
858
0
              break;
859
0
            AppendImageFormat("V",image->filename);
860
0
            status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
861
0
            if (status == MagickFalse)
862
0
              return(status);
863
0
          }
864
0
        for (y=0; y < (ssize_t) chroma_image->rows; y++)
865
0
        {
866
0
          p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
867
0
            exception);
868
0
          if (p == (const Quantum *) NULL)
869
0
            break;
870
0
          for (x=0; x < (ssize_t) chroma_image->columns; x++)
871
0
          {
872
0
            if (quantum == 1)
873
0
              (void) WriteBlobByte(image,ScaleQuantumToChar(
874
0
                GetPixelBlue(chroma_image,p)));
875
0
            else
876
0
              (void) WriteBlobShort(image,ScaleQuantumToShort(
877
0
                GetPixelBlue(chroma_image,p)));
878
0
            p+=(ptrdiff_t) GetPixelChannels(chroma_image);
879
0
          }
880
0
        }
881
0
        if (image->previous == (Image *) NULL)
882
0
          {
883
0
            status=SetImageProgress(image,SaveImageTag,2,3);
884
0
            if (status == MagickFalse)
885
0
              break;
886
0
          }
887
0
      }
888
0
    chroma_image=DestroyImage(chroma_image);
889
0
    if (interlace == PartitionInterlace)
890
0
      (void) CopyMagickString(image->filename,image_info->filename,
891
0
        MagickPathExtent);
892
0
    if (GetNextImageInList(image) == (Image *) NULL)
893
0
      break;
894
0
    image=SyncNextImageInList(image);
895
0
    status=SetImageProgress(image,SaveImagesTag,scene++,number_scenes);
896
0
    if (status == MagickFalse)
897
0
      break;
898
0
  } while (image_info->adjoin != MagickFalse);
899
0
  if (CloseBlob(image) == MagickFalse)
900
0
    status=MagickFalse;
901
0
  return(status);
902
0
}