~ubuntu-branches/ubuntu/vivid/fyre/vivid

« back to all changes in this revision

Viewing changes to src/probability-map.c

  • Committer: Bazaar Package Importer
  • Author(s): Christoph Haas
  • Date: 2005-05-25 21:59:19 UTC
  • Revision ID: james.westby@ubuntu.com-20050525215919-jawtso5ic23qb401
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: c; c-basic-offset: 4; -*-
 
2
 *
 
3
 * probability-map.c - A 2-D random variable with a probability
 
4
 *                     distribution function defined as an image.
 
5
 *
 
6
 * Fyre - rendering and interactive exploration of chaotic functions
 
7
 * Copyright (C) 2004-2005 David Trowbridge and Micah Dowty
 
8
 *
 
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.
 
13
 *
 
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.
 
18
 *
 
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.
 
22
 *
 
23
 */
 
24
 
 
25
#include <gtk/gtk.h>
 
26
#include "probability-map.h"
 
27
#include "math-util.h"
 
28
 
 
29
static void probability_map_class_init(ProbabilityMapClass *klass);
 
30
static void probability_map_dispose(GObject *gobject);
 
31
 
 
32
 
 
33
/************************************************************************************/
 
34
/**************************************************** Initialization / Finalization */
 
35
/************************************************************************************/
 
36
 
 
37
GType probability_map_get_type(void)
 
38
{
 
39
    static GType obj_type = 0;
 
40
 
 
41
    if (!obj_type) {
 
42
        static const GTypeInfo obj_info = {
 
43
            sizeof(ProbabilityMapClass),
 
44
            NULL, /* base_init */
 
45
            NULL, /* base_finalize */
 
46
            (GClassInitFunc) probability_map_class_init,
 
47
            NULL, /* class_finalize */
 
48
            NULL, /* class_data */
 
49
            sizeof(ProbabilityMap),
 
50
            0,
 
51
            NULL, /* instance init */
 
52
        };
 
53
 
 
54
        obj_type = g_type_register_static(G_TYPE_OBJECT, "ProbabilityMap", &obj_info, 0);
 
55
    }
 
56
 
 
57
    return obj_type;
 
58
}
 
59
 
 
60
static void probability_map_class_init(ProbabilityMapClass *klass)
 
61
{
 
62
    GObjectClass *object_class = (GObjectClass*) klass;
 
63
    object_class->dispose = probability_map_dispose;
 
64
}
 
65
 
 
66
static void probability_map_dispose(GObject *gobject)
 
67
{
 
68
    ProbabilityMap *self = PROBABILITY_MAP(gobject);
 
69
 
 
70
    if (self->cumulative) {
 
71
        g_free(self->cumulative);
 
72
        self->cumulative = NULL;
 
73
    }
 
74
}
 
75
 
 
76
 
 
77
/************************************************************************************/
 
78
/********************************************************************* Constructors */
 
79
/************************************************************************************/
 
80
 
 
81
ProbabilityMap*  probability_map_new_file            (const gchar*          filename)
 
82
{
 
83
    return probability_map_new_file_channel(filename, FYRE_CHANNEL_LUMA);
 
84
}
 
85
 
 
86
ProbabilityMap*  probability_map_new_file_channel    (const gchar*          filename,
 
87
                                                      FyreImageChannel      channel)
 
88
{
 
89
    GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 
90
    ProbabilityMap *self;
 
91
 
 
92
    g_assert(pixbuf != NULL);
 
93
    self = probability_map_new_pixbuf_channel(pixbuf, channel);
 
94
    gdk_pixbuf_unref(pixbuf);
 
95
 
 
96
    return self;
 
97
}
 
98
 
 
99
ProbabilityMap*  probability_map_new_pixbuf          (GdkPixbuf*            pixbuf)
 
100
{
 
101
    return probability_map_new_pixbuf_channel(pixbuf, FYRE_CHANNEL_LUMA);
 
102
}
 
103
 
 
104
ProbabilityMap*  probability_map_new_pixbuf_channel  (GdkPixbuf*            pixbuf,
 
105
                                                      FyreImageChannel      channel)
 
106
{
 
107
    int offset;
 
108
 
 
109
    g_assert(gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB);
 
110
    g_assert(gdk_pixbuf_get_bits_per_sample(pixbuf) == 8);
 
111
 
 
112
    switch (channel) {
 
113
 
 
114
    case FYRE_CHANNEL_RED:
 
115
        offset = 0;
 
116
        break;
 
117
 
 
118
    case FYRE_CHANNEL_GREEN:
 
119
        offset = 1;
 
120
        break;
 
121
 
 
122
    case FYRE_CHANNEL_BLUE:
 
123
        offset = 2;
 
124
        break;
 
125
 
 
126
    case FYRE_CHANNEL_ALPHA:
 
127
        g_assert(gdk_pixbuf_get_has_alpha(pixbuf));
 
128
        offset = 3;
 
129
        break;
 
130
 
 
131
    default:
 
132
        g_warning("Unsupported channel");
 
133
        offset = 0;
 
134
    }
 
135
 
 
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),
 
141
                                   G_TYPE_UCHAR);
 
142
}
 
143
 
 
144
ProbabilityMap*  probability_map_new_raw          (const guchar*         data,
 
145
                                                   gint                  width,
 
146
                                                   gint                  height,
 
147
                                                   gint                  row_stride,
 
148
                                                   gint                  pixel_stride,
 
149
                                                   gint                  pixel_type)
 
