using dcp::Size;
int
-Image::line_factor (int n) const
+Image::vertical_factor (int n) const
{
if (n == 0) {
return 1;
return pow (2.0f, d->log2_chroma_h);
}
-/** @param n Component index.
- * @return Number of samples (i.e. pixels, unless sub-sampled) in each direction for this component.
- */
-dcp::Size
-Image::sample_size (int n) const
+int
+Image::horizontal_factor (int n) const
{
int horizontal_factor = 1;
if (n > 0) {
}
horizontal_factor = pow (2.0f, d->log2_chroma_w);
}
+ return horizontal_factor;
+}
+/** @param n Component index.
+ * @return Number of samples (i.e. pixels, unless sub-sampled) in each direction for this component.
+ */
+dcp::Size
+Image::sample_size (int n) const
+{
return dcp::Size (
- lrint (ceil (static_cast<double>(size().width) / horizontal_factor)),
- lrint (ceil (static_cast<double>(size().height) / line_factor (n)))
+ lrint (ceil (static_cast<double>(size().width) / horizontal_factor (n))),
+ lrint (ceil (static_cast<double>(size().height) / vertical_factor (n)))
);
}
we've cropped all of its Y-channel pixels.
*/
int const x = lrintf (bytes_per_pixel(c) * crop.left) & ~ ((int) desc->log2_chroma_w);
- scale_in_data[c] = data()[c] + x + stride()[c] * (crop.top / line_factor(c));
+ scale_in_data[c] = data()[c] + x + stride()[c] * (crop.top / vertical_factor(c));
}
/* Corner of the image within out_size */
}
break;
}
+ case AV_PIX_FMT_YUV420P:
+ case AV_PIX_FMT_YUV420P10:
+ {
+ shared_ptr<Image> yuv = other->scale (other->size(), dcp::YUV_TO_RGB_REC709, _pixel_format, false, false);
+
+ for (int i = 0; i < 3; ++i) {
+ dcp::Size const tsize = sample_size(i);
+ dcp::Size const osize = yuv->sample_size(i);
+ int const tbpp = ceil (bytes_per_pixel(i) / horizontal_factor(i));
+ int const obpp = ceil (yuv->bytes_per_pixel(i) / yuv->horizontal_factor(i));
+ int const abpp = other->bytes_per_pixel(0);
+ start_tx /= horizontal_factor (i);
+ start_ty /= vertical_factor (i);
+ start_ox /= yuv->horizontal_factor (i);
+ start_oy /= yuv->vertical_factor (i);
+ for (int ty = start_ty, oy = start_oy; ty < tsize.height && oy < osize.height; ++ty, ++oy) {
+ /* this image */
+ uint8_t* tp = data()[i] + ty * stride()[i] + start_tx * tbpp;
+ /* overlay image */
+ uint8_t* op = yuv->data()[i] + oy * yuv->stride()[i];
+ /* original RGBA for alpha channel */
+ uint8_t* ap = other->data()[0] + oy * other->stride()[0];
+ for (int tx = start_tx, ox = start_ox; tx < tsize.width && ox < osize.width; ++tx, ++ox) {
+ float const alpha = float (ap[3]) / 255;
+ *tp = *op * alpha + *tp * (1 - alpha);
+ tp += tbpp;
+ op += obpp;
+ ap += abpp;
+ }
+ }
+ }
+ break;
+ }
default:
throw PixelFormatError ("alpha_blend()", _pixel_format);
}
bool aligned () const;
int planes () const;
- int line_factor (int) const;
+ int vertical_factor (int) const;
+ int horizontal_factor (int) const;
dcp::Size sample_size (int) const;
boost::shared_ptr<Image> scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool aligned, bool fast) const;
/*
- Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
* @see test/make_black_test.cc, test/pixel_formats_test.cc
*/
-#include <boost/test/unit_test.hpp>
-#include <Magick++.h>
#include "lib/image.h"
+#include "lib/magick_image_proxy.h"
+#include "test.h"
+#include <Magick++.h>
+#include <boost/test/unit_test.hpp>
#include <iostream>
using std::string;
delete u;
}
-/** Test Image::alpha_blend */
-BOOST_AUTO_TEST_CASE (alpha_blend_test)
+void
+alpha_blend_test_one (AVPixelFormat format, string suffix)
{
- int const stride = 48 * 4;
+ shared_ptr<MagickImageProxy> proxy (new MagickImageProxy (private_data / "prophet_frame.tiff"));
+ shared_ptr<Image> raw = proxy->image();
+ shared_ptr<Image> background = raw->scale (raw->size(), dcp::YUV_TO_RGB_REC709, format, true, false);
- shared_ptr<Image> A (new Image (AV_PIX_FMT_RGBA, dcp::Size (48, 48), false));
- A->make_black ();
- uint8_t* a = A->data()[0];
+ shared_ptr<Image> overlay (new Image (AV_PIX_FMT_RGBA, raw->size(), true));
+ overlay->make_transparent ();
- for (int y = 0; y < 48; ++y) {
- uint8_t* p = a + y * stride;
- for (int x = 0; x < 16; ++x) {
+ for (int y = 0; y < 128; ++y) {
+ uint8_t* p = overlay->data()[0] + y * overlay->stride()[0];
+ for (int x = 0; x < 128; ++x) {
p[x * 4] = 255;
- p[(x + 16) * 4 + 1] = 255;
- p[(x + 32) * 4 + 2] = 255;
+ p[x * 4 + 3] = 255;
}
}
- shared_ptr<Image> B (new Image (AV_PIX_FMT_RGBA, dcp::Size (48, 48), true));
- B->make_transparent ();
- uint8_t* b = B->data()[0];
-
- for (int y = 32; y < 48; ++y) {
- uint8_t* p = b + y * stride;
- for (int x = 0; x < 48; ++x) {
- p[x * 4] = 255;
+ for (int y = 128; y < 256; ++y) {
+ uint8_t* p = overlay->data()[0] + y * overlay->stride()[0];
+ for (int x = 0; x < 128; ++x) {
p[x * 4 + 1] = 255;
- p[x * 4 + 2] = 255;
p[x * 4 + 3] = 255;
}
}
- A->alpha_blend (B, Position<int> (0, 0));
-
- for (int y = 0; y < 32; ++y) {
- uint8_t* p = a + y * stride;
- for (int x = 0; x < 16; ++x) {
- BOOST_CHECK_EQUAL (p[x * 4], 255);
- BOOST_CHECK_EQUAL (p[(x + 16) * 4 + 1], 255);
- BOOST_CHECK_EQUAL (p[(x + 32) * 4 + 2], 255);
+ for (int y = 256; y < 384; ++y) {
+ uint8_t* p = overlay->data()[0] + y * overlay->stride()[0];
+ for (int x = 0; x < 128; ++x) {
+ p[x * 4 + 2] = 255;
+ p[x * 4 + 3] = 255;
}
}
- for (int y = 32; y < 48; ++y) {
- uint8_t* p = a + y * stride;
- for (int x = 0; x < 48; ++x) {
- BOOST_CHECK_EQUAL (p[x * 4], 255);
- BOOST_CHECK_EQUAL (p[x * 4 + 1], 255);
- BOOST_CHECK_EQUAL (p[x * 4 + 2], 255);
- BOOST_CHECK_EQUAL (p[x * 4 + 3], 255);
- }
- }
+ background->alpha_blend (overlay, Position<int> (0, 0));
+
+ shared_ptr<Image> save = background->scale (background->size(), dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_RGB24, false, false);
+
+ write_image (save, "build/test/image_test_" + suffix + ".png", "RGB");
+ check_image ("build/test/image_test_" + suffix + ".png", private_data / ("image_test_" + suffix + ".png"));
+}
+
+/** Test Image::alpha_blend */
+BOOST_AUTO_TEST_CASE (alpha_blend_test)
+{
+ alpha_blend_test_one (AV_PIX_FMT_RGBA, "rgba");
+ alpha_blend_test_one (AV_PIX_FMT_YUV420P, "yuv420p");
}
/** Test merge (list<PositionImage>) with a single image */
}
}
+void
+check_image (boost::filesystem::path ref, boost::filesystem::path check)
+{
+#ifdef DCPOMATIC_IMAGE_MAGICK
+ using namespace MagickCore;
+#else
+ using namespace MagickLib;
+#endif
+
+ Magick::Image ref_image;
+ ref_image.read (ref.string ());
+ Magick::Image check_image;
+ check_image.read (check.string ());
+ DCPOMATIC_ASSERT (ref_image.compare (check_image));
+}
+
void
check_file (boost::filesystem::path ref, boost::filesystem::path check)
{
}
void
-write_image (shared_ptr<const Image> image, boost::filesystem::path file)
+write_image (shared_ptr<const Image> image, boost::filesystem::path file, string format)
{
#ifdef DCPOMATIC_IMAGE_MAGICK
using namespace MagickCore;
using namespace MagickLib;
#endif
- Magick::Image m (image->size().width, image->size().height, "ARGB", CharPixel, (void *) image->data()[0]);
+ Magick::Image m (image->size().width, image->size().height, format.c_str(), CharPixel, (void *) image->data()[0]);
m.write (file.string ());
}
extern void check_audio_file (boost::filesystem::path ref, boost::filesystem::path check);
extern void check_xml (boost::filesystem::path, boost::filesystem::path, std::list<std::string>);
extern void check_file (boost::filesystem::path, boost::filesystem::path);
+extern void check_image (boost::filesystem::path, boost::filesystem::path);
extern boost::filesystem::path test_film_dir (std::string);
-extern void write_image (boost::shared_ptr<const Image> image, boost::filesystem::path file);
+extern void write_image (boost::shared_ptr<const Image> image, boost::filesystem::path file, std::string format);