~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/helper/png-write.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_PNG_WRITE_C__
 
2
 
 
3
/*
 
4
 * PNG file format utilities
 
5
 *
 
6
 * Authors:
 
7
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
8
 *   Whoever wrote this example in libpng documentation
 
9
 *
 
10
 * Copyright (C) 1999-2002 authors
 
11
 *
 
12
 * Released under GNU GPL, read the file 'COPYING' for more information
 
13
 */
 
14
 
 
15
#ifdef HAVE_CONFIG_H
 
16
# include "config.h"
 
17
#endif
 
18
 
 
19
#include <interface.h>
 
20
#include <libnr/nr-pixops.h>
 
21
#include <libnr/nr-translate-scale-ops.h>
 
22
#include <2geom/rect.h>
 
23
#include <glib/gmessages.h>
 
24
#include <png.h>
 
25
#include "png-write.h"
 
26
#include "io/sys.h"
 
27
#include <display/nr-arena-item.h>
 
28
#include <display/nr-arena.h>
 
29
#include <document.h>
 
30
#include <sp-item.h>
 
31
#include <sp-root.h>
 
32
#include <sp-defs.h>
 
33
#include "preferences.h"
 
34
#include "rdf.h"
 
35
 
 
36
/* This is an example of how to use libpng to read and write PNG files.
 
37
 * The file libpng.txt is much more verbose then this.  If you have not
 
38
 * read it, do so first.  This was designed to be a starting point of an
 
39
 * implementation.  This is not officially part of libpng, and therefore
 
40
 * does not require a copyright notice.
 
41
 *
 
42
 * This file does not currently compile, because it is missing certain
 
43
 * parts, like allocating memory to hold an image.  You will have to
 
44
 * supply these parts to get it to compile.  For an example of a minimal
 
45
 * working PNG reader/writer, see pngtest.c, included in this distribution.
 
46
 */
 
47
 
 
48
static unsigned int const MAX_STRIPE_SIZE = 1024*1024;
 
49
 
 
50
struct SPEBP {
 
51
    unsigned long int width, height, sheight;
 
52
    guchar r, g, b, a;
 
53
    NRArenaItem *root; // the root arena item to show; it is assumed that all unneeded items are hidden
 
54
    guchar *px;
 
55
    unsigned (*status)(float, void *);
 
56
    void *data;
 
57
};
 
58
 
 
59
/* write a png file */
 
60
 
 
61
typedef struct SPPNGBD {
 
62
    guchar const *px;
 
63
    int rowstride;
 
64
} SPPNGBD;
 
65
 
 
66
/**
 
67
 * A simple wrapper to list png_text.
 
68
 */
 
69
class PngTextList {
 
70
public:
 
71
    PngTextList() : count(0), textItems(0) {}
 
72
    ~PngTextList();
 
73
 
 
74
    void add(gchar const* key, gchar const* text);
 
75
    gint getCount() {return count;}
 
76
    png_text* getPtext() {return textItems;}
 
77
 
 
78
private:
 
79
    gint count;
 
80
    png_text* textItems;
 
81
};
 
82
 
 
83
PngTextList::~PngTextList() {
 
84
    for (gint i = 0; i < count; i++) {
 
85
        if (textItems[i].key) {
 
86
            g_free(textItems[i].key);
 
87
        }
 
88
        if (textItems[i].text) {
 
89
            g_free(textItems[i].text);
 
90
        }
 
91
    }
 
92
}
 
93
 
 
94
void PngTextList::add(gchar const* key, gchar const* text)
 
95
{
 
96
    if (count < 0) {
 
97
        count = 0;
 
98
        textItems = 0;
 
99
    }
 
100
    png_text* tmp = (count > 0) ? g_try_renew(png_text, textItems, count + 1): g_try_new(png_text, 1);
 
101
    if (tmp) {
 
102
        textItems = tmp;
 
103
        count++;
 
104
 
 
105
        png_text* item = &(textItems[count - 1]);
 
106
        item->compression = PNG_TEXT_COMPRESSION_NONE;
 
107
        item->key = g_strdup(key);
 
108
        item->text = g_strdup(text);
 
109
        item->text_length = 0;
 
110
#ifdef PNG_iTXt_SUPPORTED
 
111
        item->itxt_length = 0;
 
112
        item->lang = 0;
 
113
        item->lang_key = 0;
 
114
#endif // PNG_iTXt_SUPPORTED
 
115
    } else {
 
116
        g_warning("Unable to allocate arrary for %d PNG text data.", count);
 
117
        textItems = 0;
 
118
        count = 0;
 
119
    }
 
120
}
 