150
{
 
151
    ProbabilityMap *self = PROBABILITY_MAP(g_object_new(probability_map_get_type(), NULL));
 
152
    gdouble sum = 0;
 
153
    const guchar* row = data;
 
154
    const guchar* pixel;
 
155
    int x;
 
156
    int y = height;
 
157
    gfloat *output;
 
158
 
 
159
    self->width = width;
 
160
    self->height = height;
 
161
    self->image_scale_x = 1.0 / (width - 1);
 
162
    self->image_scale_y = 1.0 / (height - 1);
 
163
 
 
164
    self->cumulative_length = width * height;
 
165
    self->cumulative = g_new(gfloat, self->cumulative_length);
 
166
    output = self->cumulative;
 
167
 
 
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.
 
171
     */
 
172
 
 
173
#define CALCULATE_SUMS(type) do {       \
 
174
        while (y) {                     \
 
175
            x = width;                  \
 
176
            pixel = row;                \
 
177
            while (x) {                 \
 
178
                sum += *(type*)pixel;   \
 
179
                *output = sum;          \
 
180
                output++;               \
 
181
                x--;                    \
 
182
                pixel += pixel_stride;  \
 
183
            }                           \
 
184
            y--;                        \
 
185
            row += row_stride;          \
 
186
        };                              \
 
187
    } while (0)
 
188
 
 
189
    switch (pixel_type) {
 
190
 
 
191
    case G_TYPE_UCHAR:
 
192
        CALCULATE_SUMS(guchar);
 
193
        break;
 
194
 
 
195
    case G_TYPE_UINT:
 
196
        CALCULATE_SUMS(guint);
 
197
        break;
 
198
 
 
199
    case G_TYPE_ULONG:
 
200
        CALCULATE_SUMS(gulong);
 
201
        break;
 
202
 
 
203
    case G_TYPE_FLOAT:
 
204
        CALCULATE_SUMS(gfloat);
 
205
        break;
 
206
 
 
207
    case G_TYPE_DOUBLE:
 
208
        CALCULATE_SUMS(gdouble);
 
209
        break;
 
210
 
 
211
    default:
 
212
        g_warning("Unsupported pixel format");
 
213
 
 
214
    }
 
215
 
 
216
#undef CALCULATE_SUMS
 
217
 
 
218
    return self;
 
219
}
 
220
 
 
221
 
 
222
/************************************************************************************/
 
223
/******************************************************************* Public Methods */
 
224
/************************************************************************************/
 
225
 
 
226
void             probability_map_ints             (ProbabilityMap*       self,
 
227
                                                   guint*                x,
 
228
                                                   guint*                y)
 
229
{
 
230
#if 1
 
231
    gfloat key;
 
232
    gfloat* cumulative = self->cumulative;
 
233
    gsize length = self->cumulative_length;
 
234
    gsize width = self->width;
 
235
    struct {
 
236
        int index;
 
237
        int ge;
 
238
    } endpoints[2], midpoints[2];
 
239
 
 
240
    /* Start with a uniform variate, scaled to cover the range of our CDF */
 
241
    key = uniform_variate() * cumulative[length - 1];
 
242
 
 
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.
 
247
     */
 
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;
 
252
 
 
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;
 
258
 
 
259
        if ((!midpoints[0].ge) && midpoints[1].ge) {
 
260
            /* We're sitting right on the stopping condition */
 
261
            break;
 
262
        }
 
263
        else if (!midpoints[1].ge) {
 
264
            /* The solution is above this */
 
265
            endpoints[0] = midpoints[1];
 
266
        }
 
267
        else if (midpoints[0].ge) {
 
268
            /* The solution is below this */
 
269
            endpoints[1] = midpoints[0];
 
270
        }
 
271
        else {
 
272
            /* This should only be possible if our CDF wasn't sorted */
 
273
            g_assert(0);
 
274
        }
 
275
    }
 
276
 
 
277
    /* Convert an index within our cumulative array into (x,y) coords */
 
278
    *x = midpoints[1].index % width;
 
279
    *y = midpoints[1].index / width;
 
280
#else
 
281
    gfloat* cumulative = self->cumulative;
 
282
    gsize length = self->cumulative_length;
 
283
    gdouble energy;
 
284
    gint current = self->current;
 
285
    gsize width = self->width;
 
286
 
 
287
    energy = uniform_variate() * 1000;
 
288
 
 
289
    while (energy > 0) {
 
290
        energy -= cumulative[current+1] - cumulative[current];
 
291
        current++;
 
292
        if (current == length-2)
 
293
            current = 0;
 
294
    }
 
295
 
 
296
    *x = current % width;
 
297
    *y = current / width;
 
298
 
 
299
    self->current = current;
 
300
#endif
 
301
}
 
302
 
 
303
void             probability_map_normalized       (ProbabilityMap*       self,
 
304
                                                   gdouble*              x,
 
305
                                                   gdouble*              y)
 
306
{
 
307
    guint xi, yi;
 
308
    probability_map_ints(self, &xi, &yi);
 
309
    *x = xi * self->image_scale_x;
 
310
    *y = yi * self->image_scale_y;
 
311
}
 
312
 
 
313
void             probability_map_uniform          (ProbabilityMap*       self,
 
314
                                                   gdouble*              x,
 
315
                                                   gdouble*              y)
 
316
{
 
317
    probability_map_normalized(self, x, y);
 
318
    *x += uniform_variate() * self->image_scale_x;
 
319
    *y += uniform_variate() * self->image_scale_y;
 
320
}
 
321
 
 
322
void             probability_map_gaussian         (ProbabilityMap*       self,
 
323
                                                   gdouble*              x,
 
324
                                                   gdouble*              y,
 
325
                                                   double                radius)
 
326
{
 
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;
 
330
}
 
331
 
 
332
/* The End */