Release versions 1.2.11 and 1.2.11-1
[videolink.git] / pixbufs.cpp
1 // Copyright 2005-6 Ben Hutchings <ben@decadent.org.uk>.
2 // See the file "COPYING" for licence details.
3
4 #include "pixbufs.hpp"
5
6 #include <cassert>
7
8 #include <gdkmm/pixbuf.h>
9
10 #include "auto_array.hpp"
11 #include "jquant2.h"
12
13 // Find pixel differences between an "old" and "new" RGB Pixbuf
14 // (or RGBA, but the alpha component will be ignored) and copy the
15 // differing pixels from the new one to a third RGBA Pixbuf at the
16 // specified offset with full opacity.
17 // The width and height of the new Pixbufs must be equal and match
18 // the specified dimensions.  The width and height of the old and
19 // third Pixbuf must be large enough to store a rectangle of
20 // those dimensions at the specified offset.
21 void diff_rgb_pixbufs(Glib::RefPtr<Gdk::Pixbuf> old_buf,
22                       Glib::RefPtr<Gdk::Pixbuf> new_buf,
23                       Glib::RefPtr<Gdk::Pixbuf> diff_buf,
24                       int offset_x, int offset_y,
25                       int width, int height)
26 {
27     assert(old_buf->get_colorspace() == Gdk::COLORSPACE_RGB);
28     assert(new_buf->get_colorspace() == Gdk::COLORSPACE_RGB);
29     assert(diff_buf->get_colorspace() == Gdk::COLORSPACE_RGB
30            && diff_buf->get_has_alpha());
31     int old_bpr = old_buf->get_rowstride();
32     int old_bpp = old_buf->get_n_channels();
33     assert(old_bpp >= 3);
34     assert(old_buf->get_width() >= offset_x + width);
35     assert(old_buf->get_height() >= offset_y + height);
36     int new_bpr = new_buf->get_rowstride();
37     int new_bpp = new_buf->get_n_channels();
38     assert(new_bpp >= 3);
39     assert(new_buf->get_width() == width);
40     assert(new_buf->get_height() == height);
41     int diff_bpr = diff_buf->get_rowstride();
42     int diff_bpp = diff_buf->get_n_channels();
43     assert(diff_bpp == 4);
44     assert(diff_buf->get_width() >= offset_x + width);
45     assert(diff_buf->get_height() >= offset_y + height);
46
47     const guint8 * old_p = (old_buf->get_pixels()
48                             + old_bpr * offset_y
49                             + old_bpp * offset_x);
50     const guint8 * new_p = new_buf->get_pixels();
51     guint8 * diff_p = (diff_buf->get_pixels()
52                        + diff_bpr * offset_y
53                        + diff_bpp * offset_x);
54
55     for (int y = 0; y != height; ++y)
56     {
57         for (int x = 0; x != width; ++x)
58         {
59             if (old_p[0] != new_p[0]
60                 || old_p[1] != new_p[1]
61                 || old_p[2] != new_p[2])
62             {
63                 diff_p[0] = new_p[0];
64                 diff_p[1] = new_p[1];
65                 diff_p[2] = new_p[2];
66                 diff_p[3] = 0xFF; // fully opaque
67             }
68             old_p += old_bpp;
69             new_p += new_bpp;
70             diff_p += diff_bpp;
71         }
72         old_p += old_bpr - old_bpp * width;
73         new_p += new_bpr - new_bpp * width;
74         diff_p += diff_bpr - diff_bpp * width;
75     }
76 }
77
78 // Quantise an RGBA Pixbuf to the specified number of colours, including
79 // one transparent colour.  Currently uses Floyd-Steinberg dithering.
80 void quantise_rgba_pixbuf(Glib::RefPtr<Gdk::Pixbuf> buf, int n_colours)
81 {
82     assert(buf->get_colorspace() == Gdk::COLORSPACE_RGB
83            && buf->get_has_alpha());
84     int bpr = buf->get_rowstride();
85     assert(buf->get_n_channels() == 4);
86     int width = buf->get_width();
87     int height = buf->get_height();
88
89     unsigned char * buf_p = buf->get_pixels();
90     auto_array<unsigned char *> rows(new unsigned char *[height]);
91     for (int y = 0; y != height; ++y)
92         rows[y] = &buf_p[y * bpr];
93     auto_array<unsigned char> quant_buf_p(
94         new unsigned char[width * height]);
95     auto_array<unsigned char *> quant_rows(
96         new unsigned char *[height]);
97     for (int y = 0; y != height; ++y)
98         quant_rows[y] = &quant_buf_p[y * width];
99     auto_array<unsigned int> colours(new unsigned int[n_colours]);
100
101     quantize(rows.get(), quant_rows.get(), width, height,
102              JDITHER_FS, n_colours, colours.get());
103
104     // Pixbuf doesn't support quantised images, so convert back to RGBA.
105     for (int y = 0; y != height; ++y)
106         for (int x = 0; x != width; ++x)
107         {
108             unsigned int colour = colours[quant_rows[y][x]];
109             rows[y][4*x]   = colour;
110             rows[y][4*x+1] = colour >> 8;
111             rows[y][4*x+2] = colour >> 16;
112             rows[y][4*x+3] = colour >> 24;
113         }
114 }