121
 
 
122
static bool
 
123
sp_png_write_rgba_striped(SPDocument *doc,
 
124
                          gchar const *filename, unsigned long int width, unsigned long int height, double xdpi, double ydpi,
 
125
                          int (* get_rows)(guchar const **rows, int row, int num_rows, void *data),
 
126
                          void *data)
 
127
{
 
128
    struct SPEBP *ebp = (struct SPEBP *) data;
 
129
    FILE *fp;
 
130
    png_structp png_ptr;
 
131
    png_infop info_ptr;
 
132
    png_color_8 sig_bit;
 
133
    png_uint_32 r;
 
134
 
 
135
    g_return_val_if_fail(filename != NULL, false);
 
136
    g_return_val_if_fail(data != NULL, false);
 
137
 
 
138
    /* open the file */
 
139
 
 
140
    Inkscape::IO::dump_fopen_call(filename, "M");
 
141
    fp = Inkscape::IO::fopen_utf8name(filename, "wb");
 
142
    g_return_val_if_fail(fp != NULL, false);
 
143
 
 
144
    /* Create and initialize the png_struct with the desired error handler
 
145
     * functions.  If you want to use the default stderr and longjump method,
 
146
     * you can supply NULL for the last three parameters.  We also check that
 
147
     * the library version is compatible with the one used at compile time,
 
148
     * in case we are using dynamically linked libraries.  REQUIRED.
 
149
     */
 
150
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 
151
 
 
152
    if (png_ptr == NULL) {
 
153
        fclose(fp);
 
154
        return false;
 
155
    }
 
156
 
 
157
    /* Allocate/initialize the image information data.  REQUIRED */
 
158
    info_ptr = png_create_info_struct(png_ptr);
 
159
    if (info_ptr == NULL) {
 
160
        fclose(fp);
 
161
        png_destroy_write_struct(&png_ptr, NULL);
 
162
        return false;
 
163
    }
 
164
 
 
165
    /* Set error handling.  REQUIRED if you aren't supplying your own
 
166
     * error hadnling functions in the png_create_write_struct() call.
 
167
     */
 
168
    if (setjmp(png_ptr->jmpbuf)) {
 
169
        /* If we get here, we had a problem reading the file */
 
170
        fclose(fp);
 
171
        png_destroy_write_struct(&png_ptr, &info_ptr);
 
172
        return false;
 
173
    }
 
174
 
 
175
    /* set up the output control if you are using standard C streams */
 
176
    png_init_io(png_ptr, fp);
 
177
 
 
178
    /* Set the image information here.  Width and height are up to 2^31,
 
179
     * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
 
180
     * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
 
181
     * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
 
182
     * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
 
183
     * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
 
184
     * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
 
185
     */
 
186
    png_set_IHDR(png_ptr, info_ptr,
 
187
                 width,
 
188
                 height,
 
189
                 8, /* bit_depth */
 
190
                 PNG_COLOR_TYPE_RGB_ALPHA,
 
191
                 PNG_INTERLACE_NONE,
 
192
                 PNG_COMPRESSION_TYPE_BASE,
 
193
                 PNG_FILTER_TYPE_BASE);
 
194
 
 
195
    /* otherwise, if we are dealing with a color image then */
 
196
    sig_bit.red = 8;
 
197
    sig_bit.green = 8;
 
198
    sig_bit.blue = 8;
 
199
    /* if the image has an alpha channel then */
 
200
    sig_bit.alpha = 8;
 
201
    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
 
202
 
 
203
    PngTextList textList;
 
204
 
 
205
    textList.add("Software", "www.inkscape.org"); // Made by Inkscape comment
 
206
    {
 
207
        const gchar* pngToDc[] = {"Title", "title",
 
208
                               "Author", "creator",
 
209
                               "Description", "description",
 
210
                               //"Copyright", "",
 
211
                               "Creation Time", "date",
 
212
                               //"Disclaimer", "",
 
213
                               //"Warning", "",
 
214
                               "Source", "source"
 
215
                               //"Comment", ""
 
216
        };
 
217
        for (size_t i = 0; i < G_N_ELEMENTS(pngToDc); i += 2) {
 
218
            struct rdf_work_entity_t * entity = rdf_find_entity ( pngToDc[i + 1] );
 
219
            if (entity) {
 
220
                gchar const* data = rdf_get_work_entity(doc, entity);
 
221
                if (data && *data) {
 
222
                    textList.add(pngToDc[i], data);
 
223
                }
 
224
            } else {
 
225
                g_warning("Unable to find entity [%s]", pngToDc[i + 1]);
 
226
            }
 
227
        }
 
228
 
 
229
 
 
230
        struct rdf_license_t *license =  rdf_get_license(doc);
 
231
        if (license) {
 
232
            if (license->name && license->uri) {
 
233
                gchar* tmp = g_strdup_printf("%s %s", license->name, license->uri);
 
234
                textList.add("Copyright", tmp);
 
235
                g_free(tmp);
 
236
            } else if (license->name) {
 
237
                textList.add("Copyright", license->name);
 
238
            } else if (license->uri) {
 
239
                textList.add("Copyright", license->uri);
 
240
            }
 
241
        }
 
242
    }
 
243
    if (textList.getCount() > 0) {
 
244
        png_set_text(png_ptr, info_ptr, textList.getPtext(), textList.getCount());
 
245
    }
 
246
 
 
247
    /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */
 
248
    /* note that if sRGB is present the cHRM chunk must be ignored
 
249
     * on read and must be written in accordance with the sRGB profile */
 
250
    png_set_pHYs(png_ptr, info_ptr, unsigned(xdpi / 0.0254 + 0.5), unsigned(ydpi / 0.0254 + 0.5), PNG_RESOLUTION_METER);
 
251
 
 
252
    /* Write the file header information.  REQUIRED */
 
253
    png_write_info(png_ptr, info_ptr);
 
254
 
 
255
    /* Once we write out the header, the compression type on the text
 
256
     * chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or
 
257
     * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again
 
258
     * at the end.
 
259
     */
 
260
 
 
261
    /* set up the transformations you want.  Note that these are
 
262
     * all optional.  Only call them if you want them.
 
263
     */
 
264
 
 
265
    /* --- CUT --- */
 
266
 
 
267
    /* The easiest way to write the image (you may have a different memory
 
268
     * layout, however, so choose what fits your needs best).  You need to
 
269
     * use the first method if you aren't handling interlacing yourself.
 
270
     */
 
271
 
 
272
    png_bytep* row_pointers = new png_bytep[ebp->sheight];
 
273
 
 
274
    r = 0;
 
275
    while (r < static_cast< png_uint_32 > (height) ) {
 
276
        int n = get_rows((unsigned char const **) row_pointers, r, height-r, data);
 
277
        if (!n) break;
 
278
        png_write_rows(png_ptr, row_pointers, n);
 
279
        r += n;
 
280
    }
 
281
 
 
282
    delete[] row_pointers;
 
283
 
 
284
    /* You can write optional chunks like tEXt, zTXt, and tIME at the end
 
285
     * as well.
 
286
     */
 
287
 
 
288
    /* It is REQUIRED to call this to finish writing the rest of the file */
 
289
    png_write_end(png_ptr, info_ptr);
 
290
 
 
291
    /* if you allocated any text comments, free them here */
 
292
 
 
293
    /* clean up after the write, and free any memory allocated */
 
294
    png_destroy_write_struct(&png_ptr, &info_ptr);
 
295
 
 
296
    /* close the file */
 
297
    fclose(fp);
 
298
 
 
299
    /* that's it */
 
300
    return true;
 
301
}
 
