39a569cf2e238fe2f098dddfb130578f638f3c3f
[videolink.git] / pixbufs.cpp
1 // Copyright 2005 Ben Hutchings <ben@decadentplace.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 old and new Pixbufs must be equal
18 // and match the specified dimensions.  The width and height of
19 // the 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() == width);
35     assert(old_buf->get_height() == 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     const guint8 * new_p = new_buf->get_pixels();
49     guint8 * diff_p = (diff_buf->get_pixels()
50                        + diff_bpr * offset_y
51                        + diff_bpp * offset_x);
52
53     for (int y = 0; y != height; ++y)
54     {
55         for (int x = 0; x != width; ++x)
56         {
57             if (old_p[0] != new_p[0]
58                 || old_p[1] != new_p[1]
59                 || old_p[2] != new_p[2])
60             {
61                 diff_p[0] = new_p[0];
62                 diff_p[1] = new_p[1];
63                 diff_p[2] = new_p[2];
64                 diff_p[3] = 0xFF; // fully opaque
65             }
66             old_p += old_bpp;
67             new_p += new_bpp;
68             diff_p += diff_bpp;
69         }
70         old_p += old_bpr - old_bpp * width;
71         new_p += new_bpr - new_bpp * width;
72         diff_p += diff_bpr - diff_bpp * width;
73     }
74 }
75
76 // Quantise an RGBA Pixbuf to the specified number of colours, including
77 // one transparent colour.  Currently uses Floyd-Steinberg dithering.
78 void quantise_rgba_pixbuf(Glib::RefPtr<Gdk::Pixbuf> buf, int n_colours)
79 {
80     assert(buf->get_colorspace() == Gdk::COLORSPACE_RGB
81            && buf->get_has_alpha());
82     int bpr = buf->get_rowstride();
83     assert(buf->get_n_channels() == 4);
84     int width = buf->get_width();
85     int height = buf->get_height();
86
87     unsigned char * buf_p = buf->get_pixels();
88     auto_array<unsigned char *> rows(new unsigned char *[height]);
89     for (int y = 0; y != height; ++y)
90         rows[y] = &buf_p[y * bpr];
91     auto_array<unsigned char> quant_buf_p(
92         new unsigned char[width * height]);
93     auto_array<unsigned char *> quant_rows(
94         new unsigned char *[height]);
95     for (int y = 0; y != height; ++y)
96         quant_rows[y] = &quant_buf_p[y * width];
97     auto_array<unsigned int> colours(new unsigned int[n_colours]);
98
99     quantize(rows.get(), quant_rows.get(), width, height,
100              JDITHER_FS, n_colours, colours.get());
101
102     // Pixbuf doesn't support quantised images, so convert back to RGBA.
103     for (int y = 0; y != height; ++y)
104         for (int x = 0; x != width; ++x)
105         {
106             unsigned int colour = colours[quant_rows[y][x]];
107             rows[y][4*x]   = colour;
108             rows[y][4*x+1] = colour >> 8;
109             rows[y][4*x+2] = colour >> 16;
110             rows[y][4*x+3] = colour >> 24;
111         }
112 }