/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
#include <iostream>
+#include <openssl/md5.h>
extern "C" {
#include <libswscale/swscale.h>
#include <libavutil/pixfmt.h>
#include <libavutil/pixdesc.h>
-#include <libpostproc/postprocess.h>
}
#include "image.h"
#include "exceptions.h"
#include "scaler.h"
#include "timer.h"
+#include "rect.h"
+
+#include "i18n.h"
using std::string;
using std::min;
using std::cout;
+using std::cerr;
+using std::list;
+using std::stringstream;
using boost::shared_ptr;
-using libdcp::Size;
+using dcp::Size;
int
Image::line_factor (int n) const
/** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size' */
shared_ptr<Image>
-Image::crop_scale_window (Crop crop, libdcp::Size inter_size, libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
+Image::crop_scale_window (Crop crop, dcp::Size inter_size, dcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
{
assert (scaler);
/* Empirical testing suggests that sws_scale() will crash if
*/
assert (aligned ());
+ assert (out_size.width >= inter_size.width);
+ assert (out_size.height >= inter_size.height);
+
+ /* Here's an image of out_size */
shared_ptr<Image> out (new Image (out_format, out_size, out_aligned));
out->make_black ();
-
- libdcp::Size cropped_size = crop.apply (size ());
+ /* Size of the image after any crop */
+ dcp::Size const cropped_size = crop.apply (size ());
+
+ /* Scale context for a scale from cropped_size to inter_size */
struct SwsContext* scale_context = sws_getContext (
cropped_size.width, cropped_size.height, pixel_format(),
inter_size.width, inter_size.height, out_format,
scaler->ffmpeg_id (), 0, 0, 0
);
+ if (!scale_context) {
+ throw StringError (N_("Could not allocate SwsContext"));
+ }
+
+ /* Prepare input data pointers with crop */
uint8_t* scale_in_data[components()];
for (int c = 0; c < components(); ++c) {
scale_in_data[c] = data()[c] + int (rint (bytes_per_pixel(c) * crop.left)) + stride()[c] * (crop.top / line_factor(c));
}
+ /* Corner of the image within out_size */
Position<int> const corner ((out_size.width - inter_size.width) / 2, (out_size.height - inter_size.height) / 2);
- uint8_t* scale_out_data[components()];
- for (int c = 0; c < components(); ++c) {
+ uint8_t* scale_out_data[out->components()];
+ for (int c = 0; c < out->components(); ++c) {
scale_out_data[c] = out->data()[c] + int (rint (out->bytes_per_pixel(c) * corner.x)) + out->stride()[c] * corner.y;
}
}
shared_ptr<Image>
-Image::scale (libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
+Image::scale (dcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
{
assert (scaler);
/* Empirical testing suggests that sws_scale() will crash if
return scaled;
}
-/** Run a FFmpeg post-process on this image and return the processed version.
- * @param pp Flags for the required set of post processes.
- * @return Post-processed image.
- */
-shared_ptr<Image>
-Image::post_process (string pp, bool aligned) const
-{
- shared_ptr<Image> out (new Image (pixel_format(), size (), aligned));
-
- int pp_format = 0;
- switch (pixel_format()) {
- case PIX_FMT_YUV420P:
- pp_format = PP_FORMAT_420;
- break;
- case PIX_FMT_YUV422P10LE:
- case PIX_FMT_YUV422P:
- case PIX_FMT_UYVY422:
- pp_format = PP_FORMAT_422;
- break;
- case PIX_FMT_YUV444P:
- case PIX_FMT_YUV444P9BE:
- case PIX_FMT_YUV444P9LE:
- case PIX_FMT_YUV444P10BE:
- case PIX_FMT_YUV444P10LE:
- pp_format = PP_FORMAT_444;
- default:
- throw PixelFormatError ("post_process", pixel_format());
- }
-
- pp_mode* mode = pp_get_mode_by_name_and_quality (pp.c_str (), PP_QUALITY_MAX);
- pp_context* context = pp_get_context (size().width, size().height, pp_format | PP_CPU_CAPS_MMX2);
-
- pp_postprocess (
- (const uint8_t **) data(), stride(),
- out->data(), out->stride(),
- size().width, size().height,
- 0, 0, mode, context, 0
- );
-
- pp_free_mode (mode);
- pp_free_context (context);
-
- return out;
-}
-
shared_ptr<Image>
Image::crop (Crop crop, bool aligned) const
{
- libdcp::Size cropped_size = crop.apply (size ());
+ dcp::Size cropped_size = crop.apply (size ());
shared_ptr<Image> out (new Image (pixel_format(), cropped_size, aligned));
for (int c = 0; c < components(); ++c) {
}
}
+void
+Image::make_transparent ()
+{
+ if (_pixel_format != PIX_FMT_RGBA) {
+ throw PixelFormatError ("make_transparent()", _pixel_format);
+ }
+
+ memset (data()[0], 0, lines(0) * stride()[0]);
+}
+
void
Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
{
* @param p Pixel format.
* @param s Size in pixels.
*/
-Image::Image (AVPixelFormat p, libdcp::Size s, bool aligned)
- : libdcp::Image (s)
+Image::Image (AVPixelFormat p, dcp::Size s, bool aligned)
+ : dcp::Image (s)
, _pixel_format (p)
, _aligned (aligned)
{
void
Image::allocate ()
{
- _data = (uint8_t **) av_malloc (4 * sizeof (uint8_t *));
+ _data = (uint8_t **) wrapped_av_malloc (4 * sizeof (uint8_t *));
_data[0] = _data[1] = _data[2] = _data[3] = 0;
- _line_size = (int *) av_malloc (4 * sizeof (int));
+ _line_size = (int *) wrapped_av_malloc (4 * sizeof (int));
_line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0;
- _stride = (int *) av_malloc (4 * sizeof (int));
+ _stride = (int *) wrapped_av_malloc (4 * sizeof (int));
_stride[0] = _stride[1] = _stride[2] = _stride[3] = 0;
for (int i = 0; i < components(); ++i) {
seem to mind. The nasty + 1 in this malloc makes sure there is always a byte
for that instruction to read safely.
*/
- _data[i] = (uint8_t *) av_malloc (_stride[i] * lines (i) + 1);
+ _data[i] = (uint8_t *) wrapped_av_malloc (_stride[i] * lines (i) + 1);
}
}
Image::Image (Image const & other)
- : libdcp::Image (other)
+ : dcp::Image (other)
, _pixel_format (other._pixel_format)
, _aligned (other._aligned)
{
}
Image::Image (AVFrame* frame)
- : libdcp::Image (libdcp::Size (frame->width, frame->height))
+ : dcp::Image (dcp::Size (frame->width, frame->height))
, _pixel_format (static_cast<AVPixelFormat> (frame->format))
, _aligned (true)
{
}
Image::Image (shared_ptr<const Image> other, bool aligned)
- : libdcp::Image (other)
+ : dcp::Image (other)
, _pixel_format (other->_pixel_format)
, _aligned (aligned)
{
void
Image::swap (Image & other)
{
- libdcp::Image::swap (other);
+ dcp::Image::swap (other);
std::swap (_pixel_format, other._pixel_format);
return _stride;
}
-libdcp::Size
+dcp::Size
Image::size () const
{
return _size;
return _aligned;
}
+PositionImage
+merge (list<PositionImage> images)
+{
+ if (images.empty ()) {
+ return PositionImage ();
+ }
+
+ dcpomatic::Rect<int> all (images.front().position, images.front().image->size().width, images.front().image->size().height);
+ for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
+ all.extend (dcpomatic::Rect<int> (i->position, i->image->size().width, i->image->size().height));
+ }
+
+ shared_ptr<Image> merged (new Image (images.front().image->pixel_format (), dcp::Size (all.width, all.height), true));
+ merged->make_transparent ();
+ for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
+ merged->alpha_blend (i->image, i->position);
+ }
+
+ return PositionImage (merged, all.position ());
+}
+
+string
+Image::digest () const
+{
+ MD5_CTX md5_context;
+ MD5_Init (&md5_context);
+
+ for (int i = 0; i < components(); ++i) {
+ MD5_Update (&md5_context, data()[i], line_size()[i]);
+ }
+
+ unsigned char digest[MD5_DIGEST_LENGTH];
+ MD5_Final (digest, &md5_context);
+
+ stringstream s;
+ for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
+ s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
+ }
+
+ return s.str ();
+}