Code

d69b6fe547eb96243f8f1bcfa74e5f2af75513ea
[inkscape.git] / src / libnr / nr-pixblock.cpp
1 #define __NR_PIXBLOCK_C__
3 /** \file
4  * \brief Allocation/Setup of NRPixBlock objects. Pixel store functions.
5  *
6  * Authors:
7  *   (C) 1999-2002 Lauris Kaplinski <lauris@kaplinski.com>
8  *   2008, Jasper van de Gronde <th.v.d.gonde@hccnet.nl>
9  *
10  * This code is in the Public Domain
11  */
13 #include <cstring>
14 #include <string>
15 #include <string.h>
16 #include <glib/gmem.h>
17 #include "nr-pixblock.h"
19 /// Size of buffer that needs no allocation (default 4).
20 #define NR_TINY_MAX sizeof (unsigned char *)
22 /**
23  * Pixbuf initialisation using homegrown memory handling ("pixelstore").
24  *
25  * Pixbuf sizes are differentiated into tiny, <4K, <16K, <64K, and more,
26  * with each type having its own method of memory handling. After allocating
27  * memory, the buffer is cleared if the clear flag is set. Intended to
28  * reduce memory fragmentation.
29  * \param pb Pointer to the pixbuf struct.
30  * \param mode Indicates grayscale/RGB/RGBA.
31  * \param clear True if buffer should be cleared.
32  * \pre x1>=x0 && y1>=y0 && pb!=NULL
33  */
34 void
35 nr_pixblock_setup_fast (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
36 {
37         int w, h, bpp;
38         size_t size;
40         w = x1 - x0;
41         h = y1 - y0;
42         bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
44         size = bpp * w * h;
46         if (size <= NR_TINY_MAX) {
47                 pb->size = NR_PIXBLOCK_SIZE_TINY;
48                 if (clear) memset (pb->data.p, 0x0, size);
49         } else if (size <= 4096) {
50                 pb->size = NR_PIXBLOCK_SIZE_4K;
51                 pb->data.px = nr_pixelstore_4K_new (clear, 0x0);
52         } else if (size <= 16384) {
53                 pb->size = NR_PIXBLOCK_SIZE_16K;
54                 pb->data.px = nr_pixelstore_16K_new (clear, 0x0);
55         } else if (size <= 65536) {
56                 pb->size = NR_PIXBLOCK_SIZE_64K;
57                 pb->data.px = nr_pixelstore_64K_new (clear, 0x0);
58         } else if (size <= 262144) {
59                 pb->size = NR_PIXBLOCK_SIZE_256K;
60                 pb->data.px = nr_pixelstore_256K_new (clear, 0x0);
61         } else if (size <= 1048576) {
62                 pb->size = NR_PIXBLOCK_SIZE_1M;
63                 pb->data.px = nr_pixelstore_1M_new (clear, 0x0);
64         } else {
65                 pb->size = NR_PIXBLOCK_SIZE_BIG;
66              pb->data.px = NULL;
67                 if (size > 100000000) { // Don't even try to allocate more than 100Mb (5000x5000 RGBA
68                             // pixels). It'll just bog the system down even if successful. FIXME:
69                             // Can anyone suggest something better than the magic number?
70                 g_warning ("%lu bytes requested for pixel buffer, I won't try to allocate that.", (long unsigned) size);
71                 return;
72              }
73                 pb->data.px = g_try_new (unsigned char, size);
74                 if (pb->data.px == NULL) { // memory allocation failed
75                 g_warning ("Could not allocate %lu bytes for pixel buffer!", (long unsigned) size);
76                 return;
77              }
78                 if (clear) memset (pb->data.px, 0x0, size);
79         }
81         pb->mode = mode;
82         pb->empty = 1;
83     pb->visible_area.x0 = pb->area.x0 = x0;
84     pb->visible_area.y0 = pb->area.y0 = y0;
85     pb->visible_area.x1 = pb->area.x1 = x1;
86     pb->visible_area.y1 = pb->area.y1 = y1;
87         pb->rs = bpp * w;
88 }
90 /**
91  * Pixbuf initialisation using g_new.
92  *
93  * After allocating memory, the buffer is cleared if the clear flag is set.
94  * \param pb Pointer to the pixbuf struct.
95  * \param mode Indicates grayscale/RGB/RGBA.
96  * \param clear True if buffer should be cleared.
97  * \pre x1>=x0 && y1>=y0 && pb!=NULL
98  FIXME: currently unused except for nr_pixblock_new and pattern tiles, replace with _fast and delete?
99  */
100 void
101 nr_pixblock_setup (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
103         int w, h, bpp;
104         size_t size;
106         w = x1 - x0;
107         h = y1 - y0;
108         bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
110         size = bpp * w * h;
112         if (size <= NR_TINY_MAX) {
113                 pb->size = NR_PIXBLOCK_SIZE_TINY;
114                 if (clear) memset (pb->data.p, 0x0, size);
115         } else {
116                 pb->size = NR_PIXBLOCK_SIZE_BIG;
117                 pb->data.px = g_new (unsigned char, size);
118                 if (clear) memset (pb->data.px, 0x0, size);
119         }
121         pb->mode = mode;
122         pb->empty = 1;
123     pb->visible_area.x0 = pb->area.x0 = x0;
124     pb->visible_area.y0 = pb->area.y0 = y0;
125     pb->visible_area.x1 = pb->area.x1 = x1;
126     pb->visible_area.y1 = pb->area.y1 = y1;
127         pb->rs = bpp * w;
130 /**
131  * Pixbuf initialisation with preset values.
132  *
133  * After copying all parameters into the NRPixBlock struct, the pixel buffer is cleared if the clear flag is set.
134  * \param pb Pointer to the pixbuf struct.
135  * \param mode Indicates grayscale/RGB/RGBA.
136  * \param clear True if buffer should be cleared.
137  * \pre x1>=x0 && y1>=y0 && pb!=NULL
138  */
139 void
140 nr_pixblock_setup_extern (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, unsigned char *px, int rs, bool empty, bool clear)
142         int w, bpp;
144         w = x1 - x0;
145         bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
147         pb->size = NR_PIXBLOCK_SIZE_STATIC; 
148         pb->mode = mode;
149         pb->empty = empty;
150         pb->visible_area.x0 = pb->area.x0 = x0;
151         pb->visible_area.y0 = pb->area.y0 = y0;
152         pb->visible_area.x1 = pb->area.x1 = x1;
153         pb->visible_area.y1 = pb->area.y1 = y1;
154         pb->data.px = px;
155         pb->rs = rs;
157         g_assert (pb->data.px != NULL);
158         if (clear) {
159                 if (rs == bpp * w) {
160                         /// \todo How do you recognise if 
161                         /// px was an uncleared tiny buffer? 
162                         if (pb->data.px) 
163                                 memset (pb->data.px, 0x0, bpp * (y1 - y0) * w);
164                 } else {
165                         int y;
166                         for (y = y0; y < y1; y++) {
167                                 memset (pb->data.px + (y - y0) * rs, 0x0, bpp * w);
168                         }
169                 }
170         }
173 /**
174  * Frees memory taken by pixel data in NRPixBlock.
175  * \param pb Pointer to pixblock.
176  * \pre pb and pb->data.px point to valid addresses.
177  *
178  * According to pb->size, one of the functions for freeing the pixelstore
179  * is called. May be called regardless of how pixbuf was set up.
180  */
181 void
182 nr_pixblock_release (NRPixBlock *pb)
184         switch (pb->size) {
185         case NR_PIXBLOCK_SIZE_TINY:
186                 break;
187         case NR_PIXBLOCK_SIZE_4K:
188                 nr_pixelstore_4K_free (pb->data.px);
189                 break;
190         case NR_PIXBLOCK_SIZE_16K:
191                 nr_pixelstore_16K_free (pb->data.px);
192                 break;
193         case NR_PIXBLOCK_SIZE_64K:
194                 nr_pixelstore_64K_free (pb->data.px);
195                 break;
196         case NR_PIXBLOCK_SIZE_256K:
197                 nr_pixelstore_256K_free (pb->data.px);
198                 break;
199         case NR_PIXBLOCK_SIZE_1M:
200                 nr_pixelstore_1M_free (pb->data.px);
201                 break;
202         case NR_PIXBLOCK_SIZE_BIG:
203                 g_free (pb->data.px);
204                 break;
205         case NR_PIXBLOCK_SIZE_STATIC:
206                 break;
207         default:
208                 break;
209         }
212 /**
213  * Allocates NRPixBlock and sets it up.
214  *
215  * \return Pointer to fresh pixblock.
216  * Calls g_new() and nr_pixblock_setup().
217 FIXME: currently unused, delete? JG: Should be used more often! (To simplify memory management.)
218  */
219 NRPixBlock *
220 nr_pixblock_new (NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
222         NRPixBlock *pb;
224         pb = g_new (NRPixBlock, 1);
225     if (!pb) return 0;
227         nr_pixblock_setup (pb, mode, x0, y0, x1, y1, clear);
228     if (pb->size!=NR_PIXBLOCK_SIZE_TINY && !pb->data.px) {
229         g_free(pb);
230         return 0;
231     }
233         return pb;
236 /**
237  * Allocates NRPixBlock and sets it up.
238  *
239  * \return Pointer to fresh pixblock.
240  * Calls g_new() and nr_pixblock_setup().
241  */
242 NRPixBlock *
243 nr_pixblock_new_fast (NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
245     NRPixBlock *pb;
247     pb = g_new (NRPixBlock, 1);
248     if (!pb) return 0;
250     nr_pixblock_setup_fast (pb, mode, x0, y0, x1, y1, clear);
251     if (pb->size!=NR_PIXBLOCK_SIZE_TINY && !pb->data.px) {
252         g_free(pb);
253         return 0;
254     }
256     return pb;
259 /**
260  * Frees all memory taken by pixblock.
261  *
262  * \return NULL
263  */
264 NRPixBlock *
265 nr_pixblock_free (NRPixBlock *pb)
267         nr_pixblock_release (pb);
269         g_free (pb);
271         return NULL;
274 /* PixelStore operations */
276 #define NR_4K_BLOCK 32
277 static unsigned char **nr_4K_px = NULL;
278 static unsigned int nr_4K_len = 0;
279 static unsigned int nr_4K_size = 0;
281 unsigned char *
282 nr_pixelstore_4K_new (bool clear, unsigned char val)
284         unsigned char *px;
286         if (nr_4K_len != 0) {
287                 nr_4K_len -= 1;
288                 px = nr_4K_px[nr_4K_len];
289         } else {
290                 px = g_new (unsigned char, 4096);
291         }
292         
293         if (clear) memset (px, val, 4096);
295         return px;
298 void
299 nr_pixelstore_4K_free (unsigned char *px)
301         if (nr_4K_len == nr_4K_size) {
302                 nr_4K_size += NR_4K_BLOCK;
303                 nr_4K_px = g_renew (unsigned char *, nr_4K_px, nr_4K_size);
304         }
306         nr_4K_px[nr_4K_len] = px;
307         nr_4K_len += 1;
310 #define NR_16K_BLOCK 32
311 static unsigned char **nr_16K_px = NULL;
312 static unsigned int nr_16K_len = 0;
313 static unsigned int nr_16K_size = 0;
315 unsigned char *
316 nr_pixelstore_16K_new (bool clear, unsigned char val)
318         unsigned char *px;
320         if (nr_16K_len != 0) {
321                 nr_16K_len -= 1;
322                 px = nr_16K_px[nr_16K_len];
323         } else {
324                 px = g_new (unsigned char, 16384);
325         }
326         
327         if (clear) memset (px, val, 16384);
329         return px;
332 void
333 nr_pixelstore_16K_free (unsigned char *px)
335         if (nr_16K_len == nr_16K_size) {
336                 nr_16K_size += NR_16K_BLOCK;
337                 nr_16K_px = g_renew (unsigned char *, nr_16K_px, nr_16K_size);
338         }
340         nr_16K_px[nr_16K_len] = px;
341         nr_16K_len += 1;
344 #define NR_64K_BLOCK 32
345 static unsigned char **nr_64K_px = NULL;
346 static unsigned int nr_64K_len = 0;
347 static unsigned int nr_64K_size = 0;
349 unsigned char *
350 nr_pixelstore_64K_new (bool clear, unsigned char val)
352         unsigned char *px;
354         if (nr_64K_len != 0) {
355                 nr_64K_len -= 1;
356                 px = nr_64K_px[nr_64K_len];
357         } else {
358                 px = g_new (unsigned char, 65536);
359         }
361         if (clear) memset (px, val, 65536);
363         return px;
366 void
367 nr_pixelstore_64K_free (unsigned char *px)
369         if (nr_64K_len == nr_64K_size) {
370                 nr_64K_size += NR_64K_BLOCK;
371                 nr_64K_px = g_renew (unsigned char *, nr_64K_px, nr_64K_size);
372         }
374         nr_64K_px[nr_64K_len] = px;
375         nr_64K_len += 1;
378 #define NR_256K_BLOCK 32
379 #define NR_256K 262144
380 static unsigned char **nr_256K_px = NULL;
381 static unsigned int nr_256K_len = 0;
382 static unsigned int nr_256K_size = 0;
384 unsigned char *
385 nr_pixelstore_256K_new (bool clear, unsigned char val)
387         unsigned char *px;
389         if (nr_256K_len != 0) {
390                 nr_256K_len -= 1;
391                 px = nr_256K_px[nr_256K_len];
392         } else {
393            px = g_new (unsigned char, NR_256K);
394         }
396         if (clear) memset (px, val, NR_256K);
398         return px;
401 void
402 nr_pixelstore_256K_free (unsigned char *px)
404         if (nr_256K_len == nr_256K_size) {
405                 nr_256K_size += NR_256K_BLOCK;
406                 nr_256K_px = g_renew (unsigned char *, nr_256K_px, nr_256K_size);
407         }
409         nr_256K_px[nr_256K_len] = px;
410         nr_256K_len += 1;
413 #define NR_1M_BLOCK 32
414 #define NR_1M 1048576
415 static unsigned char **nr_1M_px = NULL;
416 static unsigned int nr_1M_len = 0;
417 static unsigned int nr_1M_size = 0;
419 unsigned char *
420 nr_pixelstore_1M_new (bool clear, unsigned char val)
422         unsigned char *px;
424         if (nr_1M_len != 0) {
425                 nr_1M_len -= 1;
426                 px = nr_1M_px[nr_1M_len];
427         } else {
428            px = g_new (unsigned char, NR_1M);
429         }
431         if (clear) memset (px, val, NR_1M);
433         return px;
436 void
437 nr_pixelstore_1M_free (unsigned char *px)
439         if (nr_1M_len == nr_1M_size) {
440                 nr_1M_size += NR_1M_BLOCK;
441                 nr_1M_px = g_renew (unsigned char *, nr_1M_px, nr_1M_size);
442         }
444         nr_1M_px[nr_1M_len] = px;
445         nr_1M_len += 1;
448 /*
449   Local Variables:
450   mode:c++
451   c-file-style:"stroustrup"
452   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
453   indent-tabs-mode:nil
454   fill-column:99
455   End:
456 */
457 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :