+ throw PixelFormatError (N_("make_black()"), _pixel_format);
+ }
+}
+
+void
+Image::alpha_blend (shared_ptr<const Image> other, Position position)
+{
+ /* Only implemented for RGBA onto RGB24 so far */
+ assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA);
+
+ int start_tx = position.x;
+ int start_ox = 0;
+
+ if (start_tx < 0) {
+ start_ox = -start_tx;
+ start_tx = 0;
+ }
+
+ int start_ty = position.y;
+ int start_oy = 0;
+
+ if (start_ty < 0) {
+ start_oy = -start_ty;
+ start_ty = 0;
+ }
+
+ for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
+ uint8_t* tp = data()[0] + ty * stride()[0] + position.x * 3;
+ uint8_t* op = other->data()[0] + oy * other->stride()[0];
+ for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
+ float const alpha = float (op[3]) / 255;
+ tp[0] = (tp[0] * (1 - alpha)) + op[0] * alpha;
+ tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha;
+ tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha;
+ tp += 3;
+ op += 4;
+ }
+ }
+}
+
+void
+Image::read_from_socket (shared_ptr<Socket> socket)
+{
+ for (int i = 0; i < components(); ++i) {
+ uint8_t* p = data()[i];
+ for (int y = 0; y < lines(i); ++y) {
+ socket->read (p, line_size()[i]);
+ p += stride()[i];
+ }
+ }
+}
+
+void
+Image::write_to_socket (shared_ptr<Socket> socket) const
+{
+ for (int i = 0; i < components(); ++i) {
+ uint8_t* p = data()[i];
+ for (int y = 0; y < lines(i); ++y) {
+ socket->write (p, line_size()[i]);
+ p += stride()[i];
+ }
+ }
+}
+
+
+float
+Image::bytes_per_pixel (int c) const
+{
+ AVPixFmtDescriptor const * d = av_pix_fmt_desc_get(_pixel_format);
+ if (!d) {
+ throw PixelFormatError (N_("lines()"), _pixel_format);
+ }
+
+ if (c >= components()) {
+ return 0;
+ }
+
+ float bpp[4] = { 0, 0, 0, 0 };
+
+ bpp[0] = floor ((d->comp[0].depth_minus1 + 1 + 7) / 8);
+ if (d->nb_components > 1) {
+ bpp[1] = floor ((d->comp[1].depth_minus1 + 1 + 7) / 8) / pow (2, d->log2_chroma_w);