302
 
 
303
 
 
304
/**
 
305
 *
 
306
 */
 
307
static int
 
308
sp_export_get_rows(guchar const **rows, int row, int num_rows, void *data)
 
309
{
 
310
    struct SPEBP *ebp = (struct SPEBP *) data;
 
311
 
 
312
    if (ebp->status) {
 
313
        if (!ebp->status((float) row / ebp->height, ebp->data)) return 0;
 
314
    }
 
315
 
 
316
    num_rows = MIN(num_rows, static_cast<int>(ebp->sheight));
 
317
    num_rows = MIN(num_rows, static_cast<int>(ebp->height - row));
 
318
 
 
319
    /* Set area of interest */
 
320
    // bbox is now set to the entire image to prevent discontinuities
 
321
    // in the image when blur is used (the borders may still be a bit
 
322
    // off, but that's less noticeable).
 
323
    NRRectL bbox;
 
324
    bbox.x0 = 0;
 
325
    bbox.y0 = row;
 
326
    bbox.x1 = ebp->width;
 
327
    bbox.y1 = row + num_rows;
 
328
    /* Update to renderable state */
 
329
    NRGC gc(NULL);
 
330
    gc.transform.setIdentity();
 
331
 
 
332
    nr_arena_item_invoke_update(ebp->root, &bbox, &gc,
 
333
           NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
 
334
 
 
335
    NRPixBlock pb;
 
336
    nr_pixblock_setup_extern(&pb, NR_PIXBLOCK_MODE_R8G8B8A8N,
 
337
                             bbox.x0, bbox.y0, bbox.x1, bbox.y1,
 
338
                             ebp->px, 4 * ebp->width, FALSE, FALSE);
 
339
 
 
340
    for (int r = 0; r < num_rows; r++) {
 
341
        guchar *p = NR_PIXBLOCK_PX(&pb) + r * pb.rs;
 
342
        for (int c = 0; c < static_cast<int>(ebp->width); c++) {
 
343
            *p++ = ebp->r;
 
344
            *p++ = ebp->g;
 
345
            *p++ = ebp->b;
 
346
            *p++ = ebp->a;
 
347
        }
 
348
    }
 
349
 
 
350
    /* Render */
 
351
    nr_arena_item_invoke_render(NULL, ebp->root, &bbox, &pb, 0);
 
352
 
 
353
    for (int r = 0; r < num_rows; r++) {
 
354
        rows[r] = NR_PIXBLOCK_PX(&pb) + r * pb.rs;
 
355
    }
 
356
 
 
357
    nr_pixblock_release(&pb);
 
358
 
 
359
    return num_rows;
 
360
}
 
361
 
 
362
/**
 
363
 * Hide all items that are not listed in list, recursively, skipping groups and defs.
 
364
 */
 
365
static void
 
366
hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey)
 
