summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmark/rgb_to_xyz.cc2
-rw-r--r--benchmark/rgb_to_xyz_avx2.cc69
-rw-r--r--benchmark/wscript2
-rw-r--r--src/rgb_xyz.cc68
-rw-r--r--src/rgb_xyz.h10
-rw-r--r--test/rgb_xyz_test.cc75
6 files changed, 224 insertions, 2 deletions
diff --git a/benchmark/rgb_to_xyz.cc b/benchmark/rgb_to_xyz.cc
index eeec7f52..bdd1e426 100644
--- a/benchmark/rgb_to_xyz.cc
+++ b/benchmark/rgb_to_xyz.cc
@@ -62,7 +62,7 @@ main ()
shared_ptr<dcp::OpenJPEGImage> xyz;
for (int i = 0; i < trials; ++i) {
- xyz = dcp::rgb_to_xyz_avx2 (rgb.get(), size, size.width * 8, dcp::ColourConversion::srgb_to_xyz());
+ xyz = dcp::rgb_to_xyz (rgb.get(), size, size.width * 8, dcp::ColourConversion::srgb_to_xyz());
}
}
diff --git a/benchmark/rgb_to_xyz_avx2.cc b/benchmark/rgb_to_xyz_avx2.cc
new file mode 100644
index 00000000..eeec7f52
--- /dev/null
+++ b/benchmark/rgb_to_xyz_avx2.cc
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of libdcp.
+
+ libdcp is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ libdcp is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libdcp. If not, see <http://www.gnu.org/licenses/>.
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+#include "openjpeg_image.h"
+#include "rgb_xyz.h"
+#include "colour_conversion.h"
+#include <boost/scoped_array.hpp>
+#include <stdint.h>
+
+using boost::scoped_array;
+using boost::shared_ptr;
+
+int const trials = 256;
+
+int
+main ()
+{
+ srand (1);
+
+ dcp::Size size(1998, 1080);
+
+ scoped_array<uint8_t> rgb (new uint8_t[size.width * size.height * 8]);
+ for (int y = 0; y < size.height; ++y) {
+ uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get() + y * size.width * 8);
+ for (int x = 0; x < size.width; ++x) {
+ for (int c = 0; c < 3; ++c) {
+ *p = (rand() & 0xfff) << 4;
+ ++p;
+ }
+ }
+ }
+
+ shared_ptr<dcp::OpenJPEGImage> xyz;
+ for (int i = 0; i < trials; ++i) {
+ xyz = dcp::rgb_to_xyz_avx2 (rgb.get(), size, size.width * 8, dcp::ColourConversion::srgb_to_xyz());
+ }
+}
+
+
diff --git a/benchmark/wscript b/benchmark/wscript
index 3e748972..0b6994b9 100644
--- a/benchmark/wscript
+++ b/benchmark/wscript
@@ -32,7 +32,7 @@
#
def build(bld):
- for p in ['rgb_to_xyz']:
+ for p in ['rgb_to_xyz', 'rgb_to_xyz_avx2']:
obj = bld(features='cxx cxxprogram')
obj.name = p
obj.uselib = 'BOOST_FILESYSTEM'
diff --git a/src/rgb_xyz.cc b/src/rgb_xyz.cc
index 6db1f1ec..5a6a909d 100644
--- a/src/rgb_xyz.cc
+++ b/src/rgb_xyz.cc
@@ -273,6 +273,74 @@ dcp::combined_rgb_to_xyz (ColourConversion const & conversion, double* matrix)
* DCI_COEFFICIENT * 65535;
}
+
+shared_ptr<dcp::OpenJPEGImage>
+dcp::rgb_to_xyz (
+ uint8_t const * rgb,
+ dcp::Size size,
+ int stride,
+ ColourConversion const & conversion
+ )
+{
+ shared_ptr<OpenJPEGImage> xyz (new OpenJPEGImage (size));
+
+ struct {
+ double r, g, b;
+ } s;
+
+ struct {
+ double x, y, z;
+ } d;
+
+ double const * lut_in = conversion.in()->lut (12, false);
+ double const * lut_out = conversion.out()->lut (16, true);
+
+ /* This is is the product of the RGB to XYZ matrix, the Bradford transform and the DCI companding */
+ double fast_matrix[9];
+ combined_rgb_to_xyz (conversion, fast_matrix);
+
+ int clamped = 0;
+ int* xyz_x = xyz->data (0);
+ int* xyz_y = xyz->data (1);
+ int* xyz_z = xyz->data (2);
+ for (int y = 0; y < size.height; ++y) {
+ uint16_t const * p = reinterpret_cast<uint16_t const *> (rgb + y * stride);
+ for (int x = 0; x < size.width; ++x) {
+
+ /* In gamma LUT (converting 16-bit to 12-bit) */
+ s.r = lut_in[*p++ >> 4];
+ s.g = lut_in[*p++ >> 4];
+ s.b = lut_in[*p++ >> 4];
+
+ /* RGB to XYZ, Bradford transform and DCI companding */
+ d.x = s.r * fast_matrix[0] + s.g * fast_matrix[1] + s.b * fast_matrix[2];
+ d.y = s.r * fast_matrix[3] + s.g * fast_matrix[4] + s.b * fast_matrix[5];
+ d.z = s.r * fast_matrix[6] + s.g * fast_matrix[7] + s.b * fast_matrix[8];
+
+ /* Clamp */
+
+ if (d.x < 0 || d.y < 0 || d.z < 0 || d.x > 65535 || d.y > 65535 || d.z > 65535) {
+ ++clamped;
+ }
+
+ d.x = max (0.0, d.x);
+ d.y = max (0.0, d.y);
+ d.z = max (0.0, d.z);
+ d.x = min (65535.0, d.x);
+ d.y = min (65535.0, d.y);
+ d.z = min (65535.0, d.z);
+
+ /* Out gamma LUT */
+ *xyz_x++ = lrint (lut_out[lrint(d.x)] * 4095);
+ *xyz_y++ = lrint (lut_out[lrint(d.y)] * 4095);
+ *xyz_z++ = lrint (lut_out[lrint(d.z)] * 4095);
+ }
+ }
+
+ return xyz;
+}
+
+
/** @param rgb RGBA data; packed RGBA 16:16:16:16, 48bpp, 16R, 16G, 16B, 16A
* with the 2-byte value for each R/G/B/A component stored as
* little-endian; i.e. AV_PIX_FMT_RGB48LE. A is ignored but necessary for
diff --git a/src/rgb_xyz.h b/src/rgb_xyz.h
index 56a5fd83..59b2400d 100644
--- a/src/rgb_xyz.h
+++ b/src/rgb_xyz.h
@@ -57,6 +57,15 @@ extern void xyz_to_rgb (
boost::optional<NoteHandler> note = boost::optional<NoteHandler> ()
);
+
+extern boost::shared_ptr<OpenJPEGImage> rgb_to_xyz (
+ uint8_t const * rgba,
+ dcp::Size size,
+ int stride,
+ ColourConversion const & conversion
+ );
+
+
extern boost::shared_ptr<OpenJPEGImage> rgb_to_xyz_avx2 (
uint8_t const * rgba,
dcp::Size size,
@@ -64,6 +73,7 @@ extern boost::shared_ptr<OpenJPEGImage> rgb_to_xyz_avx2 (
ColourConversion const & conversion
);
+
extern void combined_rgb_to_xyz (ColourConversion const & conversion, double* matrix);
}
diff --git a/test/rgb_xyz_test.cc b/test/rgb_xyz_test.cc
index 92c48e62..d9c27ae2 100644
--- a/test/rgb_xyz_test.cc
+++ b/test/rgb_xyz_test.cc
@@ -46,6 +46,81 @@ using boost::shared_ptr;
using boost::optional;
using boost::scoped_array;
+
+/** Convert a test image from sRGB to XYZ and check that the transforms are right */
+BOOST_AUTO_TEST_CASE (rgb_xyz_test)
+{
+ srand (0);
+ dcp::Size const size (640, 480);
+
+ scoped_array<uint8_t> rgb (new uint8_t[size.width * size.height * 6]);
+ for (int y = 0; y < size.height; ++y) {
+ uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get() + y * size.width * 6);
+ for (int x = 0; x < size.width; ++x) {
+ /* Write a 12-bit random number for each component */
+ for (int c = 0; c < 3; ++c) {
+ *p = (rand () & 0xfff) << 4;
+ ++p;
+ }
+ }
+ }
+
+ shared_ptr<dcp::OpenJPEGImage> xyz = dcp::rgb_to_xyz (rgb.get(), size, size.width * 6, dcp::ColourConversion::srgb_to_xyz ());
+
+ for (int y = 0; y < size.height; ++y) {
+ uint16_t* p = reinterpret_cast<uint16_t*> (rgb.get() + y * size.width * 6);
+ for (int x = 0; x < size.width; ++x) {
+
+ double cr = *p++ / 65535.0;
+ double cg = *p++ / 65535.0;
+ double cb = *p++ / 65535.0;
+
+ /* Input gamma */
+
+ if (cr < 0.04045) {
+ cr /= 12.92;
+ } else {
+ cr = pow ((cr + 0.055) / 1.055, 2.4);
+ }
+
+ if (cg < 0.04045) {
+ cg /= 12.92;
+ } else {
+ cg = pow ((cg + 0.055) / 1.055, 2.4);
+ }
+
+ if (cb < 0.04045) {
+ cb /= 12.92;
+ } else {
+ cb = pow ((cb + 0.055) / 1.055, 2.4);
+ }
+
+ /* Matrix */
+
+ double cx = cr * 0.4124564 + cg * 0.3575761 + cb * 0.1804375;
+ double cy = cr * 0.2126729 + cg * 0.7151522 + cb * 0.0721750;
+ double cz = cr * 0.0193339 + cg * 0.1191920 + cb * 0.9503041;
+
+ /* Compand */
+
+ cx *= 48 / 52.37;
+ cy *= 48 / 52.37;
+ cz *= 48 / 52.37;
+
+ /* Output gamma */
+
+ cx = pow (cx, 1 / 2.6);
+ cy = pow (cy, 1 / 2.6);
+ cz = pow (cz, 1 / 2.6);
+
+ BOOST_REQUIRE_CLOSE (cx * 4095, xyz->data(0)[y * size.width + x], 1);
+ BOOST_REQUIRE_CLOSE (cy * 4095, xyz->data(1)[y * size.width + x], 1);
+ BOOST_REQUIRE_CLOSE (cz * 4095, xyz->data(2)[y * size.width + x], 1);
+ }
+ }
+}
+
+
/** Convert a test image from sRGB to XYZ using the AVX2 code and check that the transforms are right */
BOOST_AUTO_TEST_CASE (rgb_xyz_test_avx2)
{