X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fimage.cc;h=c0916df89712cba657723b6a13f29c62574cd862;hb=28111007e2e6fd62f5810be780706ae1618bd33f;hp=9dae94f7ce2f3f6fa1f7f5ac7d64a396931a9d0d;hpb=b057363e69b77119137c0c8b07402828096e03aa;p=dcpomatic.git diff --git a/src/lib/image.cc b/src/lib/image.cc index 9dae94f7c..c0916df89 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -52,9 +52,14 @@ using std::cout; using std::cerr; using std::list; using std::runtime_error; -using boost::shared_ptr; +using std::shared_ptr; using dcp::Size; + +/** The memory alignment, in bytes, used for each row of an image if aligment is requested */ +#define ALIGNMENT 64 + + int Image::vertical_factor (int n) const { @@ -67,7 +72,7 @@ Image::vertical_factor (int n) const throw PixelFormatError ("line_factor()", _pixel_format); } - return pow (2.0f, d->log2_chroma_h); + return lrintf(powf(2.0f, d->log2_chroma_h)); } int @@ -82,7 +87,7 @@ Image::horizontal_factor (int n) const throw PixelFormatError ("sample_size()", _pixel_format); } - return pow (2.0f, d->log2_chroma_w); + return lrintf(powf(2.0f, d->log2_chroma_w)); } /** @param n Component index. @@ -117,6 +122,23 @@ Image::planes () const return d->nb_components; } + +static +int +round_width_for_subsampling (int p, AVPixFmtDescriptor const * desc) +{ + return p & ~ ((1 << desc->log2_chroma_w) - 1); +} + + +static +int +round_height_for_subsampling (int p, AVPixFmtDescriptor const * desc) +{ + return p & ~ ((1 << desc->log2_chroma_h) - 1); +} + + /** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size'. * @param crop Amount to crop by. * @param inter_size Size to scale the cropped image to. @@ -153,8 +175,34 @@ Image::crop_scale_window ( shared_ptr out (new Image(out_format, out_size, out_aligned)); out->make_black (); + AVPixFmtDescriptor const * in_desc = av_pix_fmt_desc_get (_pixel_format); + if (!in_desc) { + throw PixelFormatError ("crop_scale_window()", _pixel_format); + } + + /* Round down so that we crop only the number of pixels that is straightforward + * considering any subsampling. + */ + Crop corrected_crop( + round_width_for_subsampling(crop.left, in_desc), + round_width_for_subsampling(crop.right, in_desc), + round_height_for_subsampling(crop.top, in_desc), + round_height_for_subsampling(crop.bottom, in_desc) + ); + + /* Also check that we aren't cropping more image than there actually is */ + if ((corrected_crop.left + corrected_crop.right) >= (size().width - 4)) { + corrected_crop.left = 0; + corrected_crop.right = size().width - 4; + } + + if ((corrected_crop.top + corrected_crop.bottom) >= (size().height - 4)) { + corrected_crop.top = 0; + corrected_crop.bottom = size().height - 4; + } + /* Size of the image after any crop */ - dcp::Size const cropped_size = crop.apply (size ()); + dcp::Size const cropped_size = corrected_crop.apply (size()); /* Scale context for a scale from cropped_size to inter_size */ struct SwsContext* scale_context = sws_getContext ( @@ -167,8 +215,8 @@ Image::crop_scale_window ( throw runtime_error (N_("Could not allocate SwsContext")); } - DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUV_TO_RGB_COUNT); - int const lut[dcp::YUV_TO_RGB_COUNT] = { + DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUVToRGB::COUNT); + int const lut[static_cast(dcp::YUVToRGB::COUNT)] = { SWS_CS_ITU601, SWS_CS_ITU709 }; @@ -186,40 +234,32 @@ Image::crop_scale_window ( */ sws_setColorspaceDetails ( scale_context, - sws_getCoefficients (lut[yuv_to_rgb]), video_range == VIDEO_RANGE_VIDEO ? 0 : 1, - sws_getCoefficients (lut[yuv_to_rgb]), out_video_range == VIDEO_RANGE_VIDEO ? 0 : 1, + sws_getCoefficients (lut[static_cast(yuv_to_rgb)]), video_range == VIDEO_RANGE_VIDEO ? 0 : 1, + sws_getCoefficients (lut[static_cast(yuv_to_rgb)]), out_video_range == VIDEO_RANGE_VIDEO ? 0 : 1, 0, 1 << 16, 1 << 16 ); - AVPixFmtDescriptor const * in_desc = av_pix_fmt_desc_get (_pixel_format); - if (!in_desc) { - throw PixelFormatError ("crop_scale_window()", _pixel_format); - } - /* Prepare input data pointers with crop */ uint8_t* scale_in_data[planes()]; for (int c = 0; c < planes(); ++c) { - /* To work out the crop in bytes, start by multiplying - the crop by the (average) bytes per pixel. Then - round down so that we don't crop a subsampled pixel until - we've cropped all of its Y-channel pixels. - */ - int const x = lrintf (bytes_per_pixel(c) * crop.left) & ~ ((int) in_desc->log2_chroma_w); - scale_in_data[c] = data()[c] + x + stride()[c] * (crop.top / vertical_factor(c)); + int const x = lrintf(bytes_per_pixel(c) * corrected_crop.left); + scale_in_data[c] = data()[c] + x + stride()[c] * (corrected_crop.top / vertical_factor(c)); } - /* Corner of the image within out_size */ - Position const corner ((out_size.width - inter_size.width) / 2, (out_size.height - inter_size.height) / 2); - AVPixFmtDescriptor const * out_desc = av_pix_fmt_desc_get (out_format); if (!out_desc) { throw PixelFormatError ("crop_scale_window()", out_format); } + /* Corner of the image within out_size */ + Position const corner ( + round_width_for_subsampling((out_size.width - inter_size.width) / 2, out_desc), + round_height_for_subsampling((out_size.height - inter_size.height) / 2, out_desc) + ); + uint8_t* scale_out_data[out->planes()]; for (int c = 0; c < out->planes(); ++c) { - /* See the note in the crop loop above */ - int const x = lrintf (out->bytes_per_pixel(c) * corner.x) & ~ ((int) out_desc->log2_chroma_w); + int const x = lrintf(out->bytes_per_pixel(c) * corner.x); scale_out_data[c] = out->data()[c] + x + out->stride()[c] * (corner.y / out->vertical_factor(c)); } @@ -232,7 +272,7 @@ Image::crop_scale_window ( sws_freeContext (scale_context); - if (crop != Crop() && cropped_size == inter_size && _pixel_format == out_format) { + if (corrected_crop != Crop() && cropped_size == inter_size) { /* We are cropping without any scaling or pixel format conversion, so FFmpeg may have left some data behind in our image. Clear it out. It may get to the point where we should just stop trying to be clever with cropping. @@ -272,8 +312,8 @@ Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_fo (fast ? SWS_FAST_BILINEAR : SWS_BICUBIC) | SWS_ACCURATE_RND, 0, 0, 0 ); - DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUV_TO_RGB_COUNT); - int const lut[dcp::YUV_TO_RGB_COUNT] = { + DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUVToRGB::COUNT); + int const lut[static_cast(dcp::YUVToRGB::COUNT)] = { SWS_CS_ITU601, SWS_CS_ITU709 }; @@ -291,8 +331,8 @@ Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_fo */ sws_setColorspaceDetails ( scale_context, - sws_getCoefficients (lut[yuv_to_rgb]), 0, - sws_getCoefficients (lut[yuv_to_rgb]), 0, + sws_getCoefficients (lut[static_cast(yuv_to_rgb)]), 0, + sws_getCoefficients (lut[static_cast(yuv_to_rgb)]), 0, 0, 1 << 16, 1 << 16 ); @@ -640,7 +680,7 @@ Image::alpha_blend (shared_ptr other, Position position) } case AV_PIX_FMT_YUV420P: { - shared_ptr yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, false, false); + shared_ptr yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, false, false); dcp::Size const ts = size(); dcp::Size const os = yuv->size(); for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) { @@ -675,7 +715,7 @@ Image::alpha_blend (shared_ptr other, Position position) } case AV_PIX_FMT_YUV420P10: { - shared_ptr yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, false, false); + shared_ptr yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, false, false); dcp::Size const ts = size(); dcp::Size const os = yuv->size(); for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) { @@ -710,7 +750,7 @@ Image::alpha_blend (shared_ptr other, Position position) } case AV_PIX_FMT_YUV422P10LE: { - shared_ptr yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, false, false); + shared_ptr yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, false, false); dcp::Size const ts = size(); dcp::Size const os = yuv->size(); for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) { @@ -838,7 +878,7 @@ Image::bytes_per_pixel (int c) const * * @param p Pixel format. * @param s Size in pixels. - * @param aligned true to make each row of this image aligned to a 32-byte boundary. + * @param aligned true to make each row of this image aligned to a ALIGNMENT-byte boundary. */ Image::Image (AVPixelFormat p, dcp::Size s, bool aligned) : _size (s) @@ -862,7 +902,7 @@ Image::allocate () for (int i = 0; i < planes(); ++i) { _line_size[i] = ceil (_size.width * bytes_per_pixel(i)); - _stride[i] = stride_round_up (i, _line_size, _aligned ? 32 : 1); + _stride[i] = stride_round_up (i, _line_size, _aligned ? ALIGNMENT : 1); /* The assembler function ff_rgb24ToY_avx (in libswscale/x86/input.asm) uses a 16-byte fetch to read three bytes (R/G/B) of image data. @@ -875,7 +915,7 @@ Image::allocate () Further to the above, valgrind is now telling me that ff_rgb24ToY_ssse3 over-reads by more then _avx. I can't follow the code to work out how much, - so I'll just over-allocate by 32 bytes and have done with it. Empirical + so I'll just over-allocate by ALIGNMENT bytes and have done with it. Empirical testing suggests that it works. In addition to these concerns, we may read/write as much as a whole extra line @@ -901,18 +941,18 @@ Image::allocate () |XXXwrittenXXX|<------line-size------------->|XXXwrittenXXXXXXwrittenXXX ^^^^ out of bounds */ - _data[i] = (uint8_t *) wrapped_av_malloc (_stride[i] * (sample_size(i).height + 1) + 32); + _data[i] = (uint8_t *) wrapped_av_malloc (_stride[i] * (sample_size(i).height + 1) + ALIGNMENT); #if HAVE_VALGRIND_MEMCHECK_H /* The data between the end of the line size and the stride is undefined but processed by libswscale, causing lots of valgrind errors. Mark it all defined to quell these errors. */ - VALGRIND_MAKE_MEM_DEFINED (_data[i], _stride[i] * (sample_size(i).height + 1) + 32); + VALGRIND_MAKE_MEM_DEFINED (_data[i], _stride[i] * (sample_size(i).height + 1) + ALIGNMENT); #endif } } Image::Image (Image const & other) - : boost::enable_shared_from_this(other) + : std::enable_shared_from_this(other) , _size (other._size) , _pixel_format (other._pixel_format) , _aligned (other._aligned) @@ -1293,7 +1333,7 @@ Image::as_png () const DCPOMATIC_ASSERT (bytes_per_pixel(0) == 4); DCPOMATIC_ASSERT (planes() == 1); if (pixel_format() != AV_PIX_FMT_RGBA) { - return convert_pixel_format(dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_RGBA, true, false)->as_png(); + return convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, true, false)->as_png(); } /* error handling? */