367
{
 
368
    if ( SP_IS_ITEM(o)
 
369
         && !SP_IS_DEFS(o)
 
370
         && !SP_IS_ROOT(o)
 
371
         && !SP_IS_GROUP(o)
 
372
         && !g_slist_find(list, o) )
 
373
    {
 
374
        sp_item_invoke_hide(SP_ITEM(o), dkey);
 
375
    }
 
376
 
 
377
    // recurse
 
378
    if (!g_slist_find(list, o)) {
 
379
        for (SPObject *child = sp_object_first_child(o) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
380
            hide_other_items_recursively(child, list, dkey);
 
381
        }
 
382
    }
 
383
}
 
384
 
 
385
 
 
386
/**
 
387
 * Export the given document as a Portable Network Graphics (PNG) file.
 
388
 *
 
389
 * \return true if succeeded (or if no action was taken), false if an error occurred.
 
390
 */
 
391
bool sp_export_png_file (SPDocument *doc, gchar const *filename,
 
392
                   double x0, double y0, double x1, double y1,
 
393
                   unsigned long int width, unsigned long int height, double xdpi, double ydpi,
 
394
                   unsigned long bgcolor,
 
395
                   unsigned int (*status) (float, void *),
 
396
                   void *data, bool force_overwrite,
 
397
                   GSList *items_only)
 
398
{
 
399
    return sp_export_png_file(doc, filename, Geom::Rect(Geom::Point(x0,y0),Geom::Point(x1,y1)),
 
400
                              width, height, xdpi, ydpi, bgcolor, status, data, force_overwrite, items_only);
 
401
}
 
402
bool
 
403
sp_export_png_file(SPDocument *doc, gchar const *filename,
 
404
                   Geom::Rect const &area,
 
405
                   unsigned long width, unsigned long height, double xdpi, double ydpi,
 
406
                   unsigned long bgcolor,
 
407
                   unsigned (*status)(float, void *),
 
408
                   void *data, bool force_overwrite,
 
409
                   GSList *items_only)
 
