the resulting images; fixes #1274.
2018-04-13 Carl Hetherington <cth@carlh.net>
+ * Fix incorrect preview crop with DCP sources when the preview is smaller than half
+ of the DCP's resolution (#1274).
+
* Update encoding server list when one goes away (#1176).
* Add servers with bad server-link versions in the list (#982).
/** @param note Handler for any notes that occur.
* @param size Size that the returned image will be scaled to, in case this
* can be used as an optimisation.
- * @return Image (which must be aligned)
+ * @return Image (which must be aligned) and log2 of any scaling down that has
+ * already been applied to the image; e.g. if the the image is already half the size
+ * of the original, the second part of the return value will be 1.
*/
- virtual boost::shared_ptr<Image> image (
+ virtual std::pair<boost::shared_ptr<Image>, int> image (
boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> (),
boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
) const = 0;
virtual bool same (boost::shared_ptr<const ImageProxy>) const = 0;
/** Do any useful work that would speed up a subsequent call to ::image().
* This method may be called in a different thread to image().
+ * @return log2 of any scaling down that will be applied to the image.
*/
- virtual void prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const {}
+ int prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const { return 0; }
virtual AVPixelFormat pixel_format () const = 0;
virtual size_t memory_used () const = 0;
};
#include "j2k_image_proxy.h"
#include "dcpomatic_socket.h"
#include "image.h"
+#include "dcpomatic_assert.h"
#include <dcp/raw_convert.h>
#include <dcp/openjpeg_image.h>
#include <dcp/mono_picture_frame.h>
using std::string;
using std::cout;
using std::max;
+using std::pair;
+using std::make_pair;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
socket->read (_data.data().get (), _data.size ());
}
-void
+int
J2KImageProxy::prepare (optional<dcp::Size> target_size) const
{
boost::mutex::scoped_lock lm (_mutex);
if (_decompressed && target_size == _target_size) {
- return;
+ DCPOMATIC_ASSERT (_reduce);
+ return *_reduce;
}
int reduce = 0;
}
_target_size = target_size;
+ _reduce = reduce;
+
+ return reduce;
}
-shared_ptr<Image>
+pair<shared_ptr<Image>, int>
J2KImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size> target_size) const
{
- prepare (target_size);
+ int const reduce = prepare (target_size);
shared_ptr<Image> image (new Image (_pixel_format, _decompressed->size(), true));
}
}
- return image;
+ return make_pair (image, reduce);
}
void
J2KImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
- boost::shared_ptr<Image> image (
+ std::pair<boost::shared_ptr<Image>, int> image (
boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> (),
boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
) const;
void send_binary (boost::shared_ptr<Socket>) const;
/** @return true if our image is definitely the same as another, false if it is probably not */
bool same (boost::shared_ptr<const ImageProxy>) const;
- void prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const;
+ int prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const;
AVPixelFormat pixel_format () const {
return _pixel_format;
}
boost::optional<dcp::Eye> _eye;
mutable boost::shared_ptr<dcp::OpenJPEGImage> _decompressed;
mutable boost::optional<dcp::Size> _target_size;
+ mutable boost::optional<int> _reduce;
AVPixelFormat _pixel_format;
mutable boost::mutex _mutex;
boost::optional<int> _forced_reduction;
using std::string;
using std::cout;
+using std::pair;
+using std::make_pair;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
delete[] data;
}
-shared_ptr<Image>
+pair<shared_ptr<Image>, int>
MagickImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size>) const
{
boost::mutex::scoped_lock lm (_mutex);
if (_image) {
- return _image;
+ return make_pair (_image, 0);
}
Magick::Image* magick_image = 0;
delete magick_image;
- return _image;
+ return make_pair (_image, 0);
}
void
explicit MagickImageProxy (boost::filesystem::path);
MagickImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
- boost::shared_ptr<Image> image (
+ std::pair<boost::shared_ptr<Image>, int> image (
boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> (),
boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
) const;
using std::string;
using std::cout;
+using std::pair;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
shared_ptr<Image>
PlayerVideo::image (dcp::NoteHandler note, function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const
{
- shared_ptr<Image> im = _in->image (optional<dcp::NoteHandler> (note), _inter_size);
+ pair<shared_ptr<Image>, int> prox = _in->image (optional<dcp::NoteHandler> (note), _inter_size);
+ shared_ptr<Image> im = prox.first;
+ int const reduce = prox.second;
Crop total_crop = _crop;
switch (_part) {
break;
}
+ if (reduce > 0) {
+ /* Scale the crop down to account for the scaling that has already happened in ImageProxy::image */
+ int const r = pow(2, reduce);
+ total_crop.left /= r;
+ total_crop.right /= r;
+ total_crop.top /= r;
+ total_crop.bottom /= r;
+ }
+
dcp::YUVToRGB yuv_to_rgb = dcp::YUV_TO_RGB_REC601;
if (_colour_conversion) {
yuv_to_rgb = _colour_conversion.get().yuv_to_rgb();
/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2018 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "i18n.h"
using std::string;
+using std::pair;
+using std::make_pair;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
using boost::optional;
_image->read_from_socket (socket);
}
-shared_ptr<Image>
+pair<shared_ptr<Image>, int>
RawImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size>) const
{
- return _image;
+ return make_pair (_image, 0);
}
void
return false;
}
- return (*_image.get()) == (*rp->image().get());
+ return (*_image.get()) == (*rp->image().first.get());
}
AVPixelFormat
explicit RawImageProxy (boost::shared_ptr<Image>);
RawImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
- boost::shared_ptr<Image> image (
+ std::pair<boost::shared_ptr<Image>, int> image (
boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> (),
boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
) const;
alpha_blend_test_one (AVPixelFormat format, string suffix)
{
shared_ptr<MagickImageProxy> proxy (new MagickImageProxy (private_data / "prophet_frame.tiff"));
- shared_ptr<Image> raw = proxy->image();
+ shared_ptr<Image> raw = proxy->image().first;
shared_ptr<Image> background = raw->convert_pixel_format (dcp::YUV_TO_RGB_REC709, format, true, false);
shared_ptr<Image> overlay (new Image (AV_PIX_FMT_BGRA, dcp::Size(431, 891), true));
BOOST_AUTO_TEST_CASE (crop_scale_window_test)
{
shared_ptr<MagickImageProxy> proxy(new MagickImageProxy("test/data/flat_red.png"));
- shared_ptr<Image> raw = proxy->image();
+ shared_ptr<Image> raw = proxy->image().first;
shared_ptr<Image> out = raw->crop_scale_window(Crop(), dcp::Size(1998, 836), dcp::Size(1998, 1080), dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_YUV420P, true, false);
shared_ptr<Image> save = out->scale(dcp::Size(1998, 1080), dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_RGB24, false, false);
write_image(save, "build/test/crop_scale_window_test.png", "RGB");