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; }
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; }