1
/* -*- mode: c; c-basic-offset: 4; -*-
3
* probability-map.c - A 2-D random variable with a probability
4
* distribution function defined as an image.
6
* Fyre - rendering and interactive exploration of chaotic functions
7
* Copyright (C) 2004-2005 David Trowbridge and Micah Dowty
9
* This program is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU General Public License
11
* as published by the Free Software Foundation; either version 2
12
* of the License, or (at your option) any later version.
14
* This program is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program; if not, write to the Free Software
21
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
#include "probability-map.h"
27
#include "math-util.h"
29
static void probability_map_class_init(ProbabilityMapClass *klass);
30
static void probability_map_dispose(GObject *gobject);
33
/************************************************************************************/
34
/**************************************************** Initialization / Finalization */
35
/************************************************************************************/
37
GType probability_map_get_type(void)
39
static GType obj_type = 0;
42
static const GTypeInfo obj_info = {
43
sizeof(ProbabilityMapClass),
45
NULL, /* base_finalize */
46
(GClassInitFunc) probability_map_class_init,
47
NULL, /* class_finalize */
48
NULL, /* class_data */
49
sizeof(ProbabilityMap),
51
NULL, /* instance init */
54
obj_type = g_type_register_static(G_TYPE_OBJECT, "ProbabilityMap", &obj_info, 0);
60
static void probability_map_class_init(ProbabilityMapClass *klass)
62
GObjectClass *object_class = (GObjectClass*) klass;
63
object_class->dispose = probability_map_dispose;
66
static void probability_map_dispose(GObject *gobject)
68
ProbabilityMap *self = PROBABILITY_MAP(gobject);
70
if (self->cumulative) {
71
g_free(self->cumulative);
72
self->cumulative = NULL;
77
/************************************************************************************/
78
/********************************************************************* Constructors */
79
/************************************************************************************/
81
ProbabilityMap* probability_map_new_file (const gchar* filename)
83
return probability_map_new_file_channel(filename, FYRE_CHANNEL_LUMA);
86
ProbabilityMap* probability_map_new_file_channel (const gchar* filename,
87
FyreImageChannel channel)
89
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
92
g_assert(pixbuf != NULL);
93
self = probability_map_new_pixbuf_channel(pixbuf, channel);
94
gdk_pixbuf_unref(pixbuf);
99
ProbabilityMap* probability_map_new_pixbuf (GdkPixbuf* pixbuf)
101
return probability_map_new_pixbuf_channel(pixbuf, FYRE_CHANNEL_LUMA);
104
ProbabilityMap* probability_map_new_pixbuf_channel (GdkPixbuf* pixbuf,
105
FyreImageChannel channel)
109
g_assert(gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB);
110
g_assert(gdk_pixbuf_get_bits_per_sample(pixbuf) == 8);
114
case FYRE_CHANNEL_RED:
118
case FYRE_CHANNEL_GREEN:
122
case FYRE_CHANNEL_BLUE:
126
case FYRE_CHANNEL_ALPHA:
127
g_assert(gdk_pixbuf_get_has_alpha(pixbuf));
132
g_warning("Unsupported channel");
136
return probability_map_new_raw(gdk_pixbuf_get_pixels(pixbuf) + offset,
137
gdk_pixbuf_get_width(pixbuf),
138
gdk_pixbuf_get_height(pixbuf),
139
gdk_pixbuf_get_rowstride(pixbuf),
140
gdk_pixbuf_get_n_channels(pixbuf),
144
ProbabilityMap* probability_map_new_raw (const guchar* data,
151
ProbabilityMap *self = PROBABILITY_MAP(g_object_new(probability_map_get_type(), NULL));
153
const guchar* row = data;
160
self->height = height;
161
self->image_scale_x = 1.0 / (width - 1);
162
self->image_scale_y = 1.0 / (height - 1);
164
self->cumulative_length = width * height;
165
self->cumulative = g_new(gfloat, self->cumulative_length);
166
output = self->cumulative;
168
/* Caluclate a cumulative distribution. For each pixel, we save the
169
* sum of it and all prior pixels. For speed, these inner loops are
170
* implemented separately for each pixel type using a macro.
173
#define CALCULATE_SUMS(type) do { \
178
sum += *(type*)pixel; \
182
pixel += pixel_stride; \
189
switch (pixel_type) {
192
CALCULATE_SUMS(guchar);
196
CALCULATE_SUMS(guint);
200
CALCULATE_SUMS(gulong);
204
CALCULATE_SUMS(gfloat);
208
CALCULATE_SUMS(gdouble);
212
g_warning("Unsupported pixel format");
216
#undef CALCULATE_SUMS
222
/************************************************************************************/
223
/******************************************************************* Public Methods */
224
/************************************************************************************/
226
void probability_map_ints (ProbabilityMap* self,
232
gfloat* cumulative = self->cumulative;
233
gsize length = self->cumulative_length;
234
gsize width = self->width;
238
} endpoints[2], midpoints[2];
240
/* Start with a uniform variate, scaled to cover the range of our CDF */
241
key = uniform_variate() * cumulative[length - 1];
243
/* Do a binary search for the first value in 'cumulative' that's greater than
244
* or equal to 'key'. These semantics are necessary to ensure that in a run
245
* of values with the same cumulative value, only the first one is ever chosen
246
* since the other values have zero probability in the original image.
248
endpoints[0].index = 0;
249
endpoints[1].index = length - 1;
250
endpoints[0].ge = cumulative[endpoints[0].index] >= key;
251
endpoints[1].ge = cumulative[endpoints[1].index] >= key;
253
while (endpoints[1].index > endpoints[0].index) {
254
midpoints[0].index = (endpoints[0].index + endpoints[1].index) >> 1;
255
midpoints[1].index = midpoints[0].index + 1;
256
midpoints[0].ge = cumulative[midpoints[0].index] >= key;
257
midpoints[1].ge = cumulative[midpoints[1].index] >= key;
259
if ((!midpoints[0].ge) && midpoints[1].ge) {
260
/* We're sitting right on the stopping condition */
263
else if (!midpoints[1].ge) {
264
/* The solution is above this */
265
endpoints[0] = midpoints[1];
267
else if (midpoints[0].ge) {
268
/* The solution is below this */
269
endpoints[1] = midpoints[0];
272
/* This should only be possible if our CDF wasn't sorted */
277
/* Convert an index within our cumulative array into (x,y) coords */
278
*x = midpoints[1].index % width;
279
*y = midpoints[1].index / width;
281
gfloat* cumulative = self->cumulative;
282
gsize length = self->cumulative_length;
284
gint current = self->current;
285
gsize width = self->width;
287
energy = uniform_variate() * 1000;
290
energy -= cumulative[current+1] - cumulative[current];
292
if (current == length-2)
296
*x = current % width;
297
*y = current / width;
299
self->current = current;
303
void probability_map_normalized (ProbabilityMap* self,
308
probability_map_ints(self, &xi, &yi);
309
*x = xi * self->image_scale_x;
310
*y = yi * self->image_scale_y;
313
void probability_map_uniform (ProbabilityMap* self,
317
probability_map_normalized(self, x, y);
318
*x += uniform_variate() * self->image_scale_x;
319
*y += uniform_variate() * self->image_scale_y;
322
void probability_map_gaussian (ProbabilityMap* self,
327
probability_map_normalized(self, x, y);
328
*x += normal_variate() * self->image_scale_x * radius;
329
*y += normal_variate() * self->image_scale_y * radius;