summaryrefslogtreecommitdiff
path: root/src/lib/j2k_image_proxy.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/j2k_image_proxy.h')
-rw-r--r--src/lib/j2k_image_proxy.h183
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;