2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 /** @file src/image.cc
23 * @brief A class to describe a video image.
27 #include "compose.hpp"
28 #include "dcpomatic_assert.h"
29 #include "dcpomatic_socket.h"
30 #include "enum_indexed_vector.h"
31 #include "exceptions.h"
33 #include "maths_util.h"
34 #include "memory_util.h"
37 #include <dcp/rgb_xyz.h>
38 #include <dcp/transfer_function.h>
39 #include <dcp/warnings.h>
40 LIBDCP_DISABLE_WARNINGS
42 #include <libavutil/frame.h>
43 #include <libavutil/pixdesc.h>
44 #include <libavutil/pixfmt.h>
45 #include <libswscale/swscale.h>
47 LIBDCP_ENABLE_WARNINGS
48 #if HAVE_VALGRIND_MEMCHECK_H
49 #include <valgrind/memcheck.h>
60 using std::make_shared;
63 using std::runtime_error;
64 using std::shared_ptr;
69 /** The memory alignment, in bytes, used for each row of an image if Alignment::PADDED is requested */
70 int constexpr ALIGNMENT = 64;
72 /* U/V black value for 8-bit colour */
73 static uint8_t const eight_bit_uv = (1 << 7) - 1;
74 /* U/V black value for 9-bit colour */
75 static uint16_t const nine_bit_uv = (1 << 8) - 1;
76 /* U/V black value for 10-bit colour */
77 static uint16_t const ten_bit_uv = (1 << 9) - 1;
78 /* U/V black value for 16-bit colour */
79 static uint16_t const sixteen_bit_uv = (1 << 15) - 1;
83 Image::vertical_factor (int n) const
89 auto d = av_pix_fmt_desc_get(_pixel_format);
91 throw PixelFormatError ("line_factor()", _pixel_format);
94 return lrintf(powf(2.0f, d->log2_chroma_h));
98 Image::horizontal_factor (int n) const
104 auto d = av_pix_fmt_desc_get(_pixel_format);
106 throw PixelFormatError ("sample_size()", _pixel_format);
109 return lrintf(powf(2.0f, d->log2_chroma_w));
113 /** @param n Component index.
114 * @return Number of samples (i.e. pixels, unless sub-sampled) in each direction for this component.
117 Image::sample_size (int n) const
120 lrint (ceil(static_cast<double>(size().width) / horizontal_factor(n))),
121 lrint (ceil(static_cast<double>(size().height) / vertical_factor(n)))
126 /** @return Number of planes */
128 Image::planes () const
130 if (_pixel_format == AV_PIX_FMT_PAL8) {
134 auto d = av_pix_fmt_desc_get(_pixel_format);
136 throw PixelFormatError ("planes()", _pixel_format);
139 if ((d->flags & AV_PIX_FMT_FLAG_PLANAR) == 0) {
143 return d->nb_components;
149 round_width_for_subsampling (int p, AVPixFmtDescriptor const * desc)
151 return p & ~ ((1 << desc->log2_chroma_w) - 1);
157 round_height_for_subsampling (int p, AVPixFmtDescriptor const * desc)
159 return p & ~ ((1 << desc->log2_chroma_h) - 1);
163 /** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size'.
164 * @param crop Amount to crop by.
165 * @param inter_size Size to scale the cropped image to.
166 * @param out_size Size of output frame; if this is larger than inter_size there will be black padding.
167 * @param yuv_to_rgb YUV to RGB transformation to use, if required.
168 * @param video_range Video range of the image.
169 * @param out_format Output pixel format.
170 * @param out_aligned true to make the output image aligned.
171 * @param out_video_range Video range to use for the output image.
172 * @param fast Try to be fast at the possible expense of quality; at present this means using
173 * fast bilinear rather than bicubic scaling.
176 Image::crop_scale_window (
178 dcp::Size inter_size,
180 dcp::YUVToRGB yuv_to_rgb,
181 VideoRange video_range,
182 AVPixelFormat out_format,
183 VideoRange out_video_range,
184 Alignment out_alignment,
188 /* Empirical testing suggests that sws_scale() will crash if
189 the input image is not padded.
191 DCPOMATIC_ASSERT (alignment() == Alignment::PADDED);
193 DCPOMATIC_ASSERT (out_size.width >= inter_size.width);
194 DCPOMATIC_ASSERT (out_size.height >= inter_size.height);
196 auto out = make_shared<Image>(out_format, out_size, out_alignment);
199 auto in_desc = av_pix_fmt_desc_get (_pixel_format);
201 throw PixelFormatError ("crop_scale_window()", _pixel_format);
204 /* Round down so that we crop only the number of pixels that is straightforward
205 * considering any subsampling.
208 round_width_for_subsampling(crop.left, in_desc),
209 round_width_for_subsampling(crop.right, in_desc),
210 round_height_for_subsampling(crop.top, in_desc),
211 round_height_for_subsampling(crop.bottom, in_desc)
214 /* Also check that we aren't cropping more image than there actually is */
215 if ((corrected_crop.left + corrected_crop.right) >= (size().width - 4)) {
216 corrected_crop.left = 0;
217 corrected_crop.right = size().width - 4;
220 if ((corrected_crop.top + corrected_crop.bottom) >= (size().height - 4)) {
221 corrected_crop.top = 0;
222 corrected_crop.bottom = size().height - 4;
225 /* Size of the image after any crop */
226 auto const cropped_size = corrected_crop.apply (size());
228 /* Scale context for a scale from cropped_size to inter_size */
229 auto scale_context = sws_getContext (
230 cropped_size.width, cropped_size.height, pixel_format(),
231 inter_size.width, inter_size.height, out_format,
232 fast ? SWS_FAST_BILINEAR : SWS_BICUBIC, 0, 0, 0
235 if (!scale_context) {
236 throw runtime_error (N_("Could not allocate SwsContext"));
239 DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUVToRGB::COUNT);
240 EnumIndexedVector<int, dcp::YUVToRGB> lut;
241 lut[dcp::YUVToRGB::REC601] = SWS_CS_ITU601;
242 lut[dcp::YUVToRGB::REC709] = SWS_CS_ITU709;
243 lut[dcp::YUVToRGB::REC2020] = SWS_CS_BT2020;
245 /* The 3rd parameter here is:
246 0 -> source range MPEG (i.e. "video", 16-235)
247 1 -> source range JPEG (i.e. "full", 0-255)
249 0 -> destination range MPEG (i.e. "video", 16-235)
250 1 -> destination range JPEG (i.e. "full", 0-255)
252 But remember: sws_setColorspaceDetails ignores these
253 parameters unless the both source and destination images
254 are isYUV or isGray. (If either is not, it uses video range).
256 sws_setColorspaceDetails (
258 sws_getCoefficients(lut[yuv_to_rgb]), video_range == VideoRange::VIDEO ? 0 : 1,
259 sws_getCoefficients(lut[yuv_to_rgb]), out_video_range == VideoRange::VIDEO ? 0 : 1,
263 /* Prepare input data pointers with crop */
264 uint8_t* scale_in_data[planes()];
265 for (int c = 0; c < planes(); ++c) {
266 int const x = lrintf(bytes_per_pixel(c) * corrected_crop.left);
267 scale_in_data[c] = data()[c] + x + stride()[c] * (corrected_crop.top / vertical_factor(c));
270 auto out_desc = av_pix_fmt_desc_get (out_format);
272 throw PixelFormatError ("crop_scale_window()", out_format);
275 /* Corner of the image within out_size */
276 Position<int> const corner (
277 round_width_for_subsampling((out_size.width - inter_size.width) / 2, out_desc),
278 round_height_for_subsampling((out_size.height - inter_size.height) / 2, out_desc)
281 uint8_t* scale_out_data[out->planes()];
282 for (int c = 0; c < out->planes(); ++c) {
283 int const x = lrintf(out->bytes_per_pixel(c) * corner.x);
284 scale_out_data[c] = out->data()[c] + x + out->stride()[c] * (corner.y / out->vertical_factor(c));
289 scale_in_data, stride(),
290 0, cropped_size.height,
291 scale_out_data, out->stride()
294 sws_freeContext (scale_context);
296 /* There are some cases where there will be unwanted image data left in the image at this point:
298 * 1. When we are cropping without any scaling or pixel format conversion.
299 * 2. When we are scaling to certain sizes and placing the result into a larger
302 * Clear out the sides of the image to take care of those cases.
304 auto const pad = (out_size.width - inter_size.width) / 2;
305 out->make_part_black(0, pad);
306 out->make_part_black(corner.x + inter_size.width, pad);
309 video_range == VideoRange::VIDEO &&
310 out_video_range == VideoRange::FULL &&
311 av_pix_fmt_desc_get(_pixel_format)->flags & AV_PIX_FMT_FLAG_RGB
313 /* libswscale will not convert video range for RGB sources, so we have to do it ourselves */
314 out->video_range_to_full_range ();
322 Image::convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, Alignment out_alignment, bool fast) const
324 return scale(size(), yuv_to_rgb, out_format, out_alignment, fast);
328 /** @param out_size Size to scale to.
329 * @param yuv_to_rgb YUVToRGB transform transform to use, if required.
330 * @param out_format Output pixel format.
331 * @param out_alignment Output alignment.
332 * @param fast Try to be fast at the possible expense of quality; at present this means using
333 * fast bilinear rather than bicubic scaling.
336 Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, Alignment out_alignment, bool fast) const
338 /* Empirical testing suggests that sws_scale() will crash if
339 the input image alignment is not PADDED.
341 DCPOMATIC_ASSERT (alignment() == Alignment::PADDED);
343 auto scaled = make_shared<Image>(out_format, out_size, out_alignment);
344 auto scale_context = sws_getContext (
345 size().width, size().height, pixel_format(),
346 out_size.width, out_size.height, out_format,
347 (fast ? SWS_FAST_BILINEAR : SWS_BICUBIC) | SWS_ACCURATE_RND, 0, 0, 0
350 DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUVToRGB::COUNT);
351 EnumIndexedVector<int, dcp::YUVToRGB> lut;
352 lut[dcp::YUVToRGB::REC601] = SWS_CS_ITU601;
353 lut[dcp::YUVToRGB::REC709] = SWS_CS_ITU709;
354 lut[dcp::YUVToRGB::REC2020] = SWS_CS_BT2020;
356 /* The 3rd parameter here is:
357 0 -> source range MPEG (i.e. "video", 16-235)
358 1 -> source range JPEG (i.e. "full", 0-255)
360 0 -> destination range MPEG (i.e. "video", 16-235)
361 1 -> destination range JPEG (i.e. "full", 0-255)
363 But remember: sws_setColorspaceDetails ignores these
364 parameters unless the corresponding image isYUV or isGray.
365 (If it's neither, it uses video range).
367 sws_setColorspaceDetails (
369 sws_getCoefficients(lut[yuv_to_rgb]), 0,
370 sws_getCoefficients(lut[yuv_to_rgb]), 0,
378 scaled->data(), scaled->stride()
381 sws_freeContext (scale_context);
387 /** Blacken a YUV image whose bits per pixel is rounded up to 16 */
389 Image::yuv_16_black (uint16_t v, bool alpha)
391 memset (data()[0], 0, sample_size(0).height * stride()[0]);
392 for (int i = 1; i < 3; ++i) {
393 auto p = reinterpret_cast<int16_t*> (data()[i]);
394 int const lines = sample_size(i).height;
395 for (int y = 0; y < lines; ++y) {
396 /* We divide by 2 here because we are writing 2 bytes at a time */
397 for (int x = 0; x < line_size()[i] / 2; ++x) {
400 p += stride()[i] / 2;
405 memset (data()[3], 0, sample_size(3).height * stride()[3]);
411 Image::swap_16 (uint16_t v)
413 return ((v >> 8) & 0xff) | ((v & 0xff) << 8);
418 Image::make_part_black (int const start, int const width)
420 auto y_part = [&]() {
421 int const bpp = bytes_per_pixel(0);
422 int const h = sample_size(0).height;
423 int const s = stride()[0];
425 for (int y = 0; y < h; ++y) {
426 memset (p + start * bpp, 0, width * bpp);
431 switch (_pixel_format) {
432 case AV_PIX_FMT_RGB24:
433 case AV_PIX_FMT_ARGB:
434 case AV_PIX_FMT_RGBA:
435 case AV_PIX_FMT_ABGR:
436 case AV_PIX_FMT_BGRA:
437 case AV_PIX_FMT_RGB555LE:
438 case AV_PIX_FMT_RGB48LE:
439 case AV_PIX_FMT_RGB48BE:
440 case AV_PIX_FMT_XYZ12LE:
442 int const h = sample_size(0).height;
443 int const bpp = bytes_per_pixel(0);
444 int const s = stride()[0];
445 uint8_t* p = data()[0];
446 for (int y = 0; y < h; y++) {
447 memset (p + start * bpp, 0, width * bpp);
452 case AV_PIX_FMT_YUV420P:
455 for (int i = 1; i < 3; ++i) {
457 int const h = sample_size(i).height;
458 for (int y = 0; y < h; ++y) {
459 for (int x = start / 2; x < (start + width) / 2; ++x) {
467 case AV_PIX_FMT_YUV422P10LE:
470 for (int i = 1; i < 3; ++i) {
471 auto p = reinterpret_cast<int16_t*>(data()[i]);
472 int const h = sample_size(i).height;
473 for (int y = 0; y < h; ++y) {
474 for (int x = start / 2; x < (start + width) / 2; ++x) {
477 p += stride()[i] / 2;
482 case AV_PIX_FMT_YUV444P10LE:
485 for (int i = 1; i < 3; ++i) {
486 auto p = reinterpret_cast<int16_t*>(data()[i]);
487 int const h = sample_size(i).height;
488 for (int y = 0; y < h; ++y) {
489 for (int x = start; x < (start + width); ++x) {
492 p += stride()[i] / 2;
498 throw PixelFormatError ("make_part_black()", _pixel_format);
506 switch (_pixel_format) {
507 case AV_PIX_FMT_YUV420P:
508 case AV_PIX_FMT_YUV422P:
509 case AV_PIX_FMT_YUV444P:
510 case AV_PIX_FMT_YUV411P:
511 memset (data()[0], 0, sample_size(0).height * stride()[0]);
512 memset (data()[1], eight_bit_uv, sample_size(1).height * stride()[1]);
513 memset (data()[2], eight_bit_uv, sample_size(2).height * stride()[2]);
516 case AV_PIX_FMT_YUVJ420P:
517 case AV_PIX_FMT_YUVJ422P:
518 case AV_PIX_FMT_YUVJ444P:
519 memset (data()[0], 0, sample_size(0).height * stride()[0]);
520 memset (data()[1], eight_bit_uv + 1, sample_size(1).height * stride()[1]);
521 memset (data()[2], eight_bit_uv + 1, sample_size(2).height * stride()[2]);
524 case AV_PIX_FMT_YUV422P9LE:
525 case AV_PIX_FMT_YUV444P9LE:
526 yuv_16_black (nine_bit_uv, false);
529 case AV_PIX_FMT_YUV422P9BE:
530 case AV_PIX_FMT_YUV444P9BE:
531 yuv_16_black (swap_16 (nine_bit_uv), false);
534 case AV_PIX_FMT_YUV422P10LE:
535 case AV_PIX_FMT_YUV444P10LE:
536 yuv_16_black (ten_bit_uv, false);
539 case AV_PIX_FMT_YUV422P16LE:
540 case AV_PIX_FMT_YUV444P16LE:
541 yuv_16_black (sixteen_bit_uv, false);
544 case AV_PIX_FMT_YUV444P10BE:
545 case AV_PIX_FMT_YUV422P10BE:
546 yuv_16_black (swap_16 (ten_bit_uv), false);
549 case AV_PIX_FMT_YUVA420P9BE:
550 case AV_PIX_FMT_YUVA422P9BE:
551 case AV_PIX_FMT_YUVA444P9BE:
552 yuv_16_black (swap_16 (nine_bit_uv), true);
555 case AV_PIX_FMT_YUVA420P9LE:
556 case AV_PIX_FMT_YUVA422P9LE:
557 case AV_PIX_FMT_YUVA444P9LE:
558 yuv_16_black (nine_bit_uv, true);
561 case AV_PIX_FMT_YUVA420P10BE:
562 case AV_PIX_FMT_YUVA422P10BE:
563 case AV_PIX_FMT_YUVA444P10BE:
564 yuv_16_black (swap_16 (ten_bit_uv), true);
567 case AV_PIX_FMT_YUVA420P10LE:
568 case AV_PIX_FMT_YUVA422P10LE:
569 case AV_PIX_FMT_YUVA444P10LE:
570 yuv_16_black (ten_bit_uv, true);
573 case AV_PIX_FMT_YUVA420P16BE:
574 case AV_PIX_FMT_YUVA422P16BE:
575 case AV_PIX_FMT_YUVA444P16BE:
576 yuv_16_black (swap_16 (sixteen_bit_uv), true);
579 case AV_PIX_FMT_YUVA420P16LE:
580 case AV_PIX_FMT_YUVA422P16LE:
581 case AV_PIX_FMT_YUVA444P16LE:
582 yuv_16_black (sixteen_bit_uv, true);
585 case AV_PIX_FMT_RGB24:
586 case AV_PIX_FMT_ARGB:
587 case AV_PIX_FMT_RGBA:
588 case AV_PIX_FMT_ABGR:
589 case AV_PIX_FMT_BGRA:
590 case AV_PIX_FMT_RGB555LE:
591 case AV_PIX_FMT_RGB48LE:
592 case AV_PIX_FMT_RGB48BE:
593 case AV_PIX_FMT_XYZ12LE:
594 memset (data()[0], 0, sample_size(0).height * stride()[0]);
597 case AV_PIX_FMT_UYVY422:
599 int const Y = sample_size(0).height;
600 int const X = line_size()[0];
601 uint8_t* p = data()[0];
602 for (int y = 0; y < Y; ++y) {
603 for (int x = 0; x < X / 4; ++x) {
604 *p++ = eight_bit_uv; // Cb
606 *p++ = eight_bit_uv; // Cr
614 throw PixelFormatError ("make_black()", _pixel_format);
620 Image::make_transparent ()
622 if (_pixel_format != AV_PIX_FMT_BGRA && _pixel_format != AV_PIX_FMT_RGBA && _pixel_format != AV_PIX_FMT_RGBA64BE) {
623 throw PixelFormatError ("make_transparent()", _pixel_format);
626 memset (data()[0], 0, sample_size(0).height * stride()[0]);
635 uint8_t* const* data;
639 uint8_t* line_pointer(int y) const {
640 return data[0] + y * stride[0] + start_x * bpp;
650 uint8_t* const* data;
654 uint8_t* line_pointer(int y) const {
655 return data[0] + y * stride[0];
658 float alpha_divisor() const {
659 return pow(2, bpp * 2) - 1;
664 template <class OtherType>
666 alpha_blend_onto_rgb24(TargetParams const& target, OtherParams const& other, int red, int blue, std::function<float (OtherType*)> get, int value_divisor)
668 /* Going onto RGB24. First byte is red, second green, third blue */
669 auto const alpha_divisor = other.alpha_divisor();
670 for (int ty = target.start_y, oy = other.start_y; ty < target.size.height && oy < other.size.height; ++ty, ++oy) {
671 auto tp = target.line_pointer(ty);
672 auto op = reinterpret_cast<OtherType*>(other.line_pointer(oy));
673 for (int tx = target.start_x, ox = other.start_x; tx < target.size.width && ox < other.size.width; ++tx, ++ox) {
674 float const alpha = get(op + 3) / alpha_divisor;
675 tp[0] = (get(op + red) / value_divisor) * alpha + tp[0] * (1 - alpha);
676 tp[1] = (get(op + 1) / value_divisor) * alpha + tp[1] * (1 - alpha);
677 tp[2] = (get(op + blue) / value_divisor) * alpha + tp[2] * (1 - alpha);
680 op += other.bpp / sizeof(OtherType);
686 template <class OtherType>
688 alpha_blend_onto_bgra(TargetParams const& target, OtherParams const& other, int red, int blue, std::function<float (OtherType*)> get, int value_divisor)
690 auto const alpha_divisor = other.alpha_divisor();
691 for (int ty = target.start_y, oy = other.start_y; ty < target.size.height && oy < other.size.height; ++ty, ++oy) {
692 auto tp = target.line_pointer(ty);
693 auto op = reinterpret_cast<OtherType*>(other.line_pointer(oy));
694 for (int tx = target.start_x, ox = other.start_x; tx < target.size.width && ox < other.size.width; ++tx, ++ox) {
695 float const alpha = get(op + 3) / alpha_divisor;
696 tp[0] = (get(op + blue) / value_divisor) * alpha + tp[0] * (1 - alpha);
697 tp[1] = (get(op + 1) / value_divisor) * alpha + tp[1] * (1 - alpha);
698 tp[2] = (get(op + red) / value_divisor) * alpha + tp[2] * (1 - alpha);
699 tp[3] = (get(op + 3) / value_divisor) * alpha + tp[3] * (1 - alpha);
702 op += other.bpp / sizeof(OtherType);
708 template <class OtherType>
710 alpha_blend_onto_rgba(TargetParams const& target, OtherParams const& other, int red, int blue, std::function<float (OtherType*)> get, int value_divisor)
712 auto const alpha_divisor = other.alpha_divisor();
713 for (int ty = target.start_y, oy = other.start_y; ty < target.size.height && oy < other.size.height; ++ty, ++oy) {
714 auto tp = target.line_pointer(ty);
715 auto op = reinterpret_cast<OtherType*>(other.line_pointer(oy));
716 for (int tx = target.start_x, ox = other.start_x; tx < target.size.width && ox < other.size.width; ++tx, ++ox) {
717 float const alpha = get(op + 3) / alpha_divisor;
718 tp[0] = (get(op + red) / value_divisor) * alpha + tp[0] * (1 - alpha);
719 tp[1] = (get(op + 1) / value_divisor) * alpha + tp[1] * (1 - alpha);
720 tp[2] = (get(op + blue) / value_divisor) * alpha + tp[2] * (1 - alpha);
721 tp[3] = (get(op + 3) / value_divisor) * alpha + tp[3] * (1 - alpha);
724 op += other.bpp / sizeof(OtherType);
730 template <class OtherType>
732 alpha_blend_onto_rgb48le(TargetParams const& target, OtherParams const& other, int red, int blue, std::function<float (OtherType*)> get, int value_scale)
734 auto const alpha_divisor = other.alpha_divisor();
735 for (int ty = target.start_y, oy = other.start_y; ty < target.size.height && oy < other.size.height; ++ty, ++oy) {
736 auto tp = reinterpret_cast<uint16_t*>(target.line_pointer(ty));
737 auto op = reinterpret_cast<OtherType*>(other.line_pointer(oy));
738 for (int tx = target.start_x, ox = other.start_x; tx < target.size.width && ox < other.size.width; ++tx, ++ox) {
739 float const alpha = get(op + 3) / alpha_divisor;
740 tp[0] = get(op + red) * value_scale * alpha + tp[0] * (1 - alpha);
741 tp[1] = get(op + 1) * value_scale * alpha + tp[1] * (1 - alpha);
742 tp[2] = get(op + blue) * value_scale * alpha + tp[2] * (1 - alpha);
744 tp += target.bpp / 2;
745 op += other.bpp / sizeof(OtherType);
751 template <class OtherType>
753 alpha_blend_onto_xyz12le(TargetParams const& target, OtherParams const& other, int red, int blue, std::function<float (OtherType*)> get, int value_divisor)
755 auto const alpha_divisor = other.alpha_divisor();
756 auto conv = dcp::ColourConversion::srgb_to_xyz();
757 double fast_matrix[9];
758 dcp::combined_rgb_to_xyz(conv, fast_matrix);
759 auto lut_in = conv.in()->double_lut(0, 1, 8, false);
760 auto lut_out = conv.out()->int_lut(0, 1, 16, true, 65535);
761 for (int ty = target.start_y, oy = other.start_y; ty < target.size.height && oy < other.size.height; ++ty, ++oy) {
762 auto tp = reinterpret_cast<uint16_t*>(target.data[0] + ty * target.stride[0] + target.start_x * target.bpp);
763 auto op = reinterpret_cast<OtherType*>(other.data[0] + oy * other.stride[0]);
764 for (int tx = target.start_x, ox = other.start_x; tx < target.size.width && ox < other.size.width; ++tx, ++ox) {
765 float const alpha = get(op + 3) / alpha_divisor;
767 /* Convert sRGB to XYZ; op is BGRA. First, input gamma LUT */
768 double const r = lut_in[get(op + red) / value_divisor];
769 double const g = lut_in[get(op + 1) / value_divisor];
770 double const b = lut_in[get(op + blue) / value_divisor];
772 /* RGB to XYZ, including Bradford transform and DCI companding */
773 double const x = max(0.0, min(1.0, r * fast_matrix[0] + g * fast_matrix[1] + b * fast_matrix[2]));
774 double const y = max(0.0, min(1.0, r * fast_matrix[3] + g * fast_matrix[4] + b * fast_matrix[5]));
775 double const z = max(0.0, min(1.0, r * fast_matrix[6] + g * fast_matrix[7] + b * fast_matrix[8]));
777 /* Out gamma LUT and blend */
778 tp[0] = lut_out[lrint(x * 65535)] * alpha + tp[0] * (1 - alpha);
779 tp[1] = lut_out[lrint(y * 65535)] * alpha + tp[1] * (1 - alpha);
780 tp[2] = lut_out[lrint(z * 65535)] * alpha + tp[2] * (1 - alpha);
782 tp += target.bpp / 2;
783 op += other.bpp / sizeof(OtherType);
791 alpha_blend_onto_yuv420p(TargetParams const& target, OtherParams const& other, uint8_t* const* alpha_data, int const* alpha_stride)
793 auto const ts = target.size;
794 auto const os = other.size;
795 for (int ty = target.start_y, oy = other.start_y; ty < ts.height && oy < os.height; ++ty, ++oy) {
796 int const hty = ty / 2;
797 int const hoy = oy / 2;
798 uint8_t* tY = target.data[0] + (ty * target.stride[0]) + target.start_x;
799 uint8_t* tU = target.data[1] + (hty * target.stride[1]) + target.start_x / 2;
800 uint8_t* tV = target.data[2] + (hty * target.stride[2]) + target.start_x / 2;
801 uint8_t* oY = other.data[0] + (oy * other.stride[0]) + other.start_x;
802 uint8_t* oU = other.data[1] + (hoy * other.stride[1]) + other.start_x / 2;
803 uint8_t* oV = other.data[2] + (hoy * other.stride[2]) + other.start_x / 2;
804 uint8_t* alpha = alpha_data[0] + (oy * alpha_stride[0]) + other.start_x * 4;
805 for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) {
806 float const a = float(alpha[3]) / 255;
807 *tY = *oY * a + *tY * (1 - a);
808 *tU = *oU * a + *tU * (1 - a);
809 *tV = *oV * a + *tV * (1 - a);
828 alpha_blend_onto_yuv420p10(TargetParams const& target, OtherParams const& other, uint8_t* const* alpha_data, int const* alpha_stride)
830 auto const ts = target.size;
831 auto const os = other.size;
832 for (int ty = target.start_y, oy = other.start_y; ty < ts.height && oy < os.height; ++ty, ++oy) {
833 int const hty = ty / 2;
834 int const hoy = oy / 2;
835 uint16_t* tY = reinterpret_cast<uint16_t*>(target.data[0] + (ty * target.stride[0])) + target.start_x;
836 uint16_t* tU = reinterpret_cast<uint16_t*>(target.data[1] + (hty * target.stride[1])) + target.start_x / 2;
837 uint16_t* tV = reinterpret_cast<uint16_t*>(target.data[2] + (hty * target.stride[2])) + target.start_x / 2;
838 uint16_t* oY = reinterpret_cast<uint16_t*>(other.data[0] + (oy * other.stride[0])) + other.start_x;
839 uint16_t* oU = reinterpret_cast<uint16_t*>(other.data[1] + (hoy * other.stride[1])) + other.start_x / 2;
840 uint16_t* oV = reinterpret_cast<uint16_t*>(other.data[2] + (hoy * other.stride[2])) + other.start_x / 2;
841 uint8_t* alpha = alpha_data[0] + (oy * alpha_stride[0]) + other.start_x * 4;
842 for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) {
843 float const a = float(alpha[3]) / 255;
844 *tY = *oY * a + *tY * (1 - a);
845 *tU = *oU * a + *tU * (1 - a);
846 *tV = *oV * a + *tV * (1 - a);
865 alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherParams const& other, uint8_t* const* alpha_data, int const* alpha_stride)
867 auto const ts = target.size;
868 auto const os = other.size;
869 for (int ty = target.start_y, oy = other.start_y; ty < ts.height && oy < os.height; ++ty, ++oy) {
870 uint16_t* tY = reinterpret_cast<uint16_t*>(target.data[0] + (ty * target.stride[0])) + target.start_x;
871 uint16_t* tU = reinterpret_cast<uint16_t*>(target.data[1] + (ty * target.stride[1])) + target.start_x / 2;
872 uint16_t* tV = reinterpret_cast<uint16_t*>(target.data[2] + (ty * target.stride[2])) + target.start_x / 2;
873 uint16_t* oY = reinterpret_cast<uint16_t*>(other.data[0] + (oy * other.stride[0])) + other.start_x;
874 uint16_t* oU = reinterpret_cast<uint16_t*>(other.data[1] + (oy * other.stride[1])) + other.start_x / 2;
875 uint16_t* oV = reinterpret_cast<uint16_t*>(other.data[2] + (oy * other.stride[2])) + other.start_x / 2;
876 uint8_t* alpha = alpha_data[0] + (oy * alpha_stride[0]) + other.start_x * 4;
877 for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) {
878 float const a = float(alpha[3]) / 255;
879 *tY = *oY * a + *tY * (1 - a);
880 *tU = *oU * a + *tU * (1 - a);
881 *tV = *oV * a + *tV * (1 - a);
899 Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
902 other->pixel_format() == AV_PIX_FMT_BGRA ||
903 other->pixel_format() == AV_PIX_FMT_RGBA ||
904 other->pixel_format() == AV_PIX_FMT_RGBA64BE
907 int const blue = other->pixel_format() == AV_PIX_FMT_BGRA ? 0 : 2;
908 int const red = other->pixel_format() == AV_PIX_FMT_BGRA ? 2 : 0;
910 int start_tx = position.x;
914 start_ox = -start_tx;
918 int start_ty = position.y;
922 start_oy = -start_ty;
926 TargetParams target_params = {
935 OtherParams other_params = {
941 other->pixel_format() == AV_PIX_FMT_RGBA64BE ? 8 : 4
944 auto byteswap = [](uint16_t* p) {
945 return (*p >> 8) | ((*p & 0xff) << 8);
948 auto pass = [](uint8_t* p) {
952 switch (_pixel_format) {
953 case AV_PIX_FMT_RGB24:
954 target_params.bpp = 3;
955 if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) {
956 alpha_blend_onto_rgb24<uint16_t>(target_params, other_params, red, blue, byteswap, 256);
958 alpha_blend_onto_rgb24<uint8_t>(target_params, other_params, red, blue, pass, 1);
961 case AV_PIX_FMT_BGRA:
962 target_params.bpp = 4;
963 if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) {
964 alpha_blend_onto_bgra<uint16_t>(target_params, other_params, red, blue, byteswap, 256);
966 alpha_blend_onto_bgra<uint8_t>(target_params, other_params, red, blue, pass, 1);
969 case AV_PIX_FMT_RGBA:
970 target_params.bpp = 4;
971 if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) {
972 alpha_blend_onto_rgba<uint16_t>(target_params, other_params, red, blue, byteswap, 256);
974 alpha_blend_onto_rgba<uint8_t>(target_params, other_params, red, blue, pass, 1);
977 case AV_PIX_FMT_RGB48LE:
978 target_params.bpp = 6;
979 if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) {
980 alpha_blend_onto_rgb48le<uint16_t>(target_params, other_params, red, blue, byteswap, 1);
982 alpha_blend_onto_rgb48le<uint8_t>(target_params, other_params, red, blue, pass, 256);
985 case AV_PIX_FMT_XYZ12LE:
986 target_params.bpp = 6;
987 if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) {
988 alpha_blend_onto_xyz12le<uint16_t>(target_params, other_params, red, blue, byteswap, 256);
990 alpha_blend_onto_xyz12le<uint8_t>(target_params, other_params, red, blue, pass, 1);
993 case AV_PIX_FMT_YUV420P:
995 auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false);
996 other_params.data = yuv->data();
997 other_params.stride = yuv->stride();
998 alpha_blend_onto_yuv420p(target_params, other_params, other->data(), other->stride());
1001 case AV_PIX_FMT_YUV420P10:
1003 auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false);
1004 other_params.data = yuv->data();
1005 other_params.stride = yuv->stride();
1006 alpha_blend_onto_yuv420p10(target_params, other_params, other->data(), other->stride());
1009 case AV_PIX_FMT_YUV422P9LE:
1010 case AV_PIX_FMT_YUV422P10LE:
1012 auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false);
1013 other_params.data = yuv->data();
1014 other_params.stride = yuv->stride();
1015 alpha_blend_onto_yuv422p9or10le(target_params, other_params, other->data(), other->stride());
1019 throw PixelFormatError ("alpha_blend()", _pixel_format);
1025 Image::copy (shared_ptr<const Image> other, Position<int> position)
1027 /* Only implemented for RGB24 onto RGB24 so far */
1028 DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB24 && other->pixel_format() == AV_PIX_FMT_RGB24);
1029 DCPOMATIC_ASSERT (position.x >= 0 && position.y >= 0);
1031 int const N = min (position.x + other->size().width, size().width) - position.x;
1032 for (int ty = position.y, oy = 0; ty < size().height && oy < other->size().height; ++ty, ++oy) {
1033 uint8_t * const tp = data()[0] + ty * stride()[0] + position.x * 3;
1034 uint8_t * const op = other->data()[0] + oy * other->stride()[0];
1035 memcpy (tp, op, N * 3);
1041 Image::read_from_socket (shared_ptr<Socket> socket)
1043 for (int i = 0; i < planes(); ++i) {
1044 uint8_t* p = data()[i];
1045 int const lines = sample_size(i).height;
1046 for (int y = 0; y < lines; ++y) {
1047 socket->read (p, line_size()[i]);
1055 Image::write_to_socket (shared_ptr<Socket> socket) const
1057 for (int i = 0; i < planes(); ++i) {
1058 uint8_t* p = data()[i];
1059 int const lines = sample_size(i).height;
1060 for (int y = 0; y < lines; ++y) {
1061 socket->write (p, line_size()[i]);
1069 Image::bytes_per_pixel (int c) const
1071 auto d = av_pix_fmt_desc_get(_pixel_format);
1073 throw PixelFormatError ("bytes_per_pixel()", _pixel_format);
1076 if (c >= planes()) {
1080 float bpp[4] = { 0, 0, 0, 0 };
1082 #ifdef DCPOMATIC_HAVE_AVCOMPONENTDESCRIPTOR_DEPTH_MINUS1
1083 bpp[0] = floor ((d->comp[0].depth_minus1 + 8) / 8);
1084 if (d->nb_components > 1) {
1085 bpp[1] = floor ((d->comp[1].depth_minus1 + 8) / 8) / pow (2.0f, d->log2_chroma_w);
1087 if (d->nb_components > 2) {
1088 bpp[2] = floor ((d->comp[2].depth_minus1 + 8) / 8) / pow (2.0f, d->log2_chroma_w);
1090 if (d->nb_components > 3) {
1091 bpp[3] = floor ((d->comp[3].depth_minus1 + 8) / 8) / pow (2.0f, d->log2_chroma_w);
1094 bpp[0] = floor ((d->comp[0].depth + 7) / 8);
1095 if (d->nb_components > 1) {
1096 bpp[1] = floor ((d->comp[1].depth + 7) / 8) / pow (2.0f, d->log2_chroma_w);
1098 if (d->nb_components > 2) {
1099 bpp[2] = floor ((d->comp[2].depth + 7) / 8) / pow (2.0f, d->log2_chroma_w);
1101 if (d->nb_components > 3) {
1102 bpp[3] = floor ((d->comp[3].depth + 7) / 8) / pow (2.0f, d->log2_chroma_w);
1106 if ((d->flags & AV_PIX_FMT_FLAG_PLANAR) == 0) {
1107 /* Not planar; sum them up */
1108 return bpp[0] + bpp[1] + bpp[2] + bpp[3];
1115 /** Construct a Image of a given size and format, allocating memory
1118 * @param p Pixel format.
1119 * @param s Size in pixels.
1120 * @param alignment PADDED to make each row of this image aligned to a ALIGNMENT-byte boundary, otherwise COMPACT.
1122 Image::Image (AVPixelFormat p, dcp::Size s, Alignment alignment)
1125 , _alignment (alignment)
1134 _data = (uint8_t **) wrapped_av_malloc (4 * sizeof (uint8_t *));
1135 _data[0] = _data[1] = _data[2] = _data[3] = 0;
1137 _line_size = (int *) wrapped_av_malloc (4 * sizeof (int));
1138 _line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0;
1140 _stride = (int *) wrapped_av_malloc (4 * sizeof (int));
1141 _stride[0] = _stride[1] = _stride[2] = _stride[3] = 0;
1143 auto stride_round_up = [](int stride, int t) {
1144 int const a = stride + (t - 1);
1148 for (int i = 0; i < planes(); ++i) {
1149 _line_size[i] = ceil (_size.width * bytes_per_pixel(i));
1150 _stride[i] = stride_round_up (_line_size[i], _alignment == Alignment::PADDED ? ALIGNMENT : 1);
1152 /* The assembler function ff_rgb24ToY_avx (in libswscale/x86/input.asm)
1153 uses a 16-byte fetch to read three bytes (R/G/B) of image data.
1154 Hence on the last pixel of the last line it reads over the end of
1155 the actual data by 1 byte. If the width of an image is a multiple
1156 of the stride alignment there will be no padding at the end of image lines.
1157 OS X crashes on this illegal read, though other operating systems don't
1158 seem to mind. The nasty + 1 in this malloc makes sure there is always a byte
1159 for that instruction to read safely.
1161 Further to the above, valgrind is now telling me that ff_rgb24ToY_ssse3
1162 over-reads by more then _avx. I can't follow the code to work out how much,
1163 so I'll just over-allocate by ALIGNMENT bytes and have done with it. Empirical
1164 testing suggests that it works.
1166 In addition to these concerns, we may read/write as much as a whole extra line
1167 at the end of each plane in cases where we are messing with offsets in order to
1168 do pad or crop. To solve this we over-allocate by an extra _stride[i] bytes.
1170 As an example: we may write to images starting at an offset so we get some padding.
1171 Hence we want to write in the following pattern:
1173 block start write start line end
1174 |..(padding)..|<------line-size------------->|..(padding)..|
1175 |..(padding)..|<------line-size------------->|..(padding)..|
1176 |..(padding)..|<------line-size------------->|..(padding)..|
1178 where line-size is of the smaller (inter_size) image and the full padded line length is that of
1179 out_size. To get things to work we have to tell FFmpeg that the stride is that of out_size.
1180 However some parts of FFmpeg (notably rgb48Toxyz12 in swscale.c) process data for the full
1181 specified *stride*. This does not matter until we get to the last line:
1183 block start write start line end
1184 |..(padding)..|<------line-size------------->|XXXwrittenXXX|
1185 |XXXwrittenXXX|<------line-size------------->|XXXwrittenXXX|
1186 |XXXwrittenXXX|<------line-size------------->|XXXwrittenXXXXXXwrittenXXX
1189 _data[i] = (uint8_t *) wrapped_av_malloc (_stride[i] * (sample_size(i).height + 1) + ALIGNMENT);
1190 #if HAVE_VALGRIND_MEMCHECK_H
1191 /* The data between the end of the line size and the stride is undefined but processed by
1192 libswscale, causing lots of valgrind errors. Mark it all defined to quell these errors.
1194 VALGRIND_MAKE_MEM_DEFINED (_data[i], _stride[i] * (sample_size(i).height + 1) + ALIGNMENT);
1200 Image::Image (Image const & other)
1201 : std::enable_shared_from_this<Image>(other)
1202 , _size (other._size)
1203 , _pixel_format (other._pixel_format)
1204 , _alignment (other._alignment)
1208 for (int i = 0; i < planes(); ++i) {
1209 uint8_t* p = _data[i];
1210 uint8_t* q = other._data[i];
1211 int const lines = sample_size(i).height;
1212 for (int j = 0; j < lines; ++j) {
1213 memcpy (p, q, _line_size[i]);
1215 q += other.stride()[i];
1221 Image::Image (AVFrame const * frame, Alignment alignment)
1222 : _size (frame->width, frame->height)
1223 , _pixel_format (static_cast<AVPixelFormat>(frame->format))
1224 , _alignment (alignment)
1226 DCPOMATIC_ASSERT (_pixel_format != AV_PIX_FMT_NONE);
1230 for (int i = 0; i < planes(); ++i) {
1231 uint8_t* p = _data[i];
1232 uint8_t* q = frame->data[i];
1233 int const lines = sample_size(i).height;
1234 for (int j = 0; j < lines; ++j) {
1235 memcpy (p, q, _line_size[i]);
1237 /* AVFrame's linesize is what we call `stride' */
1238 q += frame->linesize[i];
1244 Image::Image (shared_ptr<const Image> other, Alignment alignment)
1245 : _size (other->_size)
1246 , _pixel_format (other->_pixel_format)
1247 , _alignment (alignment)
1251 for (int i = 0; i < planes(); ++i) {
1252 DCPOMATIC_ASSERT (line_size()[i] == other->line_size()[i]);
1253 uint8_t* p = _data[i];
1254 uint8_t* q = other->data()[i];
1255 int const lines = sample_size(i).height;
1256 for (int j = 0; j < lines; ++j) {
1257 memcpy (p, q, line_size()[i]);
1259 q += other->stride()[i];
1266 Image::operator= (Image const & other)
1268 if (this == &other) {
1279 Image::swap (Image & other)
1281 std::swap (_size, other._size);
1282 std::swap (_pixel_format, other._pixel_format);
1284 for (int i = 0; i < 4; ++i) {
1285 std::swap (_data[i], other._data[i]);
1286 std::swap (_line_size[i], other._line_size[i]);
1287 std::swap (_stride[i], other._stride[i]);
1290 std::swap (_alignment, other._alignment);
1296 for (int i = 0; i < planes(); ++i) {
1301 av_free (_line_size);
1307 Image::data () const
1314 Image::line_size () const
1321 Image::stride () const
1328 Image::size () const
1335 Image::alignment () const
1342 merge (list<PositionImage> images, Image::Alignment alignment)
1344 if (images.empty ()) {
1348 if (images.size() == 1) {
1349 images.front().image = Image::ensure_alignment(images.front().image, alignment);
1350 return images.front();
1353 dcpomatic::Rect<int> all (images.front().position, images.front().image->size().width, images.front().image->size().height);
1354 for (auto const& i: images) {
1355 all.extend (dcpomatic::Rect<int>(i.position, i.image->size().width, i.image->size().height));
1358 auto merged = make_shared<Image>(images.front().image->pixel_format(), dcp::Size(all.width, all.height), alignment);
1359 merged->make_transparent ();
1360 for (auto const& i: images) {
1361 merged->alpha_blend (i.image, i.position - all.position());
1364 return PositionImage (merged, all.position ());
1369 operator== (Image const & a, Image const & b)
1371 if (a.planes() != b.planes() || a.pixel_format() != b.pixel_format() || a.alignment() != b.alignment()) {
1375 for (int c = 0; c < a.planes(); ++c) {
1376 if (a.sample_size(c).height != b.sample_size(c).height || a.line_size()[c] != b.line_size()[c] || a.stride()[c] != b.stride()[c]) {
1380 uint8_t* p = a.data()[c];
1381 uint8_t* q = b.data()[c];
1382 int const lines = a.sample_size(c).height;
1383 for (int y = 0; y < lines; ++y) {
1384 if (memcmp (p, q, a.line_size()[c]) != 0) {
1398 * @param f Amount to fade by; 0 is black, 1 is no fade.
1401 Image::fade (float f)
1403 /* U/V black value for 8-bit colour */
1404 static int const eight_bit_uv = (1 << 7) - 1;
1405 /* U/V black value for 10-bit colour */
1406 static uint16_t const ten_bit_uv = (1 << 9) - 1;
1408 switch (_pixel_format) {
1409 case AV_PIX_FMT_YUV420P:
1412 uint8_t* p = data()[0];
1413 int const lines = sample_size(0).height;
1414 for (int y = 0; y < lines; ++y) {
1416 for (int x = 0; x < line_size()[0]; ++x) {
1417 *q = int(float(*q) * f);
1424 for (int c = 1; c < 3; ++c) {
1425 uint8_t* p = data()[c];
1426 int const lines = sample_size(c).height;
1427 for (int y = 0; y < lines; ++y) {
1429 for (int x = 0; x < line_size()[c]; ++x) {
1430 *q = eight_bit_uv + int((int(*q) - eight_bit_uv) * f);
1440 case AV_PIX_FMT_RGB24:
1443 uint8_t* p = data()[0];
1444 int const lines = sample_size(0).height;
1445 for (int y = 0; y < lines; ++y) {
1447 for (int x = 0; x < line_size()[0]; ++x) {
1448 *q = int (float (*q) * f);
1456 case AV_PIX_FMT_XYZ12LE:
1457 case AV_PIX_FMT_RGB48LE:
1458 /* 16-bit little-endian */
1459 for (int c = 0; c < 3; ++c) {
1460 int const stride_pixels = stride()[c] / 2;
1461 int const line_size_pixels = line_size()[c] / 2;
1462 uint16_t* p = reinterpret_cast<uint16_t*> (data()[c]);
1463 int const lines = sample_size(c).height;
1464 for (int y = 0; y < lines; ++y) {
1466 for (int x = 0; x < line_size_pixels; ++x) {
1467 *q = int (float (*q) * f);
1475 case AV_PIX_FMT_YUV422P10LE:
1479 int const stride_pixels = stride()[0] / 2;
1480 int const line_size_pixels = line_size()[0] / 2;
1481 uint16_t* p = reinterpret_cast<uint16_t*> (data()[0]);
1482 int const lines = sample_size(0).height;
1483 for (int y = 0; y < lines; ++y) {
1485 for (int x = 0; x < line_size_pixels; ++x) {
1486 *q = int(float(*q) * f);
1494 for (int c = 1; c < 3; ++c) {
1495 int const stride_pixels = stride()[c] / 2;
1496 int const line_size_pixels = line_size()[c] / 2;
1497 uint16_t* p = reinterpret_cast<uint16_t*> (data()[c]);
1498 int const lines = sample_size(c).height;
1499 for (int y = 0; y < lines; ++y) {
1501 for (int x = 0; x < line_size_pixels; ++x) {
1502 *q = ten_bit_uv + int((int(*q) - ten_bit_uv) * f);
1513 throw PixelFormatError ("fade()", _pixel_format);
1518 shared_ptr<const Image>
1519 Image::ensure_alignment (shared_ptr<const Image> image, Image::Alignment alignment)
1521 if (image->alignment() == alignment) {
1525 return make_shared<Image>(image, alignment);
1530 Image::memory_used () const
1533 for (int i = 0; i < planes(); ++i) {
1534 m += _stride[i] * sample_size(i).height;
1541 Image::video_range_to_full_range ()
1543 switch (_pixel_format) {
1544 case AV_PIX_FMT_RGB24:
1546 float const factor = 256.0 / 219.0;
1547 uint8_t* p = data()[0];
1548 int const lines = sample_size(0).height;
1549 for (int y = 0; y < lines; ++y) {
1551 for (int x = 0; x < line_size()[0]; ++x) {
1552 *q = clamp(lrintf((*q - 16) * factor), 0L, 255L);
1559 case AV_PIX_FMT_RGB48LE:
1561 float const factor = 65536.0 / 56064.0;
1562 uint16_t* p = reinterpret_cast<uint16_t*>(data()[0]);
1563 int const lines = sample_size(0).height;
1564 for (int y = 0; y < lines; ++y) {
1566 int const line_size_pixels = line_size()[0] / 2;
1567 for (int x = 0; x < line_size_pixels; ++x) {
1568 *q = clamp(lrintf((*q - 4096) * factor), 0L, 65535L);
1571 p += stride()[0] / 2;
1575 case AV_PIX_FMT_GBRP12LE:
1577 float const factor = 4096.0 / 3504.0;
1578 for (int c = 0; c < 3; ++c) {
1579 uint16_t* p = reinterpret_cast<uint16_t*>(data()[c]);
1580 int const lines = sample_size(c).height;
1581 for (int y = 0; y < lines; ++y) {
1583 int const line_size_pixels = line_size()[c] / 2;
1584 for (int x = 0; x < line_size_pixels; ++x) {
1585 *q = clamp(lrintf((*q - 256) * factor), 0L, 4095L);
1593 throw PixelFormatError ("video_range_to_full_range()", _pixel_format);