From: theAdib Date: Mon, 17 Jan 2011 22:17:48 +0000 (+0100) Subject: remove memory boundries on bitmap renderer, optimize memory usage X-Git-Url: https://git.tokkee.org/?p=inkscape.git;a=commitdiff_plain;h=6c28c26c5dc911196633a0c332b56f712085fa3f remove memory boundries on bitmap renderer, optimize memory usage --- diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index 18a654e27..b68105b30 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -516,7 +516,7 @@ CairoRenderContext::getClipMode(void) const CairoRenderState* CairoRenderContext::_createState(void) { - CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState)); + CairoRenderState *state = (CairoRenderState*)g_try_malloc(sizeof(CairoRenderState)); g_assert( state != NULL ); state->has_filtereffect = FALSE; @@ -796,7 +796,7 @@ CairoRenderContext::setupSurface(double width, double height) case CAIRO_SURFACE_TYPE_PDF: surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height); #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0)) - cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level); + cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level); #endif break; #endif @@ -1420,9 +1420,20 @@ CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsi if (_render_mode == RENDER_MODE_CLIP) return true; - guchar* px_rgba = (guchar*)g_malloc(4 * w * h); - if (!px_rgba) + guchar* px_rgba = NULL; + guint64 size = 4L * (guint64)w * (guint64)h; + + if(size < (guint64)G_MAXSIZE) { + px_rgba = (guchar*)g_try_malloc(4 * w * h); + if (!px_rgba) { + g_warning ("Could not allocate %lu bytes for pixel buffer!", (long unsigned) size); + return false; + } + } else { + g_warning ("the requested memory exceeds the system limit"); return false; + } + float opacity; if (_state->merge_opacity) @@ -1432,15 +1443,16 @@ CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsi // make a copy of the original pixbuf with premultiplied alpha // if we pass the original pixbuf it will get messed up + /// @todo optimize this code, it costs a lot of time for (unsigned i = 0; i < h; i++) { + guchar const *src = px + i * rs; + guint32 *dst = (guint32 *)(px_rgba + i * rs); for (unsigned j = 0; j < w; j++) { - guchar const *src = px + i * rs + j * 4; - guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4); guchar r, g, b, alpha_dst; // calculate opacity-modified alpha alpha_dst = src[3]; - if (opacity != 1.0 && _vector_based_target) + if ((opacity != 1.0) && _vector_based_target) alpha_dst = (guchar)ceil((float)alpha_dst * opacity); // premul alpha (needed because this will be undone by cairo-pdf) @@ -1449,6 +1461,9 @@ CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsi b = src[2]*alpha_dst/255; *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b)); + + dst++; // pointer to 4byte variables + src += 4; // pointer to 1byte variables } } @@ -1497,8 +1512,13 @@ CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector GLYPH_ARRAY_SIZE) - glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs); + if (num_glyphs > GLYPH_ARRAY_SIZE) { + glyphs = (cairo_glyph_t*)g_try_malloc(sizeof(cairo_glyph_t) * num_glyphs); + if(glyphs == NULL) { + g_warning("CairorenderContext::_showGlyphs: can not allocate memory for %d glyphs.", num_glyphs); + return 0; + } + } unsigned int num_invalid_glyphs = 0; unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 67f9354d8..bc3c6c484 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -452,8 +452,23 @@ static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx) Geom::OptRect bbox = item->getBounds(item->i2d_affine(), SPItem::RENDERING_BBOX); - if (!bbox) // no bbox, e.g. empty group + // no bbox, e.g. empty group + if (!bbox) { return; + } + + Geom::Rect docrect(Geom::Rect(Geom::Point(0, 0), SP_OBJECT(item)->document->getDimensions())); + Geom::Rect bboxrect(Geom::Rect(Geom::Point(bbox->min()[Geom::X], bbox->min()[Geom::Y]), Geom::Point(bbox->max()[Geom::X], bbox->max()[Geom::Y]))); + + Geom::OptRect _bbox = Geom::intersect(docrect, bboxrect); + + // assign the object dimension clipped on the document, no need to draw on area not on canvas + bbox = _bbox; + + // no bbox, e.g. empty group + if (!bbox) { + return; + } // The width and height of the bitmap in pixels unsigned width = (unsigned) floor ((bbox->max()[Geom::X] - bbox->min()[Geom::X]) * (res / PX_PER_IN)); diff --git a/src/helper/pixbuf-ops.cpp b/src/helper/pixbuf-ops.cpp index 69becad5d..7ce5c7dd2 100644 --- a/src/helper/pixbuf-ops.cpp +++ b/src/helper/pixbuf-ops.cpp @@ -91,6 +91,19 @@ sp_export_jpg_file(SPDocument *doc, gchar const *filename, else return false; } +/** + generates a bitmap from given items + the bitmap is stored in RAM and not written to file + @param x0 + @param y0 + @param x1 + @param y1 + @param width + @param height + @param xdpi + @param ydpi + @return the created GdkPixbuf structure or NULL if no memory is allocable +*/ GdkPixbuf* sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*/, double x0, double y0, double x1, double y1, @@ -164,6 +177,7 @@ sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*/, dtc[2] = NR_RGBA32_B(bgcolor); dtc[3] = NR_RGBA32_A(bgcolor); + // fill pixelblock using background colour for (gsize fy = 0; fy < height; fy++) { guchar *p = NR_PIXBLOCK_PX(&B) + fy * (gsize)B.rs; for (unsigned int fx = 0; fx < width; fx++) { diff --git a/src/libnr/nr-pixblock.cpp b/src/libnr/nr-pixblock.cpp index d69b6fe54..b95d8db8d 100644 --- a/src/libnr/nr-pixblock.cpp +++ b/src/libnr/nr-pixblock.cpp @@ -41,6 +41,13 @@ nr_pixblock_setup_fast (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, i h = y1 - y0; bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4; + guint64 sizel = (guint64)bpp * (guint64)w * (guint64)h; + + if(sizel > (guint64)G_MAXSIZE) { + g_warning ("the requested memory exceeds the system limit"); + return; + } + size = bpp * w * h; if (size <= NR_TINY_MAX) { @@ -67,8 +74,9 @@ nr_pixblock_setup_fast (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, i if (size > 100000000) { // Don't even try to allocate more than 100Mb (5000x5000 RGBA // pixels). It'll just bog the system down even if successful. FIXME: // Can anyone suggest something better than the magic number? - g_warning ("%lu bytes requested for pixel buffer, I won't try to allocate that.", (long unsigned) size); - return; + g_warning ("%lu bytes requested for pixel buffer, this will slow down the system.", (long unsigned) size); + // do not quit here, let the system decide for RAM usage + // return; } pb->data.px = g_try_new (unsigned char, size); if (pb->data.px == NULL) { // memory allocation failed @@ -144,7 +152,7 @@ nr_pixblock_setup_extern (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, w = x1 - x0; bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4; - pb->size = NR_PIXBLOCK_SIZE_STATIC; + pb->size = NR_PIXBLOCK_SIZE_STATIC; pb->mode = mode; pb->empty = empty; pb->visible_area.x0 = pb->area.x0 = x0; @@ -157,9 +165,9 @@ nr_pixblock_setup_extern (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, g_assert (pb->data.px != NULL); if (clear) { if (rs == bpp * w) { - /// \todo How do you recognise if - /// px was an uncleared tiny buffer? - if (pb->data.px) + /// \todo How do you recognise if + /// px was an uncleared tiny buffer? + if (pb->data.px) memset (pb->data.px, 0x0, bpp * (y1 - y0) * w); } else { int y; @@ -289,7 +297,7 @@ nr_pixelstore_4K_new (bool clear, unsigned char val) } else { px = g_new (unsigned char, 4096); } - + if (clear) memset (px, val, 4096); return px; @@ -323,7 +331,7 @@ nr_pixelstore_16K_new (bool clear, unsigned char val) } else { px = g_new (unsigned char, 16384); } - + if (clear) memset (px, val, 16384); return px;