410
{
 
411
    g_return_val_if_fail(doc != NULL, false);
 
412
    g_return_val_if_fail(filename != NULL, false);
 
413
    g_return_val_if_fail(width >= 1, false);
 
414
    g_return_val_if_fail(height >= 1, false);
 
415
    g_return_val_if_fail(!area.hasZeroArea(), false);
 
416
 
 
417
    //Make relative paths absolute, if possible:
 
418
    gchar *path = 0;
 
419
    if (!g_path_is_absolute(filename) && doc->uri) {
 
420
        gchar *dirname = g_path_get_dirname(doc->uri);
 
421
        if (dirname) {
 
422
            path = g_build_filename(dirname, filename, NULL);
 
423
            g_free(dirname);
 
424
        }
 
425
    }
 
426
    if (!path) {
 
427
        path = g_strdup(filename);
 
428
    }
 
429
 
 
430
    if (!force_overwrite && !sp_ui_overwrite_file(path)) {
 
431
        /* Remark: We return true so as not to invoke an error dialog in case export is cancelled
 
432
           by the user; currently this is safe because the callers only act when false is returned.
 
433
           If this changes in the future we need better distinction of return types (e.g., use int)
 
434
        */
 
435
        return true;
 
436
    }
 
437
 
 
438
    sp_document_ensure_up_to_date(doc);
 
439
 
 
440
    /* Calculate translation by transforming to document coordinates (flipping Y)*/
 
441
    Geom::Point translation = Geom::Point(-area[Geom::X][0], area[Geom::Y][1] - sp_document_height(doc));
 
442
 
 
443
    /*  This calculation is only valid when assumed that (x0,y0)= area.corner(0) and (x1,y1) = area.corner(2)
 
444
     * 1) a[0] * x0 + a[2] * y1 + a[4] = 0.0
 
445
     * 2) a[1] * x0 + a[3] * y1 + a[5] = 0.0
 
446
     * 3) a[0] * x1 + a[2] * y1 + a[4] = width
 
447
     * 4) a[1] * x0 + a[3] * y0 + a[5] = height
 
448
     * 5) a[1] = 0.0;
 
449
     * 6) a[2] = 0.0;
 
450
     *
 
451
     * (1,3) a[0] * x1 - a[0] * x0 = width
 
452
     * a[0] = width / (x1 - x0)
 
453
     * (2,4) a[3] * y0 - a[3] * y1 = height
 
454
     * a[3] = height / (y0 - y1)
 
455
     * (1) a[4] = -a[0] * x0
 
456
     * (2) a[5] = -a[3] * y1
 
457
     */
 
458
 
 
459
    Geom::Matrix const affine(Geom::Translate(translation)
 
460
                            * Geom::Scale(width / area.width(),
 
461
                                        height / area.height()));
 
462
 
 
463
    //SP_PRINT_MATRIX("SVG2PNG", &affine);
 
464
 
 
465
    struct SPEBP ebp;
 
466
    ebp.width  = width;
 
467
    ebp.height = height;
 
468
    ebp.r      = NR_RGBA32_R(bgcolor);
 
469
    ebp.g      = NR_RGBA32_G(bgcolor);
 
470
    ebp.b      = NR_RGBA32_B(bgcolor);
 
471
    ebp.a      = NR_RGBA32_A(bgcolor);
 
472
 
 
473
    /* Create new arena */
 
474
    NRArena *const arena = NRArena::create();
 
475
    // export with maximum blur rendering quality
 
476
    nr_arena_set_renderoffscreen(arena);
 
477
    unsigned const dkey = sp_item_display_key_new(1);
 
478
 
 
479
    /* Create ArenaItems and set transform */
 
480
    ebp.root = sp_item_invoke_show(SP_ITEM(sp_document_root(doc)), arena, dkey, SP_ITEM_SHOW_DISPLAY);
 
481
    nr_arena_item_set_transform(NR_ARENA_ITEM(ebp.root), affine);
 
482
 
 
483
    // We show all and then hide all items we don't want, instead of showing only requested items,
 
484
    // because that would not work if the shown item references something in defs
 
485
    if (items_only) {
 
486
        hide_other_items_recursively(sp_document_root(doc), items_only, dkey);
 
487
    }
 
488
 
 
489
    ebp.status = status;
 
490
    ebp.data   = data;
 
491
 
 
492
    bool write_status;
 
493
    if ((width < 256) || ((width * height) < 32768)) {
 
494
        ebp.px = nr_pixelstore_64K_new(FALSE, 0);
 
495
        ebp.sheight = 65536 / (4 * width);
 
496
        write_status = sp_png_write_rgba_striped(doc, path, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
 
497
        nr_pixelstore_64K_free(ebp.px);
 
498
    } else {
 
499
        ebp.sheight = 64;
 
500
        ebp.px = g_try_new(guchar, 4 * ebp.sheight * width);
 
501
        write_status = sp_png_write_rgba_striped(doc, path, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
 
502
        g_free(ebp.px);
 
503
    }
 
504
 
 
505
    // Hide items, this releases arenaitem
 
506
    sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), dkey);
 
507
 
 
508
    /* Free arena */
 
509
    nr_object_unref((NRObject *) arena);
 
510
 
 
511
    g_free(path);
 
512
 
 
513
    return write_status;
 
514
}
 
515
 
 
516
 
 
517
/*
 
518
  Local Variables:
 
519
  mode:c++
 
520
  c-file-style:"stroustrup"
 
521
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
522
  indent-tabs-mode:nil
 
523
  fill-column:99
 
524
  End:
 
525
*/
 
526
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :