void FilterTurbulence::render_cairo(FilterSlot &slot) { cairo_surface_t *input = slot.getcairo(_input); cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); if (!gen->ready()) { Geom::Point ta(fTileX, fTileY); Geom::Point tb(fTileX + fTileWidth, fTileY + fTileHeight); gen->init(seed, Geom::Rect(ta, tb), Geom::Point(XbaseFrequency, YbaseFrequency), stitchTiles, type == TURBULENCE_FRACTALNOISE, numOctaves); } Geom::Affine unit_trans = slot.get_units().get_matrix_primitiveunits2pb().inverse(); Geom::Rect slot_area = slot.get_slot_area(); double x0 = slot_area.min()[Geom::X]; double y0 = slot_area.min()[Geom::Y]; ink_cairo_surface_synthesize(out, Turbulence(*gen, unit_trans, x0, y0)); cairo_surface_mark_dirty(out); slot.set(_output, out); cairo_surface_destroy(out); }
void FilterFlood::render_cairo(FilterSlot &slot) { cairo_surface_t *input = slot.getcairo(_input); double r = SP_RGBA32_R_F(color); double g = SP_RGBA32_G_F(color); double b = SP_RGBA32_B_F(color); double a = opacity; #if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) if (icc) { guchar ru, gu, bu; icc_color_to_sRGB(icc, &ru, &gu, &bu); r = SP_COLOR_U_TO_F(ru); g = SP_COLOR_U_TO_F(gu); b = SP_COLOR_U_TO_F(bu); } #endif cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); // Get filter primitive area in user units Geom::Rect fp = filter_primitive_area( slot.get_units() ); // Convert to Cairo units Geom::Rect fp_cairo = fp * slot.get_units().get_matrix_user2pb(); // Get area in slot (tile to fill) Geom::Rect sa = slot.get_slot_area(); // Get overlap Geom::OptRect optoverlap = intersect( fp_cairo, sa ); if( optoverlap ) { Geom::Rect overlap = *optoverlap; double dx = fp_cairo.min()[Geom::X] - sa.min()[Geom::X]; double dy = fp_cairo.min()[Geom::Y] - sa.min()[Geom::Y]; if( dx < 0.0 ) dx = 0.0; if( dy < 0.0 ) dy = 0.0; cairo_t *ct = cairo_create(out); cairo_set_source_rgba(ct, r, g, b, a); cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); cairo_rectangle(ct, dx, dy, overlap.width(), overlap.height() ); cairo_fill(ct); cairo_destroy(ct); } slot.set(_output, out); cairo_surface_destroy(out); }
void FilterTile::render_cairo(FilterSlot &slot) { static bool tile_warning = false; //IMPLEMENT ME! if (!tile_warning) { g_warning("Renderer for feTile is not implemented."); tile_warning = true; } cairo_surface_t *in = slot.getcairo(_input); slot.set(_output, in); }
int FilterSkeleton::render(FilterSlot &slot, FilterUnits const &/*units*/) { //NRPixBlock *in = slot.get(_input); NRPixBlock *out = new NRPixBlock(); /* Insert rendering code here */ out->empty = FALSE; slot.set(_output, out); return 0; }
int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) { NRPixBlock *in = slot.get(_input); if (!in) { g_warning("Missing source image for feConvolveMatrix (in=%d)", _input); return 1; } if (orderX<=0 || orderY<=0) { g_warning("Empty kernel!"); return 1; } if (targetX<0 || targetX>=orderX || targetY<0 || targetY>=orderY) { g_warning("Invalid target!"); return 1; } if (kernelMatrix.size()!=(unsigned int)(orderX*orderY)) { g_warning("kernelMatrix does not have orderX*orderY elements!"); return 1; } if (bias!=0) { g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix is correct!"); // The SVG specification implies that feConvolveMatrix is defined for premultiplied colors (which makes sense). // It also says that bias should simply be added to the result for each color (without taking the alpha into account) // However, it also says that one purpose of bias is "to have .5 gray value be the zero response of the filter". // It seems sensible to indeed support the latter behaviour instead of the former, but this does appear to go against the standard. // Note that Batik simply does not support bias!=0 } if (edgeMode!=CONVOLVEMATRIX_EDGEMODE_NONE) { g_warning("Inkscape only supports edgeMode=\"none\" (and a filter uses a different one)!"); // Note that to properly support edgeMode the interaction with area_enlarge should be well understood (and probably something needs to change) // area_enlarge should NOT let Inkscape enlarge the area beyond the filter area, it should only enlarge the rendered area if a part of the object is rendered to make it overlapping (enough) with adjacent parts. } NRPixBlock *out = new NRPixBlock; nr_pixblock_setup_fast(out, in->mode, in->area.x0, in->area.y0, in->area.x1, in->area.y1, true); unsigned char *in_data = NR_PIXBLOCK_PX(in); unsigned char *out_data = NR_PIXBLOCK_PX(out); unsigned int const width = in->area.x1 - in->area.x0; unsigned int const height = in->area.y1 - in->area.y0; // Set up predivided kernel matrix std::vector<double> kernel(kernelMatrix); for(size_t i=0; i<kernel.size(); i++) { kernel[i] /= divisor; // The code that creates this object makes sure that divisor != 0 } if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) { if (preserveAlpha) { convolve2D<true,true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); } else { convolve2D<true,false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); } } else { if (preserveAlpha) { convolve2D<false,true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); } else { convolve2D<false,false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); } } out->empty = FALSE; slot.set(_output, out); return 0; }
void FilterTile::render_cairo(FilterSlot &slot) { // FIX ME! static bool tile_warning = false; if (!tile_warning) { g_warning("Renderer for feTile has non-optimal implementation, expect slowness and bugs."); tile_warning = true; } // Fixing isn't so easy as the Inkscape renderer breaks the canvas into "rendering" tiles for // faster rendering. (The "rendering" tiles are not the same as the tiles in this primitive.) // Only if the the feTile tile source falls inside the current "rendering" tile will the tile // image be available. // This input source contains only the "rendering" tile. cairo_surface_t *in = slot.getcairo(_input); // For debugging // static int i = 0; // ++i; // std::stringstream filename; // filename << "dump." << i << ".png"; // cairo_surface_write_to_png( in, filename.str().c_str() ); // This is the feTile source area as determined by the input primitive area (see SVG spec). Geom::Rect tile_area = slot.get_primitive_area(_input); if( tile_area.width() == 0.0 || tile_area.height() == 0.0 ) { slot.set(_output, in); std::cerr << "FileTile::render_cairo: tile has zero width or height" << std::endl; } else { cairo_surface_t *out = ink_cairo_surface_create_identical(in); // color_interpolation_filters for out same as in. copy_cairo_surface_ci(in, out); cairo_t *ct = cairo_create(out); // The rectangle of the "rendering" tile. Geom::Rect sa = slot.get_slot_area(); Geom::Affine trans = slot.get_units().get_matrix_user2pb(); // Create feTile tile ---------------- // Get tile area in pixbuf units (tile transformed). Geom::Rect tt = tile_area * trans; // Shift between "rendering" tile and feTile tile Geom::Point shift = sa.min() - tt.min(); // Create feTile tile surface cairo_surface_t *tile = cairo_surface_create_similar(in, cairo_surface_get_content(in), tt.width(), tt.height()); cairo_t *ct_tile = cairo_create(tile); cairo_set_source_surface(ct_tile, in, shift[Geom::X], shift[Geom::Y]); cairo_paint(ct_tile); // Paint tiles ------------------ // For debugging // std::stringstream filename; // filename << "tile." << i << ".png"; // cairo_surface_write_to_png( tile, filename.str().c_str() ); // Determine number of feTile rows and columns Geom::Rect pr = filter_primitive_area( slot.get_units() ); int tile_cols = ceil( pr.width() / tile_area.width() ); int tile_rows = ceil( pr.height() / tile_area.height() ); // Do tiling (TO DO: restrict to slot area.) for( int col=0; col < tile_cols; ++col ) { for( int row=0; row < tile_rows; ++row ) { Geom::Point offset( col*tile_area.width(), row*tile_area.height() ); offset *= trans; offset[Geom::X] -= trans[4]; offset[Geom::Y] -= trans[5]; cairo_set_source_surface(ct, tile, offset[Geom::X], offset[Geom::Y]); cairo_paint(ct); } } slot.set(_output, out); // Clean up cairo_destroy(ct); cairo_surface_destroy(out); cairo_destroy(ct_tile); cairo_surface_destroy(tile); } }
void FilterImage::render_cairo(FilterSlot &slot) { if (!feImageHref) return; //cairo_surface_t *input = slot.getcairo(_input); // Viewport is filter primitive area (in user coordinates). // Note: viewport calculation in non-trivial. Do not rely // on get_matrix_primitiveunits2pb(). Geom::Rect vp = filter_primitive_area( slot.get_units() ); slot.set_primitive_area(_output, vp); // Needed for tiling double feImageX = vp.min()[Geom::X]; double feImageY = vp.min()[Geom::Y]; double feImageWidth = vp.width(); double feImageHeight = vp.height(); // feImage is suppose to use the same parameters as a normal SVG image. // If a width or height is set to zero, the image is not suppose to be displayed. // This does not seem to be what Firefox or Opera does, nor does the W3C displacement // filter test expect this behavior. If the width and/or height are zero, we use // the width and height of the object bounding box. Geom::Affine m = slot.get_units().get_matrix_user2filterunits().inverse(); Geom::Point bbox_00 = Geom::Point(0,0) * m; Geom::Point bbox_w0 = Geom::Point(1,0) * m; Geom::Point bbox_0h = Geom::Point(0,1) * m; double bbox_width = Geom::distance(bbox_00, bbox_w0); double bbox_height = Geom::distance(bbox_00, bbox_0h); if( feImageWidth == 0 ) feImageWidth = bbox_width; if( feImageHeight == 0 ) feImageHeight = bbox_height; // Internal image, like <use> if (from_element) { if (!SVGElem) return; // TODO: do not recreate the rendering tree every time // TODO: the entire thing is a hack, we should give filter primitives an "update" method // like the one for DrawingItems document->ensureUpToDate(); Drawing drawing; Geom::OptRect optarea = SVGElem->visualBounds(); if (!optarea) return; unsigned const key = SPItem::display_key_new(1); DrawingItem *ai = SVGElem->invoke_show(drawing, key, SP_ITEM_SHOW_DISPLAY); if (!ai) { g_warning("feImage renderer: error creating DrawingItem for SVG Element"); return; } drawing.setRoot(ai); Geom::Rect area = *optarea; Geom::Affine user2pb = slot.get_units().get_matrix_user2pb(); /* FIXME: These variables are currently unused. Why were they calculated? double scaleX = feImageWidth / area.width(); double scaleY = feImageHeight / area.height(); */ Geom::Rect sa = slot.get_slot_area(); cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sa.width(), sa.height()); Inkscape::DrawingContext dc(out, sa.min()); dc.transform(user2pb); // we are now in primitive units dc.translate(feImageX, feImageY); // dc.scale(scaleX, scaleY); No scaling should be done Geom::IntRect render_rect = area.roundOutwards(); // dc.translate(render_rect.min()); This seems incorrect // Update to renderable state drawing.update(render_rect); drawing.render(dc, render_rect); SVGElem->invoke_hide(key); // For the moment, we'll assume that any image is in sRGB color space set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB); slot.set(_output, out); cairo_surface_destroy(out); return; } // External image, like <image> if (!image && !broken_ref) { broken_ref = true; /* TODO: If feImageHref is absolute, then use that (preferably handling the * case that it's not a file URI). Otherwise, go up the tree looking * for an xml:base attribute, and use that as the base URI for resolving * the relative feImageHref URI. Otherwise, if document->base is valid, * then use that as the base URI. Otherwise, use feImageHref directly * (i.e. interpreting it as relative to our current working directory). * (See http://www.w3.org/TR/xmlbase/#resolution .) */ gchar *fullname = feImageHref; if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { // Try to load from relative postion combined with document base if( document ) { fullname = g_build_filename( document->getBase(), feImageHref, NULL ); } } if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { // Should display Broken Image png. g_warning("FilterImage::render: Can not find: %s", feImageHref ); return; } image = Inkscape::Pixbuf::create_from_file(fullname); if( fullname != feImageHref ) g_free( fullname ); if ( !image ) { g_warning("FilterImage::render: failed to load image: %s", feImageHref); return; } broken_ref = false; } if (broken_ref) { return; } cairo_surface_t *image_surface = image->getSurfaceRaw(); Geom::Rect sa = slot.get_slot_area(); cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sa.width(), sa.height()); // For the moment, we'll assume that any image is in sRGB color space // set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB); // This seemed like a sensible thing to do but it breaks filters-displace-01-f.svg cairo_t *ct = cairo_create(out); cairo_translate(ct, -sa.min()[Geom::X], -sa.min()[Geom::Y]); // now ct is in pb coordinates, note the feWidth etc. are in user units ink_cairo_transform(ct, slot.get_units().get_matrix_user2pb()); // now ct is in the coordinates of feImageX etc. // Now that we have the viewport, we must map image inside. // Partially copied from sp-image.cpp. // Do nothing if preserveAspectRatio is "none". if( aspect_align != SP_ASPECT_NONE ) { // Check aspect ratio of image vs. viewport double feAspect = feImageHeight/feImageWidth; double aspect = (double)image->height()/(double)image->width(); bool ratio = (feAspect < aspect); double ax, ay; // Align side switch( aspect_align ) { case SP_ASPECT_XMIN_YMIN: ax = 0.0; ay = 0.0; break; case SP_ASPECT_XMID_YMIN: ax = 0.5; ay = 0.0; break; case SP_ASPECT_XMAX_YMIN: ax = 1.0; ay = 0.0; break; case SP_ASPECT_XMIN_YMID: ax = 0.0; ay = 0.5; break; case SP_ASPECT_XMID_YMID: ax = 0.5; ay = 0.5; break; case SP_ASPECT_XMAX_YMID: ax = 1.0; ay = 0.5; break; case SP_ASPECT_XMIN_YMAX: ax = 0.0; ay = 1.0; break; case SP_ASPECT_XMID_YMAX: ax = 0.5; ay = 1.0; break; case SP_ASPECT_XMAX_YMAX: ax = 1.0; ay = 1.0; break; default: ax = 0.0; ay = 0.0; break; } if( aspect_clip == SP_ASPECT_SLICE ) { // image clipped by viewbox if( ratio ) { // clip top/bottom feImageY -= ay * (feImageWidth * aspect - feImageHeight); feImageHeight = feImageWidth * aspect; } else { // clip sides feImageX -= ax * (feImageHeight / aspect - feImageWidth); feImageWidth = feImageHeight / aspect; } } else { // image fits into viewbox if( ratio ) { // fit to height feImageX += ax * (feImageWidth - feImageHeight / aspect ); feImageWidth = feImageHeight / aspect; } else { // fit to width feImageY += ay * (feImageHeight - feImageWidth * aspect); feImageHeight = feImageWidth * aspect; } } } double scaleX = feImageWidth / image->width(); double scaleY = feImageHeight / image->height(); cairo_translate(ct, feImageX, feImageY); cairo_scale(ct, scaleX, scaleY); cairo_set_source_surface(ct, image_surface, 0, 0); cairo_paint(ct); cairo_destroy(ct); slot.set(_output, out); }
int FilterSpecularLighting::render(FilterSlot &slot, FilterUnits const &units) { NRPixBlock *in = slot.get(_input); if (!in) { g_warning("Missing source image for feSpecularLighting (in=%d)", _input); return 1; } NRPixBlock *out = new NRPixBlock; //Fvector *L = NULL; //vector to the light int w = in->area.x1 - in->area.x0; int h = in->area.y1 - in->area.y0; int x0 = in->area.x0; int y0 = in->area.y0; int i, j; //As long as FilterRes and kernel unit is not supported we hardcode the //default value int dx = 1; //TODO setup int dy = 1; //TODO setup //surface scale Geom::Matrix trans = units.get_matrix_primitiveunits2pb(); gdouble ss = surfaceScale * trans[0]; gdouble ks = specularConstant; //diffuse lighting constant NR::Fvector L, N, LC, H; gdouble inter; nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, in->area.x0, in->area.y0, in->area.x1, in->area.y1, true); unsigned char *data_i = NR_PIXBLOCK_PX (in); unsigned char *data_o = NR_PIXBLOCK_PX (out); //No light, nothing to do switch (light_type) { case DISTANT_LIGHT: //the light vector is constant { DistantLight *dl = new DistantLight(light.distant, lighting_color); dl->light_vector(L); dl->light_components(LC); NR::normalized_sum(H, L, NR::EYE_VECTOR); //finish the work for (i = 0, j = 0; i < w*h; i++) { NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); COMPUTE_INTER(inter, N, H, ks, specularExponent); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); // CLAMP includes rounding! data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); ++j; } out->empty = FALSE; delete dl; } break; case POINT_LIGHT: { PointLight *pl = new PointLight(light.point, lighting_color, trans); pl->light_components(LC); //TODO we need a reference to the filter to determine primitiveUnits //if objectBoundingBox is used, use a different matrix for light_vector // UPDATE: trans is now correct matrix from primitiveUnits to // pixblock coordinates //finish the work for (i = 0, j = 0; i < w*h; i++) { NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); pl->light_vector(L, i % w + x0, i / w + y0, ss * (double) data_i[4*i+3]/ 255); NR::normalized_sum(H, L, NR::EYE_VECTOR); COMPUTE_INTER(inter, N, H, ks, specularExponent); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); ++j; } out->empty = FALSE; delete pl; } break; case SPOT_LIGHT: { SpotLight *sl = new SpotLight(light.spot, lighting_color, trans); //TODO we need a reference to the filter to determine primitiveUnits //if objectBoundingBox is used, use a different matrix for light_vector // UPDATE: trans is now correct matrix from primitiveUnits to // pixblock coordinates //finish the work for (i = 0, j = 0; i < w*h; i++) { NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); sl->light_vector(L, i % w + x0, i / w + y0, ss * (double) data_i[4*i+3]/ 255); sl->light_components(LC, L); NR::normalized_sum(H, L, NR::EYE_VECTOR); COMPUTE_INTER(inter, N, H, ks, specularExponent); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); ++j; } out->empty = FALSE; delete sl; } break; //else unknown light source, doing nothing case NO_LIGHT: default: { if (light_type != NO_LIGHT) g_warning("unknown light source %d", light_type); out->empty = false; } } //finishing slot.set(_output, out); //nr_pixblock_release(in); //delete in; return 0; }