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