NRPixBlock *FilterSlot::get(int slot_nr) { int index = _get_index(slot_nr); assert(index >= 0); /* If we didn't have the specified image, but we could create it * from the other information we have, let's do that */ if (_slot[index] == NULL && (slot_nr == NR_FILTER_SOURCEALPHA || slot_nr == NR_FILTER_BACKGROUNDIMAGE || slot_nr == NR_FILTER_BACKGROUNDALPHA || slot_nr == NR_FILTER_FILLPAINT || slot_nr == NR_FILTER_STROKEPAINT)) { /* If needed, fetch background */ if (slot_nr == NR_FILTER_BACKGROUNDIMAGE) { NRPixBlock *pb; pb = nr_arena_item_get_background(_arena_item); if (pb) { pb->empty = false; this->set(NR_FILTER_BACKGROUNDIMAGE, pb); } else { NRPixBlock *source = this->get(NR_FILTER_SOURCEGRAPHIC); pb = new NRPixBlock(); if (!pb) return NULL; // Allocation failed nr_pixblock_setup_fast(pb, source->mode, source->area.x0, source->area.y0, source->area.x1, source->area.y1, true); if (pb->size != NR_PIXBLOCK_SIZE_TINY && pb->data.px == NULL) { // allocation failed delete pb; return NULL; } pb->empty = FALSE; this->set(NR_FILTER_BACKGROUNDIMAGE, pb); } } else if (slot_nr == NR_FILTER_SOURCEALPHA) { /* If only a alpha channel is needed, strip it from full image */ NRPixBlock *src = get(NR_FILTER_SOURCEGRAPHIC); NRPixBlock *sa = filter_get_alpha(src); set(NR_FILTER_SOURCEALPHA, sa); } else if (slot_nr == NR_FILTER_BACKGROUNDALPHA) { NRPixBlock *src = get(NR_FILTER_BACKGROUNDIMAGE); NRPixBlock *ba = filter_get_alpha(src); set(NR_FILTER_BACKGROUNDALPHA, ba); } else if (slot_nr == NR_FILTER_FILLPAINT) { /* When a paint is needed, fetch it from arena item */ // TODO } else if (slot_nr == NR_FILTER_STROKEPAINT) { // TODO } } if (_slot[index]) { _slot[index]->empty = false; } assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr); return _slot[index]; }
void nr_gdk_draw_rgba32_solid (GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint w, gint h, guint32 rgba) { NRPixBlock pb; nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, w, h, FALSE); nr_render_rgba32_rgb (NR_PIXBLOCK_PX (&pb), w, h, pb.rs, x, y, rgba); gdk_draw_rgb_image (drawable, gc, x, y, w, h, GDK_RGB_DITHER_MAX, NR_PIXBLOCK_PX (&pb), pb.rs); nr_pixblock_release (&pb); }
void nr_gdk_draw_gray_garbage (GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint w, gint h) { gint xx, yy; for (yy = y; yy < y + h; yy += 64) { for (xx = x; xx < x + w; xx += 64) { NRPixBlock pb; gint ex, ey; ex = MIN (xx + 64, x + w); ey = MIN (yy + 64, y + h); nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8, xx, yy, ex, ey, FALSE); nr_pixblock_render_gray_noise (&pb, NULL); gdk_draw_rgb_image (drawable, gc, xx, yy, ex - xx, ey - yy, GDK_RGB_DITHER_NONE, NR_PIXBLOCK_PX (&pb), pb.rs); nr_pixblock_release (&pb); } } }
NRPixBlock *filter_get_alpha(NRPixBlock *src) { NRPixBlock *dst = new NRPixBlock; nr_pixblock_setup_fast(dst, NR_PIXBLOCK_MODE_R8G8B8A8P, src->area.x0, src->area.y0, src->area.x1, src->area.y1, false); if (!dst || (dst->size != NR_PIXBLOCK_SIZE_TINY && dst->data.px == NULL)) { g_warning("Memory allocation failed in filter_get_alpha"); delete dst; return NULL; } nr_blit_pixblock_pixblock(dst, src); unsigned char *data = NR_PIXBLOCK_PX(dst); int end = dst->rs * (dst->area.y1 - dst->area.y0); for (int i = 0 ; i < end ; i += 4) { data[i + 0] = 0; data[i + 1] = 0; data[i + 2] = 0; } dst->empty = false; return dst; }
void nr_pixblock_draw_line_rgba32 (NRPixBlock *d, long x0, long y0, long x1, long y1, short /*first*/, unsigned long rgba) { long deltax, deltay, xinc1, xinc2, yinc1, yinc2; long den, num, numadd, numpixels; long x, y, curpixel; /* Pixblock */ int dbpp; NRPixBlock spb; unsigned char *spx; if (x1 >= x0) { deltax = x1 - x0; xinc1 = 1; xinc2 = 1; } else { deltax = x0 - x1; xinc1 = -1; xinc2 = -1; } if (y1 >= y0) { deltay = y1 - y0; yinc1 = 1; yinc2 = 1; } else { deltay = y0 - y1; yinc1 = -1; yinc2 = -1; } if (deltax >= deltay) { xinc1 = 0; yinc2 = 0; den = deltax; num = deltax / 2; numadd = deltay; numpixels = deltax; } else { xinc2 = 0; yinc1 = 0; den = deltay; num = deltay / 2; numadd = deltax; numpixels = deltay; } /* We can be quite sure 1x1 pixblock is TINY */ nr_pixblock_setup_fast (&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, 1, 1, 0); spb.empty = 0; spx = NR_PIXBLOCK_PX (&spb); spx[0] = NR_RGBA32_R (rgba); spx[1] = NR_RGBA32_G (rgba); spx[2] = NR_RGBA32_B (rgba); spx[3] = NR_RGBA32_A (rgba); dbpp = NR_PIXBLOCK_BPP (d); x = x0; y = y0; for (curpixel = 0; curpixel <= numpixels; curpixel++) { if ((x >= d->area.x0) && (y >= d->area.y0) && (x < d->area.x1) && (y < d->area.y1)) { nr_compose_pixblock_pixblock_pixel (d, NR_PIXBLOCK_PX (d) + (y - d->area.y0) * d->rs + (x - d->area.x0) * dbpp, &spb, spx); } num += numadd; if (num >= den) { num -= den; x += xinc1; y += yinc1; } x += xinc2; y += yinc2; } nr_pixblock_release (&spb); }
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; }
static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf) { SPCanvasArena *arena; gint bw, bh, sw, sh; gint x, y; arena = SP_CANVAS_ARENA (item); nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER, NR_ARENA_ITEM_STATE_NONE); if (buf->is_bg) { sp_canvas_clear_buffer (buf); buf->is_bg = FALSE; buf->is_buf = TRUE; } bw = buf->rect.x1 - buf->rect.x0; bh = buf->rect.y1 - buf->rect.y0; if ((bw < 1) || (bh < 1)) return; /* 65536 is max cached buffer and we need 4 channels */ if (bw * bh < 16384) { /* We can go with single buffer */ sw = bw; sh = bh; } else if (bw <= 2048) { /* Go with row buffer */ sw = bw; sh = 16384 / bw; } else if (bh <= 2048) { /* Go with column buffer */ sw = 16384 / bh; sh = bh; } else { sw = 128; sh = 128; } /* fixme: RGB transformed bitmap blit is not implemented (Lauris) */ /* And even if it would be, unless it uses MMX there is little reason to go RGB */ #define STRICT_RGBA for (y = buf->rect.y0; y < buf->rect.y1; y += sh) { for (x = buf->rect.x0; x < buf->rect.x1; x += sw) { NRRectL area; #ifdef STRICT_RGBA NRPixBlock pb; #endif NRPixBlock cb; area.x0 = x; area.y0 = y; area.x1 = MIN (x + sw, buf->rect.x1); area.y1 = MIN (y + sh, buf->rect.y1); #ifdef STRICT_RGBA nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1, TRUE); /* fixme: */ pb.empty = FALSE; #endif nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8, area.x0, area.y0, area.x1, area.y1, buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + 3 * (x - buf->rect.x0), buf->buf_rowstride, FALSE, FALSE); #ifdef STRICT_RGBA nr_arena_item_invoke_render (arena->root, &area, &pb, 0); nr_blit_pixblock_pixblock (&cb, &pb); nr_pixblock_release (&pb); #else nr_arena_item_invoke_render (arena->root, &area, &cb, 0); #endif nr_pixblock_release (&cb); } } }
void FilterSlot::set(int slot_nr, NRPixBlock *pb) { /* Unnamed slot is for saving filter primitive results, when parameter * 'result' is not set. Only the filter immediately after this one * can access unnamed results, so we don't have to worry about overwriting * previous results in filter chain. On the other hand, we may not * overwrite any other image with this one, because they might be * accessed later on. */ int index = ((slot_nr != NR_FILTER_SLOT_NOT_SET) ? _get_index(slot_nr) : _get_index(NR_FILTER_UNNAMED_SLOT)); assert(index >= 0); // Unnamed slot is only for Inkscape::Filters::FilterSlot internal use. assert(slot_nr != NR_FILTER_UNNAMED_SLOT); assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr); if (slot_nr == NR_FILTER_SOURCEGRAPHIC || slot_nr == NR_FILTER_BACKGROUNDIMAGE) { Geom::Matrix trans = units.get_matrix_display2pb(); if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) { NRPixBlock *trans_pb = new NRPixBlock; int x0 = pb->area.x0; int y0 = pb->area.y0; int x1 = pb->area.x1; int y1 = pb->area.y1; int min_x = _min4(trans[0] * x0 + trans[2] * y0 + trans[4], trans[0] * x0 + trans[2] * y1 + trans[4], trans[0] * x1 + trans[2] * y0 + trans[4], trans[0] * x1 + trans[2] * y1 + trans[4]); int max_x = _max4(trans[0] * x0 + trans[2] * y0 + trans[4], trans[0] * x0 + trans[2] * y1 + trans[4], trans[0] * x1 + trans[2] * y0 + trans[4], trans[0] * x1 + trans[2] * y1 + trans[4]); int min_y = _min4(trans[1] * x0 + trans[3] * y0 + trans[5], trans[1] * x0 + trans[3] * y1 + trans[5], trans[1] * x1 + trans[3] * y0 + trans[5], trans[1] * x1 + trans[3] * y1 + trans[5]); int max_y = _max4(trans[1] * x0 + trans[3] * y0 + trans[5], trans[1] * x0 + trans[3] * y1 + trans[5], trans[1] * x1 + trans[3] * y0 + trans[5], trans[1] * x1 + trans[3] * y1 + trans[5]); nr_pixblock_setup_fast(trans_pb, pb->mode, min_x, min_y, max_x, max_y, true); if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) { /* TODO: this gets hit occasionally. Worst case scenario: * images are exported in horizontal stripes. One stripe * is not too high, but can get thousands of pixels wide. * Rotate this 45 degrees -> _huge_ image */ g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (transform)"); return; } if (filterquality == FILTER_QUALITY_BEST) { NR::transform_bicubic(trans_pb, pb, trans); } else { NR::transform_nearest(trans_pb, pb, trans); } nr_pixblock_release(pb); delete pb; pb = trans_pb; } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) { NRPixBlock *trans_pb = new NRPixBlock; int x0 = pb->area.x0; int y0 = pb->area.y0; int x1 = pb->area.x1; int y1 = pb->area.y1; int min_x = _min2(trans[0] * x0 + trans[4], trans[0] * x1 + trans[4]); int max_x = _max2(trans[0] * x0 + trans[4], trans[0] * x1 + trans[4]); int min_y = _min2(trans[3] * y0 + trans[5], trans[3] * y1 + trans[5]); int max_y = _max2(trans[3] * y0 + trans[5], trans[3] * y1 + trans[5]); nr_pixblock_setup_fast(trans_pb, pb->mode, min_x, min_y, max_x, max_y, true); if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) { g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (scaling)"); return; } NR::scale_bicubic(trans_pb, pb); nr_pixblock_release(pb); delete pb; pb = trans_pb; } } if(_slot[index]) { nr_pixblock_release(_slot[index]); delete _slot[index]; } _slot[index] = pb; _last_out = index; }
static gint sp_dropper_context_root_handler(SPEventContext *event_context, GdkEvent *event) { SPDropperContext *dc = (SPDropperContext *) event_context; int ret = FALSE; SPDesktop *desktop = event_context->desktop; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int pick = prefs->getInt("/tools/dropper/pick", SP_DROPPER_PICK_VISIBLE); bool setalpha = prefs->getBool("/tools/dropper/setalpha", true); switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button == 1 && !event_context->space_panning) { dc->centre = Geom::Point(event->button.x, event->button.y); dc->dragging = TRUE; ret = TRUE; } break; case GDK_MOTION_NOTIFY: if (event->motion.state & GDK_BUTTON2_MASK) { // pass on middle-drag ret = FALSE; break; } else if (!event_context->space_panning) { // otherwise, constantly calculate color no matter is any button pressed or not double rw = 0.0; double W(0), R(0), G(0), B(0), A(0); if (dc->dragging) { // calculate average // radius rw = std::min(Geom::L2(Geom::Point(event->button.x, event->button.y) - dc->centre), 400.0); if (rw == 0) { // happens sometimes, little idea why... break; } Geom::Point const cd = desktop->w2d(dc->centre); Geom::Matrix const w2dt = desktop->w2d(); const double scale = rw * w2dt.descrim(); Geom::Matrix const sm( Geom::Scale(scale, scale) * Geom::Translate(cd) ); sp_canvas_item_affine_absolute(dc->area, sm); sp_canvas_item_show(dc->area); /* Get buffer */ const int x0 = (int) floor(dc->centre[Geom::X] - rw); const int y0 = (int) floor(dc->centre[Geom::Y] - rw); const int x1 = (int) ceil(dc->centre[Geom::X] + rw); const int y1 = (int) ceil(dc->centre[Geom::Y] + rw); if ((x1 > x0) && (y1 > y0)) { NRPixBlock pb; nr_pixblock_setup_fast(&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, TRUE); /* fixme: (Lauris) */ sp_canvas_arena_render_pixblock(SP_CANVAS_ARENA(sp_desktop_drawing(desktop)), &pb); for (int y = y0; y < y1; y++) { const unsigned char *s = NR_PIXBLOCK_PX(&pb) + (y - y0) * pb.rs; for (int x = x0; x < x1; x++) { const double dx = x - dc->centre[Geom::X]; const double dy = y - dc->centre[Geom::Y]; const double w = exp(-((dx * dx) + (dy * dy)) / (rw * rw)); W += w; R += w * s[0]; G += w * s[1]; B += w * s[2]; A += w * s[3]; s += 4; } } nr_pixblock_release(&pb); R = (R + 0.001) / (255.0 * W); G = (G + 0.001) / (255.0 * W); B = (B + 0.001) / (255.0 * W); A = (A + 0.001) / (255.0 * W); R = CLAMP(R, 0.0, 1.0); G = CLAMP(G, 0.0, 1.0); B = CLAMP(B, 0.0, 1.0); A = CLAMP(A, 0.0, 1.0); } } else { // pick single pixel NRPixBlock pb; int x = (int) floor(event->button.x); int y = (int) floor(event->button.y); nr_pixblock_setup_fast(&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, x, y, x+1, y+1, TRUE); sp_canvas_arena_render_pixblock(SP_CANVAS_ARENA(sp_desktop_drawing(desktop)), &pb); const unsigned char *s = NR_PIXBLOCK_PX(&pb); R = s[0] / 255.0; G = s[1] / 255.0; B = s[2] / 255.0; A = s[3] / 255.0; } if (pick == SP_DROPPER_PICK_VISIBLE) { // compose with page color guint32 bg = sp_desktop_namedview(desktop)->pagecolor; R = R + (SP_RGBA32_R_F(bg)) * (1 - A); G = G + (SP_RGBA32_G_F(bg)) * (1 - A); B = B + (SP_RGBA32_B_F(bg)) * (1 - A); A = 1.0; } else { // un-premultiply color channels if (A > 0) { R /= A; G /= A; B /= A; } } if (fabs(A) < 1e-4) { A = 0; // suppress exponentials, CSS does not allow that } // remember color dc->R = R; dc->G = G; dc->B = B; dc->alpha = A; // status message double alpha_to_set = setalpha? dc->alpha : 1.0; guint32 c32 = SP_RGBA32_F_COMPOSE(R, G, B, alpha_to_set); gchar c[64]; sp_svg_write_color(c, sizeof(c), c32); // alpha of color under cursor, to show in the statusbar // locale-sensitive printf is OK, since this goes to the UI, not into SVG gchar *alpha = g_strdup_printf(_(" alpha %.3g"), alpha_to_set); // where the color is picked, to show in the statusbar gchar *where = dc->dragging ? g_strdup_printf(_(", averaged with radius %d"), (int) rw) : g_strdup_printf(_(" under cursor")); // message, to show in the statusbar const gchar *message = dc->dragging ? _("<b>Release mouse</b> to set color.") : _("<b>Click</b> to set fill, <b>Shift+click</b> to set stroke; <b>drag</b> to average color in area; with <b>Alt</b> to pick inverse color; <b>Ctrl+C</b> to copy the color under mouse to clipboard"); event_context->defaultMessageContext()->setF( Inkscape::NORMAL_MESSAGE, "<b>%s%s</b>%s. %s", c, (pick == SP_DROPPER_PICK_VISIBLE)? "" : alpha, where, message ); g_free(where); g_free(alpha); ret = TRUE; } break; case GDK_BUTTON_RELEASE: if (event->button.button == 1 && !event_context->space_panning) { sp_canvas_item_hide(dc->area); dc->dragging = FALSE; double alpha_to_set = setalpha? dc->alpha : 1.0; // do the actual color setting sp_desktop_set_color(desktop, (event->button.state & GDK_MOD1_MASK)? ColorRGBA(1 - dc->R, 1 - dc->G, 1 - dc->B, alpha_to_set) : ColorRGBA(dc->R, dc->G, dc->B, alpha_to_set), false, !(event->button.state & GDK_SHIFT_MASK)); // REJON: set aux. toolbar input to hex color! if (!(sp_desktop_selection(desktop)->isEmpty())) { sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_DROPPER, _("Set picked color")); } ret = TRUE; } break; case GDK_KEY_PRESS: switch (get_group0_keyval(&event->key)) { case GDK_Up: case GDK_Down: case GDK_KP_Up: case GDK_KP_Down: // prevent the zoom field from activation if (!MOD__CTRL_ONLY) { ret = TRUE; } break; case GDK_Escape: sp_desktop_selection(desktop)->clear(); default: break; } break; default: break; } if (!ret) { if (((SPEventContextClass *) parent_class)->root_handler) { ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event); } } return ret; }
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; }