2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Raw CCIR 601 4:1:1 or 4:2:2 Image Format %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "magick/studio.h"
43 #include "magick/blob.h"
44 #include "magick/blob-private.h"
45 #include "magick/cache.h"
46 #include "magick/colorspace.h"
47 #include "magick/constitute.h"
48 #include "magick/exception.h"
49 #include "magick/exception-private.h"
50 #include "magick/geometry.h"
51 #include "magick/image.h"
52 #include "magick/image-private.h"
53 #include "magick/list.h"
54 #include "magick/magick.h"
55 #include "magick/memory_.h"
56 #include "magick/monitor.h"
57 #include "magick/monitor-private.h"
58 #include "magick/resize.h"
59 #include "magick/quantum-private.h"
60 #include "magick/static.h"
61 #include "magick/string_.h"
62 #include "magick/module.h"
63 #include "magick/utility.h"
68 static MagickBooleanType
69 WriteYUVImage(const ImageInfo *,Image *);
72 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76 % R e a d Y U V I m a g e %
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 % ReadYUVImage() reads an image with digital YUV (CCIR 601 4:1:1, plane
83 % or partition interlaced, or 4:2:2 plane, partition interlaced or
84 % noninterlaced) bytes and returns it. It allocates the memory necessary
85 % for the new Image structure and returns a pointer to the new image.
87 % The format of the ReadYUVImage method is:
89 % Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
91 % A description of each parameter follows:
93 % o image_info: the image info.
95 % o exception: return any errors or warnings in this structure.
98 static Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
116 register const PixelPacket
125 register unsigned char
138 Allocate image structure.
140 assert(image_info != (const ImageInfo *) NULL);
141 assert(image_info->signature == MagickSignature);
142 if (image_info->debug != MagickFalse)
143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
144 image_info->filename);
145 assert(exception != (ExceptionInfo *) NULL);
146 assert(exception->signature == MagickSignature);
147 image=AcquireImage(image_info);
148 if ((image->columns == 0) || (image->rows == 0))
149 ThrowReaderException(OptionError,"MustSpecifyImageSize");
150 quantum=image->depth <= 8 ? 1 : 2;
151 interlace=image_info->interlace;
154 if (image_info->sampling_factor != (char *) NULL)
162 flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
163 horizontal_factor=(ssize_t) geometry_info.rho;
164 vertical_factor=(ssize_t) geometry_info.sigma;
165 if ((flags & SigmaValue) == 0)
166 vertical_factor=horizontal_factor;
167 if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
168 (vertical_factor != 1) && (vertical_factor != 2))
169 ThrowReaderException(CorruptImageError,"UnexpectedSamplingFactor");
171 if ((interlace == UndefinedInterlace) ||
172 ((interlace == NoInterlace) && (vertical_factor == 2)))
174 interlace=NoInterlace; /* CCIR 4:2:2 */
175 if (vertical_factor == 2)
176 interlace=PlaneInterlace; /* CCIR 4:1:1 */
178 if (interlace != PartitionInterlace)
183 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
184 if (status == MagickFalse)
186 image=DestroyImageList(image);
187 return((Image *) NULL);
189 if (DiscardBlobBytes(image,image->offset) == MagickFalse)
190 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
194 Allocate memory for a scanline.
196 if (interlace == NoInterlace)
197 scanline=(unsigned char *) AcquireQuantumMemory((size_t) 2UL*
198 image->columns+2UL,quantum*sizeof(*scanline));
200 scanline=(unsigned char *) AcquireQuantumMemory((size_t) image->columns,
201 quantum*sizeof(*scanline));
202 if (scanline == (unsigned char *) NULL)
203 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
206 chroma_image=CloneImage(image,image->columns/horizontal_factor,
207 image->rows/vertical_factor,MagickTrue,exception);
208 if (chroma_image == (Image *) NULL)
209 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
211 Convert raster image to pixel packets.
213 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
214 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
216 if (interlace == PartitionInterlace)
218 AppendImageFormat("Y",image->filename);
219 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
220 if (status == MagickFalse)
222 image=DestroyImageList(image);
223 return((Image *) NULL);
226 for (y=0; y < (ssize_t) image->rows; y++)
231 if (interlace == NoInterlace)
233 if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
234 count=ReadBlob(image,(size_t) (2*quantum*image->columns),scanline);
236 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
237 if (q == (PixelPacket *) NULL)
239 chroma_pixels=QueueAuthenticPixels(chroma_image,0,y,
240 chroma_image->columns,1,exception);
241 if (chroma_pixels == (PixelPacket *) NULL)
243 for (x=0; x < (ssize_t) image->columns; x+=2)
245 chroma_pixels->red=(Quantum) 0;
247 chroma_pixels->green=ScaleCharToQuantum(*p++);
250 chroma_pixels->green=ScaleShortToQuantum(((*p) << 8) | *(p+1));
254 q->red=ScaleCharToQuantum(*p++);
257 q->red=ScaleShortToQuantum(((*p) << 8) | *(p+1));
260 q->green=(Quantum) 0;
266 chroma_pixels->blue=ScaleCharToQuantum(*p++);
269 chroma_pixels->blue=ScaleShortToQuantum(((*p) << 8) | *(p+1));
273 q->red=ScaleCharToQuantum(*p++);
276 q->red=ScaleShortToQuantum(((*p) << 8) | *(p+1));
285 if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
286 count=ReadBlob(image,(size_t) quantum*image->columns,scanline);
288 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
289 if (q == (PixelPacket *) NULL)
291 for (x=0; x < (ssize_t) image->columns; x++)
294 q->red=ScaleCharToQuantum(*p++);
297 q->red=ScaleShortToQuantum(((*p) << 8) | *(p+1));
305 if (SyncAuthenticPixels(image,exception) == MagickFalse)
307 if (interlace == NoInterlace)
308 if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
310 if (image->previous == (Image *) NULL)
312 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
314 if (status == MagickFalse)
318 if (interlace == PartitionInterlace)
320 (void) CloseBlob(image);
321 AppendImageFormat("U",image->filename);
322 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
323 if (status == MagickFalse)
325 image=DestroyImageList(image);
326 return((Image *) NULL);
329 if (interlace != NoInterlace)
331 for (y=0; y < (ssize_t) chroma_image->rows; y++)
333 count=ReadBlob(image,(size_t) quantum*chroma_image->columns,scanline);
335 q=QueueAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
337 if (q == (PixelPacket *) NULL)
339 for (x=0; x < (ssize_t) chroma_image->columns; x++)
343 q->green=ScaleCharToQuantum(*p++);
346 q->green=ScaleShortToQuantum(((*p) << 8) | *(p+1));
352 if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
355 if (interlace == PartitionInterlace)
357 (void) CloseBlob(image);
358 AppendImageFormat("V",image->filename);
359 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
360 if (status == MagickFalse)
362 image=DestroyImageList(image);
363 return((Image *) NULL);
366 for (y=0; y < (ssize_t) chroma_image->rows; y++)
368 count=ReadBlob(image,(size_t) quantum*chroma_image->columns,scanline);
370 q=GetAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
372 if (q == (PixelPacket *) NULL)
374 for (x=0; x < (ssize_t) chroma_image->columns; x++)
377 q->blue=ScaleCharToQuantum(*p++);
380 q->blue=ScaleShortToQuantum(((*p) << 8) | *(p+1));
385 if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
392 resize_image=ResizeImage(chroma_image,image->columns,image->rows,
393 TriangleFilter,1.0,exception);
394 chroma_image=DestroyImage(chroma_image);
395 if (resize_image == (Image *) NULL)
396 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
397 for (y=0; y < (ssize_t) image->rows; y++)
399 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
400 chroma_pixels=GetVirtualPixels(resize_image,0,y,resize_image->columns,1,
401 &resize_image->exception);
402 if ((q == (PixelPacket *) NULL) ||
403 (chroma_pixels == (const PixelPacket *) NULL))
405 for (x=0; x < (ssize_t) image->columns; x++)
407 q->green=chroma_pixels->green;
408 q->blue=chroma_pixels->blue;
412 if (SyncAuthenticPixels(image,exception) == MagickFalse)
415 resize_image=DestroyImage(resize_image);
416 image->colorspace=YCbCrColorspace;
417 if (interlace == PartitionInterlace)
418 (void) CopyMagickString(image->filename,image_info->filename,
420 if (EOFBlob(image) != MagickFalse)
422 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
427 Proceed to next image.
429 if (image_info->number_scenes != 0)
430 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
432 if (interlace == NoInterlace)
433 count=ReadBlob(image,(size_t) (2*quantum*image->columns),scanline);
435 count=ReadBlob(image,(size_t) quantum*image->columns,scanline);
439 Allocate next image structure.
441 AcquireNextImage(image_info,image);
442 if (GetNextImageInList(image) == (Image *) NULL)
444 image=DestroyImageList(image);
445 return((Image *) NULL);
447 image=SyncNextImageInList(image);
448 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
450 if (status == MagickFalse)
453 } while (count != 0);
454 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
455 (void) CloseBlob(image);
456 return(GetFirstImageInList(image));
460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464 % R e g i s t e r Y U V I m a g e %
468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470 % RegisterYUVImage() adds attributes for the YUV image format to
471 % the list of supported formats. The attributes include the image format
472 % tag, a method to read and/or write the format, whether the format
473 % supports the saving of more than one frame to the same file or blob,
474 % whether the format supports native in-memory I/O, and a brief
475 % description of the format.
477 % The format of the RegisterYUVImage method is:
479 % size_t RegisterYUVImage(void)
482 ModuleExport size_t RegisterYUVImage(void)
487 entry=SetMagickInfo("YUV");
488 entry->decoder=(DecodeImageHandler *) ReadYUVImage;
489 entry->encoder=(EncodeImageHandler *) WriteYUVImage;
490 entry->adjoin=MagickFalse;
491 entry->raw=MagickTrue;
492 entry->description=ConstantString("CCIR 601 4:1:1 or 4:2:2");
493 entry->module=ConstantString("YUV");
494 (void) RegisterMagickInfo(entry);
495 return(MagickImageCoderSignature);
499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
503 % U n r e g i s t e r Y U V I m a g e %
507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509 % UnregisterYUVImage() removes format registrations made by the
510 % YUV module from the list of supported formats.
512 % The format of the UnregisterYUVImage method is:
514 % UnregisterYUVImage(void)
517 ModuleExport void UnregisterYUVImage(void)
519 (void) UnregisterMagickInfo("YUV");
523 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
527 % W r i t e Y U V I m a g e %
531 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
533 % WriteYUVImage() writes an image to a file in the digital YUV
534 % (CCIR 601 4:1:1, plane or partition interlaced, or 4:2:2 plane, partition
535 % interlaced or noninterlaced) bytes and returns it.
537 % The format of the WriteYUVImage method is:
539 % MagickBooleanType WriteYUVImage(const ImageInfo *image_info,Image *image)
541 % A description of each parameter follows.
543 % o image_info: the image info.
545 % o image: The image.
548 static MagickBooleanType WriteYUVImage(const ImageInfo *image_info,Image *image)
568 register const PixelPacket
580 assert(image_info != (const ImageInfo *) NULL);
581 assert(image_info->signature == MagickSignature);
582 assert(image != (Image *) NULL);
583 assert(image->signature == MagickSignature);
584 if (image->debug != MagickFalse)
585 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
586 quantum=image->depth <= 8 ? 1 : 2;
587 interlace=image->interlace;
590 if (image_info->sampling_factor != (char *) NULL)
598 flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
599 horizontal_factor=(ssize_t) geometry_info.rho;
600 vertical_factor=(ssize_t) geometry_info.sigma;
601 if ((flags & SigmaValue) == 0)
602 vertical_factor=horizontal_factor;
603 if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
604 (vertical_factor != 1) && (vertical_factor != 2))
605 ThrowWriterException(CorruptImageError,"UnexpectedSamplingFactor");
607 if ((interlace == UndefinedInterlace) ||
608 ((interlace == NoInterlace) && (vertical_factor == 2)))
610 interlace=NoInterlace; /* CCIR 4:2:2 */
611 if (vertical_factor == 2)
612 interlace=PlaneInterlace; /* CCIR 4:1:1 */
614 if (interlace != PartitionInterlace)
617 Open output image file.
619 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
620 if (status == MagickFalse)
625 AppendImageFormat("Y",image->filename);
626 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
627 if (status == MagickFalse)
634 Sample image to an even width and height, if necessary.
636 image->depth=quantum == 1 ? 8 : 16;
637 width=image->columns+(image->columns & (horizontal_factor-1));
638 height=image->rows+(image->rows & (vertical_factor-1));
639 yuv_image=ResizeImage(image,width,height,TriangleFilter,1.0,
641 if (yuv_image == (Image *) NULL)
642 ThrowWriterException(ResourceLimitError,image->exception.reason);
643 (void) TransformImageColorspace(yuv_image,YCbCrColorspace);
647 chroma_image=ResizeImage(image,width/horizontal_factor,
648 height/vertical_factor,TriangleFilter,1.0,&image->exception);
649 if (chroma_image == (Image *) NULL)
650 ThrowWriterException(ResourceLimitError,image->exception.reason);
651 (void) TransformImageColorspace(chroma_image,YCbCrColorspace);
652 if (interlace == NoInterlace)
655 Write noninterlaced YUV.
657 for (y=0; y < (ssize_t) yuv_image->rows; y++)
659 p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,
660 &yuv_image->exception);
661 if (p == (const PixelPacket *) NULL)
663 s=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
664 &chroma_image->exception);
665 if (s == (const PixelPacket *) NULL)
667 for (x=0; x < (ssize_t) yuv_image->columns; x++)
671 (void) WriteBlobByte(image,ScaleQuantumToChar(s->green));
672 (void) WriteBlobByte(image,ScaleQuantumToChar(
673 GetRedPixelComponent(p)));
675 (void) WriteBlobByte(image,ScaleQuantumToChar(s->blue));
676 (void) WriteBlobByte(image,ScaleQuantumToChar(
677 GetRedPixelComponent(p)));
681 (void) WriteBlobShort(image,ScaleQuantumToShort(s->green));
682 (void) WriteBlobShort(image,ScaleQuantumToShort(
683 GetRedPixelComponent(p)));
685 (void) WriteBlobShort(image,ScaleQuantumToShort(s->blue));
686 (void) WriteBlobShort(image,ScaleQuantumToShort(
687 GetRedPixelComponent(p)));
693 if (image->previous == (Image *) NULL)
695 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
697 if (status == MagickFalse)
701 yuv_image=DestroyImage(yuv_image);
706 Initialize Y channel.
708 for (y=0; y < (ssize_t) yuv_image->rows; y++)
710 p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,
711 &yuv_image->exception);
712 if (p == (const PixelPacket *) NULL)
714 for (x=0; x < (ssize_t) yuv_image->columns; x++)
717 (void) WriteBlobByte(image,ScaleQuantumToChar(
718 GetRedPixelComponent(p)));
720 (void) WriteBlobShort(image,ScaleQuantumToShort(
721 GetRedPixelComponent(p)));
724 if (image->previous == (Image *) NULL)
726 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
728 if (status == MagickFalse)
732 yuv_image=DestroyImage(yuv_image);
733 if (image->previous == (Image *) NULL)
735 status=SetImageProgress(image,SaveImageTag,1,3);
736 if (status == MagickFalse)
740 Initialize U channel.
742 if (interlace == PartitionInterlace)
744 (void) CloseBlob(image);
745 AppendImageFormat("U",image->filename);
746 status=OpenBlob(image_info,image,WriteBinaryBlobMode,
748 if (status == MagickFalse)
751 for (y=0; y < (ssize_t) chroma_image->rows; y++)
753 p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
754 &chroma_image->exception);
755 if (p == (const PixelPacket *) NULL)
757 for (x=0; x < (ssize_t) chroma_image->columns; x++)
760 (void) WriteBlobByte(image,ScaleQuantumToChar(
761 GetGreenPixelComponent(p)));
763 (void) WriteBlobShort(image,ScaleQuantumToShort(
764 GetGreenPixelComponent(p)));
768 if (image->previous == (Image *) NULL)
770 status=SetImageProgress(image,SaveImageTag,2,3);
771 if (status == MagickFalse)
775 Initialize V channel.
777 if (interlace == PartitionInterlace)
779 (void) CloseBlob(image);
780 AppendImageFormat("V",image->filename);
781 status=OpenBlob(image_info,image,WriteBinaryBlobMode,
783 if (status == MagickFalse)
786 for (y=0; y < (ssize_t) chroma_image->rows; y++)
788 p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
789 &chroma_image->exception);
790 if (p == (const PixelPacket *) NULL)
792 for (x=0; x < (ssize_t) chroma_image->columns; x++)
795 (void) WriteBlobByte(image,ScaleQuantumToChar(
796 GetBluePixelComponent(p)));
798 (void) WriteBlobShort(image,ScaleQuantumToShort(
799 GetBluePixelComponent(p)));
803 if (image->previous == (Image *) NULL)
805 status=SetImageProgress(image,SaveImageTag,2,3);
806 if (status == MagickFalse)
810 chroma_image=DestroyImage(chroma_image);
811 if (interlace == PartitionInterlace)
812 (void) CopyMagickString(image->filename,image_info->filename,
814 if (GetNextImageInList(image) == (Image *) NULL)
816 image=SyncNextImageInList(image);
817 status=SetImageProgress(image,SaveImagesTag,scene++,
818 GetImageListLength(image));
819 if (status == MagickFalse)
821 } while (image_info->adjoin != MagickFalse);
822 (void) CloseBlob(image);