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 guint64 sizel = (guint64)bpp * (guint64)w * (guint64)h;
46 if(sizel > (guint64)G_MAXSIZE) {
47 g_warning ("the requested memory exceeds the system limit");
48 return;
49 }
51 size = bpp * w * h;
53 if (size <= NR_TINY_MAX) {
54 pb->size = NR_PIXBLOCK_SIZE_TINY;
55 if (clear) memset (pb->data.p, 0x0, size);
56 } else if (size <= 4096) {
57 pb->size = NR_PIXBLOCK_SIZE_4K;
58 pb->data.px = nr_pixelstore_4K_new (clear, 0x0);
59 } else if (size <= 16384) {
60 pb->size = NR_PIXBLOCK_SIZE_16K;
61 pb->data.px = nr_pixelstore_16K_new (clear, 0x0);
62 } else if (size <= 65536) {
63 pb->size = NR_PIXBLOCK_SIZE_64K;
64 pb->data.px = nr_pixelstore_64K_new (clear, 0x0);
65 } else if (size <= 262144) {
66 pb->size = NR_PIXBLOCK_SIZE_256K;
67 pb->data.px = nr_pixelstore_256K_new (clear, 0x0);
68 } else if (size <= 1048576) {
69 pb->size = NR_PIXBLOCK_SIZE_1M;
70 pb->data.px = nr_pixelstore_1M_new (clear, 0x0);
71 } else {
72 pb->size = NR_PIXBLOCK_SIZE_BIG;
73 pb->data.px = NULL;
74 if (size > 100000000) { // Don't even try to allocate more than 100Mb (5000x5000 RGBA
75 // pixels). It'll just bog the system down even if successful. FIXME:
76 // Can anyone suggest something better than the magic number?
77 g_warning ("%lu bytes requested for pixel buffer, this will slow down the system.", (long unsigned) size);
78 // do not quit here, let the system decide for RAM usage
79 // return;
80 }
81 pb->data.px = g_try_new (unsigned char, size);
82 if (pb->data.px == NULL) { // memory allocation failed
83 g_warning ("Could not allocate %lu bytes for pixel buffer!", (long unsigned) size);
84 return;
85 }
86 if (clear) memset (pb->data.px, 0x0, size);
87 }
89 pb->mode = mode;
90 pb->empty = 1;
91 pb->visible_area.x0 = pb->area.x0 = x0;
92 pb->visible_area.y0 = pb->area.y0 = y0;
93 pb->visible_area.x1 = pb->area.x1 = x1;
94 pb->visible_area.y1 = pb->area.y1 = y1;
95 pb->rs = bpp * w;
96 }
98 /**
99 * Pixbuf initialisation using g_new.
100 *
101 * After allocating memory, the buffer is cleared if the clear flag is set.
102 * \param pb Pointer to the pixbuf struct.
103 * \param mode Indicates grayscale/RGB/RGBA.
104 * \param clear True if buffer should be cleared.
105 * \pre x1>=x0 && y1>=y0 && pb!=NULL
106 FIXME: currently unused except for nr_pixblock_new and pattern tiles, replace with _fast and delete?
107 */
108 void
109 nr_pixblock_setup (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
110 {
111 int w, h, bpp;
112 size_t size;
114 w = x1 - x0;
115 h = y1 - y0;
116 bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
118 size = bpp * w * h;
120 if (size <= NR_TINY_MAX) {
121 pb->size = NR_PIXBLOCK_SIZE_TINY;
122 if (clear) memset (pb->data.p, 0x0, size);
123 } else {
124 pb->size = NR_PIXBLOCK_SIZE_BIG;
125 pb->data.px = g_new (unsigned char, size);
126 if (clear) memset (pb->data.px, 0x0, size);
127 }
129 pb->mode = mode;
130 pb->empty = 1;
131 pb->visible_area.x0 = pb->area.x0 = x0;
132 pb->visible_area.y0 = pb->area.y0 = y0;
133 pb->visible_area.x1 = pb->area.x1 = x1;
134 pb->visible_area.y1 = pb->area.y1 = y1;
135 pb->rs = bpp * w;
136 }
138 /**
139 * Pixbuf initialisation with preset values.
140 *
141 * After copying all parameters into the NRPixBlock struct, the pixel buffer is cleared if the clear flag is set.
142 * \param pb Pointer to the pixbuf struct.
143 * \param mode Indicates grayscale/RGB/RGBA.
144 * \param clear True if buffer should be cleared.
145 * \pre x1>=x0 && y1>=y0 && pb!=NULL
146 */
147 void
148 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)
149 {
150 int w, bpp;
152 w = x1 - x0;
153 bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
155 pb->size = NR_PIXBLOCK_SIZE_STATIC;
156 pb->mode = mode;
157 pb->empty = empty;
158 pb->visible_area.x0 = pb->area.x0 = x0;
159 pb->visible_area.y0 = pb->area.y0 = y0;
160 pb->visible_area.x1 = pb->area.x1 = x1;
161 pb->visible_area.y1 = pb->area.y1 = y1;
162 pb->data.px = px;
163 pb->rs = rs;
165 g_assert (pb->data.px != NULL);
166 if (clear) {
167 if (rs == bpp * w) {
168 /// \todo How do you recognise if
169 /// px was an uncleared tiny buffer?
170 if (pb->data.px)
171 memset (pb->data.px, 0x0, bpp * (y1 - y0) * w);
172 } else {
173 int y;
174 for (y = y0; y < y1; y++) {
175 memset (pb->data.px + (y - y0) * rs, 0x0, bpp * w);
176 }
177 }
178 }
179 }
181 /**
182 * Frees memory taken by pixel data in NRPixBlock.
183 * \param pb Pointer to pixblock.
184 * \pre pb and pb->data.px point to valid addresses.
185 *
186 * According to pb->size, one of the functions for freeing the pixelstore
187 * is called. May be called regardless of how pixbuf was set up.
188 */
189 void
190 nr_pixblock_release (NRPixBlock *pb)
191 {
192 switch (pb->size) {
193 case NR_PIXBLOCK_SIZE_TINY:
194 break;
195 case NR_PIXBLOCK_SIZE_4K:
196 nr_pixelstore_4K_free (pb->data.px);
197 break;
198 case NR_PIXBLOCK_SIZE_16K:
199 nr_pixelstore_16K_free (pb->data.px);
200 break;
201 case NR_PIXBLOCK_SIZE_64K:
202 nr_pixelstore_64K_free (pb->data.px);
203 break;
204 case NR_PIXBLOCK_SIZE_256K:
205 nr_pixelstore_256K_free (pb->data.px);
206 break;
207 case NR_PIXBLOCK_SIZE_1M:
208 nr_pixelstore_1M_free (pb->data.px);
209 break;
210 case NR_PIXBLOCK_SIZE_BIG:
211 g_free (pb->data.px);
212 break;
213 case NR_PIXBLOCK_SIZE_STATIC:
214 break;
215 default:
216 break;
217 }
218 }
220 /**
221 * Allocates NRPixBlock and sets it up.
222 *
223 * \return Pointer to fresh pixblock.
224 * Calls g_new() and nr_pixblock_setup().
225 FIXME: currently unused, delete? JG: Should be used more often! (To simplify memory management.)
226 */
227 NRPixBlock *
228 nr_pixblock_new (NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
229 {
230 NRPixBlock *pb;
232 pb = g_new (NRPixBlock, 1);
233 if (!pb) return 0;
235 nr_pixblock_setup (pb, mode, x0, y0, x1, y1, clear);
236 if (pb->size!=NR_PIXBLOCK_SIZE_TINY && !pb->data.px) {
237 g_free(pb);
238 return 0;
239 }
241 return pb;
242 }
244 /**
245 * Allocates NRPixBlock and sets it up.
246 *
247 * \return Pointer to fresh pixblock.
248 * Calls g_new() and nr_pixblock_setup().
249 */
250 NRPixBlock *
251 nr_pixblock_new_fast (NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
252 {
253 NRPixBlock *pb;
255 pb = g_new (NRPixBlock, 1);
256 if (!pb) return 0;
258 nr_pixblock_setup_fast (pb, mode, x0, y0, x1, y1, clear);
259 if (pb->size!=NR_PIXBLOCK_SIZE_TINY && !pb->data.px) {
260 g_free(pb);
261 return 0;
262 }
264 return pb;
265 }
267 /**
268 * Frees all memory taken by pixblock.
269 *
270 * \return NULL
271 */
272 NRPixBlock *
273 nr_pixblock_free (NRPixBlock *pb)
274 {
275 nr_pixblock_release (pb);
277 g_free (pb);
279 return NULL;
280 }
282 /* PixelStore operations */
284 #define NR_4K_BLOCK 32
285 static unsigned char **nr_4K_px = NULL;
286 static unsigned int nr_4K_len = 0;
287 static unsigned int nr_4K_size = 0;
289 unsigned char *
290 nr_pixelstore_4K_new (bool clear, unsigned char val)
291 {
292 unsigned char *px;
294 if (nr_4K_len != 0) {
295 nr_4K_len -= 1;
296 px = nr_4K_px[nr_4K_len];
297 } else {
298 px = g_new (unsigned char, 4096);
299 }
301 if (clear) memset (px, val, 4096);
303 return px;
304 }
306 void
307 nr_pixelstore_4K_free (unsigned char *px)
308 {
309 if (nr_4K_len == nr_4K_size) {
310 nr_4K_size += NR_4K_BLOCK;
311 nr_4K_px = g_renew (unsigned char *, nr_4K_px, nr_4K_size);
312 }
314 nr_4K_px[nr_4K_len] = px;
315 nr_4K_len += 1;
316 }
318 #define NR_16K_BLOCK 32
319 static unsigned char **nr_16K_px = NULL;
320 static unsigned int nr_16K_len = 0;
321 static unsigned int nr_16K_size = 0;
323 unsigned char *
324 nr_pixelstore_16K_new (bool clear, unsigned char val)
325 {
326 unsigned char *px;
328 if (nr_16K_len != 0) {
329 nr_16K_len -= 1;
330 px = nr_16K_px[nr_16K_len];
331 } else {
332 px = g_new (unsigned char, 16384);
333 }
335 if (clear) memset (px, val, 16384);
337 return px;
338 }
340 void
341 nr_pixelstore_16K_free (unsigned char *px)
342 {
343 if (nr_16K_len == nr_16K_size) {
344 nr_16K_size += NR_16K_BLOCK;
345 nr_16K_px = g_renew (unsigned char *, nr_16K_px, nr_16K_size);
346 }
348 nr_16K_px[nr_16K_len] = px;
349 nr_16K_len += 1;
350 }
352 #define NR_64K_BLOCK 32
353 static unsigned char **nr_64K_px = NULL;
354 static unsigned int nr_64K_len = 0;
355 static unsigned int nr_64K_size = 0;
357 unsigned char *
358 nr_pixelstore_64K_new (bool clear, unsigned char val)
359 {
360 unsigned char *px;
362 if (nr_64K_len != 0) {
363 nr_64K_len -= 1;
364 px = nr_64K_px[nr_64K_len];
365 } else {
366 px = g_new (unsigned char, 65536);
367 }
369 if (clear) memset (px, val, 65536);
371 return px;
372 }
374 void
375 nr_pixelstore_64K_free (unsigned char *px)
376 {
377 if (nr_64K_len == nr_64K_size) {
378 nr_64K_size += NR_64K_BLOCK;
379 nr_64K_px = g_renew (unsigned char *, nr_64K_px, nr_64K_size);
380 }
382 nr_64K_px[nr_64K_len] = px;
383 nr_64K_len += 1;
384 }
386 #define NR_256K_BLOCK 32
387 #define NR_256K 262144
388 static unsigned char **nr_256K_px = NULL;
389 static unsigned int nr_256K_len = 0;
390 static unsigned int nr_256K_size = 0;
392 unsigned char *
393 nr_pixelstore_256K_new (bool clear, unsigned char val)
394 {
395 unsigned char *px;
397 if (nr_256K_len != 0) {
398 nr_256K_len -= 1;
399 px = nr_256K_px[nr_256K_len];
400 } else {
401 px = g_new (unsigned char, NR_256K);
402 }
404 if (clear) memset (px, val, NR_256K);
406 return px;
407 }
409 void
410 nr_pixelstore_256K_free (unsigned char *px)
411 {
412 if (nr_256K_len == nr_256K_size) {
413 nr_256K_size += NR_256K_BLOCK;
414 nr_256K_px = g_renew (unsigned char *, nr_256K_px, nr_256K_size);
415 }
417 nr_256K_px[nr_256K_len] = px;
418 nr_256K_len += 1;
419 }
421 #define NR_1M_BLOCK 32
422 #define NR_1M 1048576
423 static unsigned char **nr_1M_px = NULL;
424 static unsigned int nr_1M_len = 0;
425 static unsigned int nr_1M_size = 0;
427 unsigned char *
428 nr_pixelstore_1M_new (bool clear, unsigned char val)
429 {
430 unsigned char *px;
432 if (nr_1M_len != 0) {
433 nr_1M_len -= 1;
434 px = nr_1M_px[nr_1M_len];
435 } else {
436 px = g_new (unsigned char, NR_1M);
437 }
439 if (clear) memset (px, val, NR_1M);
441 return px;
442 }
444 void
445 nr_pixelstore_1M_free (unsigned char *px)
446 {
447 if (nr_1M_len == nr_1M_size) {
448 nr_1M_size += NR_1M_BLOCK;
449 nr_1M_px = g_renew (unsigned char *, nr_1M_px, nr_1M_size);
450 }
452 nr_1M_px[nr_1M_len] = px;
453 nr_1M_len += 1;
454 }
456 /*
457 Local Variables:
458 mode:c++
459 c-file-style:"stroustrup"
460 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
461 indent-tabs-mode:nil
462 fill-column:99
463 End:
464 */
465 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :