2 Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /** @file src/image.cc
21 * @brief A class to describe a video image.
25 #include "exceptions.h"
29 #include "md5_digester.h"
30 #include "dcpomatic_socket.h"
32 #include <libswscale/swscale.h>
33 #include <libavutil/pixfmt.h>
34 #include <libavutil/pixdesc.h>
45 using boost::shared_ptr;
49 Image::line_factor (int n) const
55 AVPixFmtDescriptor const * d = av_pix_fmt_desc_get(_pixel_format);
57 throw PixelFormatError ("line_factor()", _pixel_format);
60 return pow (2.0f, d->log2_chroma_h);
63 /** @param n Component index.
64 * @return Number of lines in the image for the given component.
67 Image::lines (int n) const
69 return rint (ceil (static_cast<double>(size().height) / line_factor (n)));
72 /** @return Number of components */
74 Image::components () const
76 AVPixFmtDescriptor const * d = av_pix_fmt_desc_get(_pixel_format);
78 throw PixelFormatError ("components()", _pixel_format);
81 if ((d->flags & PIX_FMT_PLANAR) == 0) {
85 return d->nb_components;
88 /** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size' */
90 Image::crop_scale_window (
91 Crop crop, dcp::Size inter_size, dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned
94 /* Empirical testing suggests that sws_scale() will crash if
95 the input image is not aligned.
97 DCPOMATIC_ASSERT (aligned ());
99 DCPOMATIC_ASSERT (out_size.width >= inter_size.width);
100 DCPOMATIC_ASSERT (out_size.height >= inter_size.height);
102 /* Here's an image of out_size */
103 shared_ptr<Image> out (new Image (out_format, out_size, out_aligned));
106 /* Size of the image after any crop */
107 dcp::Size const cropped_size = crop.apply (size ());
109 /* Scale context for a scale from cropped_size to inter_size */
110 struct SwsContext* scale_context = sws_getContext (
111 cropped_size.width, cropped_size.height, pixel_format(),
112 inter_size.width, inter_size.height, out_format,
116 if (!scale_context) {
117 throw StringError (N_("Could not allocate SwsContext"));
120 DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUV_TO_RGB_COUNT);
121 int const lut[dcp::YUV_TO_RGB_COUNT] = {
126 sws_setColorspaceDetails (
128 sws_getCoefficients (lut[yuv_to_rgb]), 0,
129 sws_getCoefficients (lut[yuv_to_rgb]), 0,
133 AVPixFmtDescriptor const * desc = av_pix_fmt_desc_get (_pixel_format);
135 throw PixelFormatError ("crop_scale_window()", _pixel_format);
138 /* Prepare input data pointers with crop */
139 uint8_t* scale_in_data[components()];
140 for (int c = 0; c < components(); ++c) {
141 /* To work out the crop in bytes, start by multiplying
142 the crop by the (average) bytes per pixel. Then
143 round down so that we don't crop a subsampled pixel until
144 we've cropped all of its Y-channel pixels.
146 int const x = int (rint (bytes_per_pixel(c) * crop.left)) & ~ ((int) desc->log2_chroma_w);
147 scale_in_data[c] = data()[c] + x + stride()[c] * (crop.top / line_factor(c));
150 /* Corner of the image within out_size */
151 Position<int> const corner ((out_size.width - inter_size.width) / 2, (out_size.height - inter_size.height) / 2);
153 uint8_t* scale_out_data[out->components()];
154 for (int c = 0; c < out->components(); ++c) {
155 scale_out_data[c] = out->data()[c] + int (rint (out->bytes_per_pixel(c) * corner.x)) + out->stride()[c] * corner.y;
160 scale_in_data, stride(),
161 0, cropped_size.height,
162 scale_out_data, out->stride()
165 sws_freeContext (scale_context);
171 Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned) const
173 /* Empirical testing suggests that sws_scale() will crash if
174 the input image is not aligned.
176 DCPOMATIC_ASSERT (aligned ());
178 shared_ptr<Image> scaled (new Image (out_format, out_size, out_aligned));
180 struct SwsContext* scale_context = sws_getContext (
181 size().width, size().height, pixel_format(),
182 out_size.width, out_size.height, out_format,
186 DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUV_TO_RGB_COUNT);
187 int const lut[dcp::YUV_TO_RGB_COUNT] = {
192 sws_setColorspaceDetails (
194 sws_getCoefficients (lut[yuv_to_rgb]), 0,
195 sws_getCoefficients (lut[yuv_to_rgb]), 0,
203 scaled->data(), scaled->stride()
206 sws_freeContext (scale_context);
212 Image::crop (Crop crop, bool aligned) const
214 dcp::Size cropped_size = crop.apply (size ());
215 shared_ptr<Image> out (new Image (pixel_format(), cropped_size, aligned));
217 for (int c = 0; c < components(); ++c) {
218 int const crop_left_in_bytes = bytes_per_pixel(c) * crop.left;
219 /* bytes_per_pixel() could be a fraction; in this case the stride will be rounded
220 up, and we need to make sure that we copy over the width (up to the stride)
221 rather than short of the width; hence the ceil() here.
223 int const cropped_width_in_bytes = ceil (bytes_per_pixel(c) * cropped_size.width);
225 /* Start of the source line, cropped from the top but not the left */
226 uint8_t* in_p = data()[c] + (crop.top / out->line_factor(c)) * stride()[c];
227 uint8_t* out_p = out->data()[c];
229 for (int y = 0; y < out->lines(c); ++y) {
230 memcpy (out_p, in_p + crop_left_in_bytes, cropped_width_in_bytes);
232 out_p += out->stride()[c];
239 /** Blacken a YUV image whose bits per pixel is rounded up to 16 */
241 Image::yuv_16_black (uint16_t v, bool alpha)
243 memset (data()[0], 0, lines(0) * stride()[0]);
244 for (int i = 1; i < 3; ++i) {
245 int16_t* p = reinterpret_cast<int16_t*> (data()[i]);
246 for (int y = 0; y < lines(i); ++y) {
247 /* We divide by 2 here because we are writing 2 bytes at a time */
248 for (int x = 0; x < line_size()[i] / 2; ++x) {
251 p += stride()[i] / 2;
256 memset (data()[3], 0, lines(3) * stride()[3]);
261 Image::swap_16 (uint16_t v)
263 return ((v >> 8) & 0xff) | ((v & 0xff) << 8);
269 /* U/V black value for 8-bit colour */
270 static uint8_t const eight_bit_uv = (1 << 7) - 1;
271 /* U/V black value for 9-bit colour */
272 static uint16_t const nine_bit_uv = (1 << 8) - 1;
273 /* U/V black value for 10-bit colour */
274 static uint16_t const ten_bit_uv = (1 << 9) - 1;
275 /* U/V black value for 16-bit colour */
276 static uint16_t const sixteen_bit_uv = (1 << 15) - 1;
278 switch (_pixel_format) {
279 case PIX_FMT_YUV420P:
280 case PIX_FMT_YUV422P:
281 case PIX_FMT_YUV444P:
282 case PIX_FMT_YUV411P:
283 memset (data()[0], 0, lines(0) * stride()[0]);
284 memset (data()[1], eight_bit_uv, lines(1) * stride()[1]);
285 memset (data()[2], eight_bit_uv, lines(2) * stride()[2]);
288 case PIX_FMT_YUVJ420P:
289 case PIX_FMT_YUVJ422P:
290 case PIX_FMT_YUVJ444P:
291 memset (data()[0], 0, lines(0) * stride()[0]);
292 memset (data()[1], eight_bit_uv + 1, lines(1) * stride()[1]);
293 memset (data()[2], eight_bit_uv + 1, lines(2) * stride()[2]);
296 case PIX_FMT_YUV422P9LE:
297 case PIX_FMT_YUV444P9LE:
298 yuv_16_black (nine_bit_uv, false);
301 case PIX_FMT_YUV422P9BE:
302 case PIX_FMT_YUV444P9BE:
303 yuv_16_black (swap_16 (nine_bit_uv), false);
306 case PIX_FMT_YUV422P10LE:
307 case PIX_FMT_YUV444P10LE:
308 yuv_16_black (ten_bit_uv, false);
311 case PIX_FMT_YUV422P16LE:
312 case PIX_FMT_YUV444P16LE:
313 yuv_16_black (sixteen_bit_uv, false);
316 case PIX_FMT_YUV444P10BE:
317 case PIX_FMT_YUV422P10BE:
318 yuv_16_black (swap_16 (ten_bit_uv), false);
321 case AV_PIX_FMT_YUVA420P9BE:
322 case AV_PIX_FMT_YUVA422P9BE:
323 case AV_PIX_FMT_YUVA444P9BE:
324 yuv_16_black (swap_16 (nine_bit_uv), true);
327 case AV_PIX_FMT_YUVA420P9LE:
328 case AV_PIX_FMT_YUVA422P9LE:
329 case AV_PIX_FMT_YUVA444P9LE:
330 yuv_16_black (nine_bit_uv, true);
333 case AV_PIX_FMT_YUVA420P10BE:
334 case AV_PIX_FMT_YUVA422P10BE:
335 case AV_PIX_FMT_YUVA444P10BE:
336 yuv_16_black (swap_16 (ten_bit_uv), true);
339 case AV_PIX_FMT_YUVA420P10LE:
340 case AV_PIX_FMT_YUVA422P10LE:
341 case AV_PIX_FMT_YUVA444P10LE:
342 yuv_16_black (ten_bit_uv, true);
345 case AV_PIX_FMT_YUVA420P16BE:
346 case AV_PIX_FMT_YUVA422P16BE:
347 case AV_PIX_FMT_YUVA444P16BE:
348 yuv_16_black (swap_16 (sixteen_bit_uv), true);
351 case AV_PIX_FMT_YUVA420P16LE:
352 case AV_PIX_FMT_YUVA422P16LE:
353 case AV_PIX_FMT_YUVA444P16LE:
354 yuv_16_black (sixteen_bit_uv, true);
362 case PIX_FMT_RGB555LE:
363 case PIX_FMT_RGB48LE:
364 case PIX_FMT_RGB48BE:
365 memset (data()[0], 0, lines(0) * stride()[0]);
368 case PIX_FMT_UYVY422:
370 int const Y = lines(0);
371 int const X = line_size()[0];
372 uint8_t* p = data()[0];
373 for (int y = 0; y < Y; ++y) {
374 for (int x = 0; x < X / 4; ++x) {
375 *p++ = eight_bit_uv; // Cb
377 *p++ = eight_bit_uv; // Cr
385 throw PixelFormatError ("make_black()", _pixel_format);
390 Image::make_transparent ()
392 if (_pixel_format != PIX_FMT_RGBA) {
393 throw PixelFormatError ("make_transparent()", _pixel_format);
396 memset (data()[0], 0, lines(0) * stride()[0]);
400 Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
402 DCPOMATIC_ASSERT (other->pixel_format() == PIX_FMT_RGBA);
403 int const other_bpp = 4;
405 int start_tx = position.x;
409 start_ox = -start_tx;
413 int start_ty = position.y;
417 start_oy = -start_ty;
421 switch (_pixel_format) {
424 int const this_bpp = 3;
425 for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
426 uint8_t* tp = data()[0] + ty * stride()[0] + start_tx * this_bpp;
427 uint8_t* op = other->data()[0] + oy * other->stride()[0];
428 for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
429 float const alpha = float (op[3]) / 255;
430 tp[0] = op[0] * alpha + tp[0] * (1 - alpha);
431 tp[1] = op[1] * alpha + tp[1] * (1 - alpha);
432 tp[2] = op[2] * alpha + tp[2] * (1 - alpha);
443 int const this_bpp = 4;
444 for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
445 uint8_t* tp = data()[0] + ty * stride()[0] + start_tx * this_bpp;
446 uint8_t* op = other->data()[0] + oy * other->stride()[0];
447 for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
448 float const alpha = float (op[3]) / 255;
449 tp[0] = op[0] * alpha + tp[0] * (1 - alpha);
450 tp[1] = op[1] * alpha + tp[1] * (1 - alpha);
451 tp[2] = op[2] * alpha + tp[2] * (1 - alpha);
452 tp[3] = op[3] * alpha + tp[3] * (1 - alpha);
460 case PIX_FMT_RGB48LE:
462 int const this_bpp = 6;
463 for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
464 uint8_t* tp = data()[0] + ty * stride()[0] + start_tx * this_bpp;
465 uint8_t* op = other->data()[0] + oy * other->stride()[0];
466 for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
467 float const alpha = float (op[3]) / 255;
468 /* Blend high bytes */
469 tp[1] = op[0] * alpha + tp[1] * (1 - alpha);
470 tp[3] = op[1] * alpha + tp[3] * (1 - alpha);
471 tp[5] = op[2] * alpha + tp[5] * (1 - alpha);
480 DCPOMATIC_ASSERT (false);
485 Image::copy (shared_ptr<const Image> other, Position<int> position)
487 /* Only implemented for RGB24 onto RGB24 so far */
488 DCPOMATIC_ASSERT (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGB24);
489 DCPOMATIC_ASSERT (position.x >= 0 && position.y >= 0);
491 int const N = min (position.x + other->size().width, size().width) - position.x;
492 for (int ty = position.y, oy = 0; ty < size().height && oy < other->size().height; ++ty, ++oy) {
493 uint8_t * const tp = data()[0] + ty * stride()[0] + position.x * 3;
494 uint8_t * const op = other->data()[0] + oy * other->stride()[0];
495 memcpy (tp, op, N * 3);
500 Image::read_from_socket (shared_ptr<Socket> socket)
502 for (int i = 0; i < components(); ++i) {
503 uint8_t* p = data()[i];
504 for (int y = 0; y < lines(i); ++y) {
505 socket->read (p, line_size()[i]);
512 Image::write_to_socket (shared_ptr<Socket> socket) const
514 for (int i = 0; i < components(); ++i) {
515 uint8_t* p = data()[i];
516 for (int y = 0; y < lines(i); ++y) {
517 socket->write (p, line_size()[i]);
525 Image::bytes_per_pixel (int c) const
527 AVPixFmtDescriptor const * d = av_pix_fmt_desc_get(_pixel_format);
529 throw PixelFormatError ("bytes_per_pixel()", _pixel_format);
532 if (c >= components()) {
536 float bpp[4] = { 0, 0, 0, 0 };
538 bpp[0] = floor ((d->comp[0].depth_minus1 + 1 + 7) / 8);
539 if (d->nb_components > 1) {
540 bpp[1] = floor ((d->comp[1].depth_minus1 + 1 + 7) / 8) / pow (2.0f, d->log2_chroma_w);
542 if (d->nb_components > 2) {
543 bpp[2] = floor ((d->comp[2].depth_minus1 + 1 + 7) / 8) / pow (2.0f, d->log2_chroma_w);
545 if (d->nb_components > 3) {
546 bpp[3] = floor ((d->comp[3].depth_minus1 + 1 + 7) / 8) / pow (2.0f, d->log2_chroma_w);
549 if ((d->flags & PIX_FMT_PLANAR) == 0) {
550 /* Not planar; sum them up */
551 return bpp[0] + bpp[1] + bpp[2] + bpp[3];
557 /** Construct a Image of a given size and format, allocating memory
560 * @param p Pixel format.
561 * @param s Size in pixels.
563 Image::Image (AVPixelFormat p, dcp::Size s, bool aligned)
574 _data = (uint8_t **) wrapped_av_malloc (4 * sizeof (uint8_t *));
575 _data[0] = _data[1] = _data[2] = _data[3] = 0;
577 _line_size = (int *) wrapped_av_malloc (4 * sizeof (int));
578 _line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0;
580 _stride = (int *) wrapped_av_malloc (4 * sizeof (int));
581 _stride[0] = _stride[1] = _stride[2] = _stride[3] = 0;
583 for (int i = 0; i < components(); ++i) {
584 _line_size[i] = ceil (_size.width * bytes_per_pixel(i));
585 _stride[i] = stride_round_up (i, _line_size, _aligned ? 32 : 1);
587 /* The assembler function ff_rgb24ToY_avx (in libswscale/x86/input.asm)
588 uses a 16-byte fetch to read three bytes (R/G/B) of image data.
589 Hence on the last pixel of the last line it reads over the end of
590 the actual data by 1 byte. If the width of an image is a multiple
591 of the stride alignment there will be no padding at the end of image lines.
592 OS X crashes on this illegal read, though other operating systems don't
593 seem to mind. The nasty + 1 in this malloc makes sure there is always a byte
594 for that instruction to read safely.
596 Further to the above, valgrind is now telling me that ff_rgb24ToY_ssse3
597 over-reads by more then _avx. I can't follow the code to work out how much,
598 so I'll just over-allocate by 32 bytes and have done with it. Empirical
599 testing suggests that it works.
601 _data[i] = (uint8_t *) wrapped_av_malloc (_stride[i] * lines (i) + 32);
605 Image::Image (Image const & other)
606 : _size (other._size)
607 , _pixel_format (other._pixel_format)
608 , _aligned (other._aligned)
612 for (int i = 0; i < components(); ++i) {
613 uint8_t* p = _data[i];
614 uint8_t* q = other._data[i];
615 for (int j = 0; j < lines(i); ++j) {
616 memcpy (p, q, _line_size[i]);
618 q += other.stride()[i];
623 Image::Image (AVFrame* frame)
624 : _size (frame->width, frame->height)
625 , _pixel_format (static_cast<AVPixelFormat> (frame->format))
630 for (int i = 0; i < components(); ++i) {
631 uint8_t* p = _data[i];
632 uint8_t* q = frame->data[i];
633 for (int j = 0; j < lines(i); ++j) {
634 memcpy (p, q, _line_size[i]);
636 /* AVFrame's linesize is what we call `stride' */
637 q += frame->linesize[i];
642 Image::Image (shared_ptr<const Image> other, bool aligned)
643 : _size (other->_size)
644 , _pixel_format (other->_pixel_format)
649 for (int i = 0; i < components(); ++i) {
650 DCPOMATIC_ASSERT (line_size()[i] == other->line_size()[i]);
651 uint8_t* p = _data[i];
652 uint8_t* q = other->data()[i];
653 for (int j = 0; j < lines(i); ++j) {
654 memcpy (p, q, line_size()[i]);
656 q += other->stride()[i];
662 Image::operator= (Image const & other)
664 if (this == &other) {
674 Image::swap (Image & other)
676 std::swap (_size, other._size);
677 std::swap (_pixel_format, other._pixel_format);
679 for (int i = 0; i < 4; ++i) {
680 std::swap (_data[i], other._data[i]);
681 std::swap (_line_size[i], other._line_size[i]);
682 std::swap (_stride[i], other._stride[i]);
685 std::swap (_aligned, other._aligned);
688 /** Destroy a Image */
691 for (int i = 0; i < components(); ++i) {
696 av_free (_line_size);
707 Image::line_size () const
713 Image::stride () const
725 Image::aligned () const
731 merge (list<PositionImage> images)
733 if (images.empty ()) {
734 return PositionImage ();
737 if (images.size() == 1) {
738 return images.front ();
741 dcpomatic::Rect<int> all (images.front().position, images.front().image->size().width, images.front().image->size().height);
742 for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
743 all.extend (dcpomatic::Rect<int> (i->position, i->image->size().width, i->image->size().height));
746 shared_ptr<Image> merged (new Image (images.front().image->pixel_format (), dcp::Size (all.width, all.height), true));
747 merged->make_transparent ();
748 for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
749 merged->alpha_blend (i->image, i->position - all.position());
752 return PositionImage (merged, all.position ());
756 operator== (Image const & a, Image const & b)
758 if (a.components() != b.components() || a.pixel_format() != b.pixel_format() || a.aligned() != b.aligned()) {
762 for (int c = 0; c < a.components(); ++c) {
763 if (a.lines(c) != b.lines(c) || a.line_size()[c] != b.line_size()[c] || a.stride()[c] != b.stride()[c]) {
767 uint8_t* p = a.data()[c];
768 uint8_t* q = b.data()[c];
769 for (int y = 0; y < a.lines(c); ++y) {
770 if (memcmp (p, q, a.line_size()[c]) != 0) {
783 * @param f Amount to fade by; 0 is black, 1 is no fade.
786 Image::fade (float f)
788 switch (_pixel_format) {
789 case PIX_FMT_YUV420P:
790 case PIX_FMT_YUV422P:
791 case PIX_FMT_YUV444P:
792 case PIX_FMT_YUV411P:
793 case PIX_FMT_YUVJ420P:
794 case PIX_FMT_YUVJ422P:
795 case PIX_FMT_YUVJ444P:
801 case PIX_FMT_RGB555LE:
803 for (int c = 0; c < 3; ++c) {
804 uint8_t* p = data()[c];
805 for (int y = 0; y < lines(c); ++y) {
807 for (int x = 0; x < line_size()[c]; ++x) {
808 *q = int (float (*q) * f);
816 case PIX_FMT_YUV422P9LE:
817 case PIX_FMT_YUV444P9LE:
818 case PIX_FMT_YUV422P10LE:
819 case PIX_FMT_YUV444P10LE:
820 case PIX_FMT_YUV422P16LE:
821 case PIX_FMT_YUV444P16LE:
822 case AV_PIX_FMT_YUVA420P9LE:
823 case AV_PIX_FMT_YUVA422P9LE:
824 case AV_PIX_FMT_YUVA444P9LE:
825 case AV_PIX_FMT_YUVA420P10LE:
826 case AV_PIX_FMT_YUVA422P10LE:
827 case AV_PIX_FMT_YUVA444P10LE:
828 case AV_PIX_FMT_RGB48LE:
829 /* 16-bit little-endian */
830 for (int c = 0; c < 3; ++c) {
831 int const stride_pixels = stride()[c] / 2;
832 int const line_size_pixels = line_size()[c] / 2;
833 uint16_t* p = reinterpret_cast<uint16_t*> (data()[c]);
834 for (int y = 0; y < lines(c); ++y) {
836 for (int x = 0; x < line_size_pixels; ++x) {
837 *q = int (float (*q) * f);
845 case PIX_FMT_YUV422P9BE:
846 case PIX_FMT_YUV444P9BE:
847 case PIX_FMT_YUV444P10BE:
848 case PIX_FMT_YUV422P10BE:
849 case AV_PIX_FMT_YUVA420P9BE:
850 case AV_PIX_FMT_YUVA422P9BE:
851 case AV_PIX_FMT_YUVA444P9BE:
852 case AV_PIX_FMT_YUVA420P10BE:
853 case AV_PIX_FMT_YUVA422P10BE:
854 case AV_PIX_FMT_YUVA444P10BE:
855 case AV_PIX_FMT_YUVA420P16BE:
856 case AV_PIX_FMT_YUVA422P16BE:
857 case AV_PIX_FMT_YUVA444P16BE:
858 case AV_PIX_FMT_RGB48BE:
859 /* 16-bit big-endian */
860 for (int c = 0; c < 3; ++c) {
861 int const stride_pixels = stride()[c] / 2;
862 int const line_size_pixels = line_size()[c] / 2;
863 uint16_t* p = reinterpret_cast<uint16_t*> (data()[c]);
864 for (int y = 0; y < lines(c); ++y) {
866 for (int x = 0; x < line_size_pixels; ++x) {
867 *q = swap_16 (int (float (swap_16 (*q)) * f));
875 case PIX_FMT_UYVY422:
877 int const Y = lines(0);
878 int const X = line_size()[0];
879 uint8_t* p = data()[0];
880 for (int y = 0; y < Y; ++y) {
881 for (int x = 0; x < X; ++x) {
882 *p = int (float (*p) * f);
890 throw PixelFormatError ("fade()", _pixel_format);