diff options
| author | Carl Hetherington <cth@carlh.net> | 2024-08-02 18:32:50 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2024-08-02 18:32:50 +0200 |
| commit | 30253cbddbe1167d1b36d4f4185681ceeb6e26ba (patch) | |
| tree | 86ffb12b9e48d9d74512aa2b2c3952e25cdf9bad /src/lib/j2k_image_proxy.h | |
| parent | 95abcc7d18997c1391423d15506bf03ab20997e6 (diff) | |
WIP: hacks.shared-ptr
Diffstat (limited to 'src/lib/j2k_image_proxy.h')
| -rw-r--r-- | src/lib/j2k_image_proxy.h | 183 |
1 files changed, 167 insertions, 16 deletions
diff --git a/src/lib/j2k_image_proxy.h b/src/lib/j2k_image_proxy.h index 1d2d5cc21..0343f929c 100644 --- a/src/lib/j2k_image_proxy.h +++ b/src/lib/j2k_image_proxy.h @@ -19,8 +19,11 @@ */ +#include "dcpomatic_assert.h" #include "image_proxy.h" #include <dcp/array_data.h> +#include <dcp/mono_j2k_picture_frame.h> +#include <dcp/types.h> #include <dcp/util.h> #include <boost/thread/mutex.hpp> @@ -31,41 +34,181 @@ namespace dcp { } +template <class Frame> class J2KImageProxy : public ImageProxy { public: - J2KImageProxy (boost::filesystem::path path, dcp::Size, AVPixelFormat pixel_format); + J2KImageProxy(boost::filesystem::path path, dcp::Size, AVPixelFormat pixel_format) + : _data(path) + , _size (size) + , _pixel_format (pixel_format) + , _error (false) + { + /* ::image assumes 16bpp */ + DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE); + } J2KImageProxy ( - std::shared_ptr<const dcp::MonoJ2KPictureFrame> frame, + dcp::MonoJ2KPictureFrame frame, dcp::Size, AVPixelFormat pixel_format, boost::optional<int> forced_reduction - ); + ) + : _data (frame) + , _size (size) + , _pixel_format (pixel_format) + , _forced_reduction (forced_reduction) + , _error (false) + { + /* ::image assumes 16bpp */ + DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE); + } J2KImageProxy ( - std::shared_ptr<const dcp::StereoJ2KPictureFrame> frame, - dcp::Size, - dcp::Eye, + dcp::StereoJ2KPictureFrame frame, + dcp::Size size, + dcp::Eye eye, AVPixelFormat pixel_format, boost::optional<int> forced_reduction - ); + ) + : _data (eye == dcp::Eye::LEFT ? frame->left() : frame->right()) + , _size (size) + , _eye (eye) + , _pixel_format (pixel_format) + , _forced_reduction (forced_reduction) + , _error (false) + { + /* ::image assumes 16bpp */ + DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE); + } - J2KImageProxy (std::shared_ptr<cxml::Node> xml, std::shared_ptr<Socket> socket); + + J2KImageProxy (std::shared_ptr<cxml::Node> xml, std::shared_ptr<Socket> socket) + { + _size = dcp::Size (xml->number_child<int>("Width"), xml->number_child<int>("Height")); + if (xml->optional_number_child<int>("Eye")) { + _eye = static_cast<dcp::Eye>(xml->number_child<int>("Eye")); + } + auto data = make_shared<ArrayData>(xml->number_child<int>("Size")); + /* This only matters when we are using J2KImageProxy for the preview, which + will never use this constructor (which is only used for passing data to + encode servers). So we can put anything in here. It's a bit of a hack. + */ + _pixel_format = AV_PIX_FMT_XYZ12LE; + socket->read(data->data(), data->size()); + _data = data; + } /* For tests */ - J2KImageProxy (dcp::ArrayData data, dcp::Size size, AVPixelFormat pixel_format); + J2KImageProxy(dcp::ArrayData data, dcp::Size size, AVPixelFormat pixel_format) + : _data(data) + , _size(size) + , _pixel_format(pixel_format) + , _error(false) + { + /* ::image assumes 16bpp */ + DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE); + } Result image ( Image::Alignment alignment, boost::optional<dcp::Size> size = boost::optional<dcp::Size> () - ) const override; + ) const override + { + int const r = prepare (alignment, target_size); + + /* I think this is safe without a lock on mutex. _image is guaranteed to be + set up when prepare() has happened. + */ + return Result(_image, r, _error); + } + + void add_metadata(xmlpp::Element*) const override + { + cxml::add_text_child(element, "Type", N_("J2K")); + cxml::add_text_child(element, "Width", raw_convert<string>(_size.width)); + cxml::add_text_child(element, "Height", raw_convert<string>(_size.height)); + if (_eye) { + cxml::add_text_child(element, "Eye", raw_convert<string>(static_cast<int>(_eye.get()))); + } + cxml::add_text_child(element, "Size", raw_convert<string>(_data.size())); + } + + void write_to_socket (std::shared_ptr<Socket> override) const override + { + socket->write(_data.data(), _data.size()); + } - void add_metadata(xmlpp::Element*) const override; - void write_to_socket (std::shared_ptr<Socket> override) const override; /** @return true if our image is definitely the same as another, false if it is probably not */ - bool same (std::shared_ptr<const ImageProxy>) const override; - int prepare (Image::Alignment alignment, boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const override; + bool same(std::shared_ptr<const ImageProxy>) const override + { + auto jp = dynamic_pointer_cast<const J2KImageProxy>(other); + if (!jp) { + return false; + } + + return _data == *jp->_data; + } + + int prepare(Image::Alignment alignment, boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const override + { + boost::mutex::scoped_lock lm (_mutex); + + if (_image && target_size == _target_size) { + DCPOMATIC_ASSERT (_reduce); + return *_reduce; + } + + int reduce = 0; + + if (_forced_reduction) { + reduce = *_forced_reduction; + } else { + while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) { + ++reduce; + } + + --reduce; + reduce = max (0, reduce); + } + + try { + /* XXX: should check that potentially trashing _data here doesn't matter */ + auto decompressed = dcp::decompress_j2k (const_cast<uint8_t*>(_data->data()), _data->size(), reduce); + _image = make_shared<Image>(_pixel_format, decompressed->size(), alignment); + + int const shift = 16 - decompressed->precision (0); + + /* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming + the data is 12-bit either way. + */ + + int const width = decompressed->size().width; + + int p = 0; + int* decomp_0 = decompressed->data (0); + int* decomp_1 = decompressed->data (1); + int* decomp_2 = decompressed->data (2); + for (int y = 0; y < decompressed->size().height; ++y) { + auto q = reinterpret_cast<uint16_t *>(_image->data()[0] + y * _image->stride()[0]); + for (int x = 0; x < width; ++x) { + *q++ = decomp_0[p] << shift; + *q++ = decomp_1[p] << shift; + *q++ = decomp_2[p] << shift; + ++p; + } + } + } catch (dcp::J2KDecompressionError& e) { + _image = make_shared<Image>(_pixel_format, _size, alignment); + _image->make_black (); + _error = true; + } + + _target_size = target_size; + _reduce = reduce; + + return reduce; + } std::shared_ptr<const dcp::Data> j2k () const { return _data; @@ -79,10 +222,18 @@ public: return _eye; } - size_t memory_used () const override; + size_t memory_used () const override + { + size_t m = _data->size(); + if (_image) { + /* 3 components, 16-bits per pixel */ + m += 3 * 2 * _image->size().width * _image->size().height; + } + return m; + } private: - std::shared_ptr<const dcp::Data> _data; + Frame _data; dcp::Size _size; boost::optional<dcp::Eye> _eye; mutable std::shared_ptr<Image> _image; |
