--- /dev/null
+/*
+ Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "dcpomatic_assert.h"
+#include "exceptions.h"
+#include "image.h"
+#include <jpeglib.h>
+
+#include "i18n.h"
+
+
+using std::shared_ptr;
+
+
+struct destination_mgr
+{
+ jpeg_destination_mgr pub;
+ std::vector<uint8_t>* data;
+};
+
+
+static void
+init_destination (j_compress_ptr)
+{
+
+}
+
+
+/* Empty the output buffer (i.e. make more space for data); we'll make
+ * the output buffer bigger instead.
+ */
+static boolean
+empty_output_buffer (j_compress_ptr state)
+{
+ auto dest = reinterpret_cast<destination_mgr*> (state->dest);
+
+ auto const old_size = dest->data->size();
+ dest->data->resize (old_size * 2);
+
+ dest->pub.next_output_byte = dest->data->data() + old_size;
+ dest->pub.free_in_buffer = old_size;
+
+ return TRUE;
+}
+
+
+static void
+term_destination (j_compress_ptr state)
+{
+ auto dest = reinterpret_cast<destination_mgr*> (state->dest);
+
+ dest->data->resize (dest->data->size() - dest->pub.free_in_buffer);
+}
+
+
+void
+error_exit (j_common_ptr)
+{
+ throw EncodeError (N_("JPEG encoding error"));
+}
+
+
+dcp::ArrayData
+image_as_jpeg (shared_ptr<const Image> image, int quality)
+{
+ if (image->pixel_format() != AV_PIX_FMT_RGB24) {
+ return image_as_jpeg(
+ Image::ensure_alignment(image, Image::Alignment::PADDED)->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, Image::Alignment::PADDED, false),
+ quality
+ );
+ }
+
+ struct jpeg_compress_struct compress;
+ struct jpeg_error_mgr error;
+
+ compress.err = jpeg_std_error (&error);
+ error.error_exit = error_exit;
+ jpeg_create_compress (&compress);
+
+ auto mgr = new destination_mgr;
+ compress.dest = reinterpret_cast<jpeg_destination_mgr*>(mgr);
+
+ mgr->pub.init_destination = init_destination;
+ mgr->pub.empty_output_buffer = empty_output_buffer;
+ mgr->pub.term_destination = term_destination;
+
+ std::vector<uint8_t> data(4096);
+ mgr->data = &data;
+ mgr->pub.next_output_byte = data.data();
+ mgr->pub.free_in_buffer = data.size();
+
+ compress.image_width = image->size().width;
+ compress.image_height = image->size().height;
+ compress.input_components = 3;
+ compress.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults (&compress);
+ jpeg_set_quality (&compress, quality, TRUE);
+
+ jpeg_start_compress (&compress, TRUE);
+
+ JSAMPROW row[1];
+ auto const source_data = image->data()[0];
+ auto const source_stride = image->stride()[0];
+ for (auto y = 0U; y < compress.image_height; ++y) {
+ row[0] = source_data + y * source_stride;
+ jpeg_write_scanlines (&compress, row, 1);
+ }
+
+ jpeg_finish_compress (&compress);
+ delete mgr;
+ jpeg_destroy_compress (&compress);
+
+ return dcp::ArrayData (data.data(), data.size());
+}
+
+
--- /dev/null
+/*
+ Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+dcp::ArrayData image_as_jpeg (std::shared_ptr<const Image> image, int quality);
image_decoder.cc
image_examiner.cc
image_filename_sorter.cc
+ image_jpeg.cc
image_png.cc
image_proxy.cc
j2k_image_proxy.cc
AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE
BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2 BOOST_REGEX
SAMPLERATE POSTPROC TIFF SSH DCP CXML GLIB LZMA XML++
- CURL ZIP BZ2 FONTCONFIG PANGOMM CAIROMM XMLSEC SUB ICU NETTLE PNG LEQM_NRT
+ CURL ZIP BZ2 FONTCONFIG PANGOMM CAIROMM XMLSEC SUB ICU NETTLE PNG JPEG LEQM_NRT
"""
if bld.env.TARGET_OSX:
def build(bld):
uselib = 'BOOST_THREAD BOOST_DATETIME DCP XMLSEC CXML XMLPP AVFORMAT AVFILTER AVCODEC '
uselib += 'AVUTIL SWSCALE SWRESAMPLE POSTPROC CURL BOOST_FILESYSTEM SSH ZIP CAIROMM FONTCONFIG PANGOMM SUB '
- uselib += 'SNDFILE SAMPLERATE BOOST_REGEX ICU NETTLE RTAUDIO PNG LEQM_NRT '
+ uselib += 'SNDFILE SAMPLERATE BOOST_REGEX ICU NETTLE RTAUDIO PNG JPEG LEQM_NRT '
if bld.env.ENABLE_DISK:
if bld.env.TARGET_LINUX:
-Subproject commit 49b6ee6d90d9fc543e829cbe9f071970965263a0
+Subproject commit 0a215a080a51c29a0712275ee4f97c66b5dfad11
#include "lib/image.h"
#include "lib/image_content.h"
#include "lib/image_decoder.h"
+#include "lib/image_jpeg.h"
#include "lib/image_png.h"
#include "lib/ffmpeg_image_proxy.h"
#include "test.h"
}
+BOOST_AUTO_TEST_CASE (as_jpeg_test)
+{
+ auto proxy = make_shared<FFmpegImageProxy>("test/data/3d_test/000001.png");
+ auto image_rgb = proxy->image(Image::Alignment::PADDED).image;
+ auto image_bgr = image_rgb->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_BGRA, Image::Alignment::PADDED, false);
+ image_as_jpeg(image_rgb, 60).write("build/test/as_jpeg_rgb.jpeg");
+ image_as_jpeg(image_bgr, 60).write("build/test/as_jpeg_bgr.jpeg");
+
+ check_image ("test/data/as_jpeg_rgb.jpeg", "build/test/as_jpeg_rgb.jpeg");
+ check_image ("test/data/as_jpeg_bgr.jpeg", "build/test/as_jpeg_bgr.jpeg");
+}
+
+
/* Very dumb test to fade black to make sure it stays black */
static void
fade_test_format_black (AVPixelFormat f, string name)
FFmpegImageProxy check_proxy (check);
auto check_image = check_proxy.image(Image::Alignment::COMPACT).image;
+ BOOST_REQUIRE_EQUAL (ref_image->planes(), check_image->planes());
+
BOOST_REQUIRE_EQUAL (ref_image->pixel_format(), check_image->pixel_format());
- AVPixelFormat const format = ref_image->pixel_format();
+ auto const format = ref_image->pixel_format();
BOOST_REQUIRE (ref_image->size() == check_image->size());
int const width = ref_image->size().width;
}
break;
}
+ case AV_PIX_FMT_YUVJ420P:
+ {
+ for (int c = 0; c < ref_image->planes(); ++c) {
+ for (int y = 0; y < height / ref_image->vertical_factor(c); ++y) {
+ auto p = ref_image->data()[c] + y * ref_image->stride()[c];
+ auto q = check_image->data()[c] + y * check_image->stride()[c];
+ for (int x = 0; x < ref_image->line_size()[c]; ++x) {
+ sum_square += pow((*p++ - *q++), 2);
+ }
+ }
+ }
+ break;
+ }
default:
BOOST_REQUIRE_MESSAGE (false, "unrecognised pixel format " << format);
}
obj = bld(features='cxx cxxprogram')
obj.name = 'unit-tests'
obj.uselib = 'BOOST_TEST BOOST_THREAD BOOST_FILESYSTEM BOOST_DATETIME SNDFILE SAMPLERATE DCP FONTCONFIG CAIROMM PANGOMM XMLPP '
- obj.uselib += 'AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE SWRESAMPLE POSTPROC CXML SUB GLIB CURL SSH XMLSEC BOOST_REGEX ICU NETTLE PNG '
+ obj.uselib += 'AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE SWRESAMPLE POSTPROC CXML SUB GLIB CURL SSH XMLSEC BOOST_REGEX ICU NETTLE PNG JPEG '
obj.uselib += 'LEQM_NRT ZIP '
if bld.env.TARGET_WINDOWS:
obj.uselib += 'WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE '
# libpng
conf.check_cfg(package='libpng', args='--cflags --libs', uselib_store='PNG', mandatory=True)
+ # libjpeg
+ conf.check_cxx(fragment="""
+ #include <cstddef>
+ #include <cstdio>
+ #include <jpeglib.h>
+ int main() { struct jpeg_compress_struct compress; jpeg_create_compress (&compress); return 0; }
+ """,
+ msg='Checking for libjpeg',
+ libpath='/usr/local/lib',
+ lib=['jpeg'],
+ uselib_store='JPEG')
+
# lwext4
if conf.options.enable_disk:
conf.check_cxx(fragment="""