double GodRaysSampler::getCachedLight(const Vector3 &location) { double x = location.x - bounds->getStart().x; double y = location.y - bounds->getStart().y; double z = location.z - bounds->getStart().z; int ix = floor_to_int(x / sampling_step); int iy = floor_to_int(y / sampling_step); int iz = floor_to_int(z / sampling_step); // Check cache limits if (ix < 0 || ix >= samples_x - 1 || iy < 0 || iy >= samples_y - 1 || iz < 0 || iz >= samples_z - 1) { return 1.0; } // Hit cache double p[8] = {getCache(ix, iy, iz), getCache(ix + 1, iy, iz), getCache(ix + 1, iy + 1, iz), getCache(ix, iy + 1, iz), getCache(ix, iy, iz + 1), getCache(ix + 1, iy, iz + 1), getCache(ix + 1, iy + 1, iz + 1), getCache(ix, iy + 1, iz + 1)}; return Interpolation::trilinear(p, (x - sampling_step * double(ix)) / sampling_step, (y - sampling_step * double(iy)) / sampling_step, (z - sampling_step * double(iz)) / sampling_step); }
void PaintedGridBrush::getArea(double x, double y, int *xstart, int *ystart, int *xend, int *yend) const { double s = smoothed_size + hard_radius; *xstart = floor_to_int(x - s); *xend = ceil_to_int(x + s); *ystart = floor_to_int(y - s); *yend = ceil_to_int(y + s); }
SphericalHarmonics3 IrradianceVolume::sample( const Vector3& pos ) const { Vector3 rel_pos = pos; rel_pos -= m_position; rel_pos += m_dimensions*0.5f; rel_pos -= m_cell_size*0.5f; rel_pos /= m_cell_size; int32 x0 = floor_to_int(rel_pos.x); int32 y0 = floor_to_int(rel_pos.y); int32 z0 = floor_to_int(rel_pos.z); int32 x1 = x0+1; int32 y1 = y0+1; int32 z1 = z0+1; float ax = rel_pos.x - float(x0); float ay = rel_pos.y - float(y0); float az = rel_pos.z - float(z0); if(x0<0) {x0=0; x1=x0; ax=0;} if(y0<0) {y0=0; y1=y0; ay=0;} if(z0<0) {z0=0; z1=z0; az=0;} if(x1>=int32(m_width)) {x1=int32(m_width)-1; x0=x1; ax=1;} if(y1>=int32(m_height)) {y1=int32(m_height)-1; y0=y1; ay=1;} if(z1>=int32(m_depth)) {z1=int32(m_depth)-1; z0=z1; az=1;} const SphericalHarmonics3& sh000 = cell(x0,y0,z0); const SphericalHarmonics3& sh100 = cell(x1,y0,z0); const SphericalHarmonics3& sh010 = cell(x0,y1,z0); const SphericalHarmonics3& sh110 = cell(x1,y1,z0); const SphericalHarmonics3& sh001 = cell(x0,y0,z1); const SphericalHarmonics3& sh101 = cell(x1,y0,z1); const SphericalHarmonics3& sh011 = cell(x0,y1,z1); const SphericalHarmonics3& sh111 = cell(x1,y1,z1); SphericalHarmonics3 top0 = sh000; top0.lerp(sh100, ax); SphericalHarmonics3 top1 = sh010; top1.lerp(sh110, ax); SphericalHarmonics3 top = top0; top.lerp(top1, ay); SphericalHarmonics3 bot0 = sh001; bot0.lerp(sh101, ax); SphericalHarmonics3 bot1 = sh011; bot1.lerp(sh111, ax); SphericalHarmonics3 bot = bot0; bot.lerp(bot1, ay); SphericalHarmonics3 res = top; res.lerp(bot, az); return res; }
Color Texture3D::getLinear(double dx, double dy, double dz) const { if (dx < 0.0) dx = 0.0; if (dx > 1.0) dx = 1.0; if (dy < 0.0) dy = 0.0; if (dy > 1.0) dy = 1.0; if (dz < 0.0) dz = 0.0; if (dz > 1.0) dz = 1.0; dx *= to_double(this->xsize - 1); dy *= to_double(this->ysize - 1); dz *= to_double(this->zsize - 1); int ix = floor_to_int(dx); if (ix == this->xsize - 1) { ix--; } int iy = floor_to_int(dy); if (iy == this->ysize - 1) { iy--; } int iz = floor_to_int(dz); if (iz == this->zsize - 1) { iz--; } dx -= to_double(ix); dy -= to_double(iy); dz -= to_double(iz); Color *data = this->data + iz * this->xsize * this->ysize + iy * this->xsize + ix; Color cx1 = data->lerp(*(data + 1), dx); Color cx2 = (data + this->xsize)->lerp(*(data + this->xsize + 1), dx); Color cy1 = cx1.lerp(cx2, dy); data += this->xsize * this->ysize; cx1 = data->lerp(*(data + 1), dx); cx2 = (data + this->xsize)->lerp(*(data + this->xsize + 1), dx); Color cy2 = cx1.lerp(cx2, dy); return cy1.lerp(cy2, dz); }
void Rasterizer::pushScanPoint(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point) { point->x = floor_to_int(point->pixel.x); point->y = floor_to_int(point->pixel.y); if (point->x < 0 || point->x >= canvas->getWidth()) { // Point outside scanline range return; } else if (scanlines->right < 0) { // First point pushed scanlines->left = point->x; scanlines->right = point->x; scanlines->up[point->x] = *point; scanlines->down[point->x] = *point; } else if (point->x > scanlines->right) { // Grow scanlines to right for (int x = scanlines->right + 1; x < point->x; x++) { scanlines->up[x].y = -1; scanlines->down[x].y = canvas->getHeight(); } scanlines->right = point->x; scanlines->up[point->x] = *point; scanlines->down[point->x] = *point; } else if (point->x < scanlines->left) { // Grow scanlines to left for (int x = point->x + 1; x < scanlines->left; x++) { scanlines->up[x].y = -1; scanlines->down[x].y = canvas->getHeight(); } scanlines->left = point->x; scanlines->up[point->x] = *point; scanlines->down[point->x] = *point; } else { // Expand existing scanline if (point->y > scanlines->up[point->x].y) { scanlines->up[point->x] = *point; } if (point->y < scanlines->down[point->x].y) { scanlines->down[point->x] = *point; } } }
bool Warp::accelerated_cairorender(Context context, cairo_t *cr, int quality, const RendDesc &renddesc_, ProgressCallback *cb)const { Point src_tl=param_src_tl.get(Point()); Point src_br=param_src_br.get(Point()); Point dest_tl=param_dest_tl.get(Point()); Point dest_tr=param_dest_tr.get(Point()); Point dest_bl=param_dest_bl.get(Point()); Point dest_br=param_dest_br.get(Point()); Real horizon=param_horizon.get(Real()); bool clip=param_clip.get(bool()); SuperCallback stageone(cb,0,9000,10000); SuperCallback stagetwo(cb,9000,10000,10000); RendDesc renddesc(renddesc_); // Untransform the render desc if(!cairo_renddesc_untransform(cr, renddesc)) return false; Real pw=(renddesc.get_w())/(renddesc.get_br()[0]-renddesc.get_tl()[0]); Real ph=(renddesc.get_h())/(renddesc.get_br()[1]-renddesc.get_tl()[1]); if(cb && !cb->amount_complete(0,10000)) return false; Point tl(renddesc.get_tl()); Point br(renddesc.get_br()); Rect bounding_rect; Rect render_rect(tl,br); Rect clip_rect(Rect::full_plane()); Rect dest_rect(dest_tl,dest_br); dest_rect.expand(dest_tr).expand(dest_bl); Real zoom_factor(1.0); // Quick exclusion clip, if necessary if(clip && !intersect(render_rect,dest_rect)) { cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_restore(cr); return true; } { Rect other(render_rect); if(clip) other&=dest_rect; Point min(other.get_min()); Point max(other.get_max()); bool init_point_set=false; // Point trans_point[4]; Point p; // Real trans_z[4]; Real z,minz(10000000000000.0f),maxz(0); //! \todo checking the 4 corners for 0<=z<horizon*2 and using //! only 4 corners which satisfy this condition isn't the //! right thing to do. It's possible that none of the 4 //! corners fall within that range, and yet content of the //! tile does. p=transform_forward(min); z=transform_backward_z(p); if(z>0 && z<horizon*2) { if(init_point_set) bounding_rect.expand(p); else bounding_rect=Rect(p); init_point_set=true; maxz=std::max(maxz,z); minz=std::min(minz,z); } p=transform_forward(max); z=transform_backward_z(p); if(z>0 && z<horizon*2) { if(init_point_set) bounding_rect.expand(p); else bounding_rect=Rect(p); init_point_set=true; maxz=std::max(maxz,z); minz=std::min(minz,z); } swap(min[1],max[1]); p=transform_forward(min); z=transform_backward_z(p); if(z>0 && z<horizon*2) { if(init_point_set) bounding_rect.expand(p); else bounding_rect=Rect(p); init_point_set=true; maxz=std::max(maxz,z); minz=std::min(minz,z); } p=transform_forward(max); z=transform_backward_z(p); if(z>0 && z<horizon*2) { if(init_point_set) bounding_rect.expand(p); else bounding_rect=Rect(p); init_point_set=true; maxz=std::max(maxz,z); minz=std::min(minz,z); } if(!init_point_set) { cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_restore(cr); return true; } zoom_factor=(1+(maxz-minz)); } #ifdef ACCEL_WARP_IS_BROKEN return Layer::accelerated_cairorender(context,cr,quality,renddesc, cb); #else /*swap(tl[1],br[1]); bounding_rect .expand(transform_forward(tl)) .expand(transform_forward(br)) ; swap(tl[1],br[1]);*/ //synfig::warning("given window: [%f,%f]-[%f,%f] %dx%d",tl[0],tl[1],br[0],br[1],renddesc.get_w(),renddesc.get_h()); //synfig::warning("Projected: [%f,%f]-[%f,%f]",bounding_rect.get_min()[0],bounding_rect.get_min()[1],bounding_rect.get_max()[0],bounding_rect.get_max()[1]); // If we are clipping, then go ahead and clip to the // source rectangle if(clip) clip_rect&=Rect(src_tl,src_br); // Bound ourselves to the bounding rectangle of // what is under us clip_rect&=context.get_full_bounding_rect();//.expand_x(abs(zoom_factor/pw)).expand_y(abs(zoom_factor/ph)); bounding_rect&=clip_rect; Point min_point(bounding_rect.get_min()); Point max_point(bounding_rect.get_max()); // we're going to divide by the difference of these pairs soon; // if they're the same, we'll be dividing by zero, and we don't // want to do that! // \todo what should we do in this case? if (min_point[0] == max_point[0]) max_point[0] += 0.001; if (min_point[1] == max_point[1]) max_point[1] += 0.001; if(tl[0]>br[0]) { tl[0]=max_point[0]; br[0]=min_point[0]; } else { br[0]=max_point[0]; tl[0]=min_point[0]; } if(tl[1]>br[1]) { tl[1]=max_point[1]; br[1]=min_point[1]; } else { br[1]=max_point[1]; tl[1]=min_point[1]; } const int tmp_d(max(renddesc.get_w(),renddesc.get_h())); Real src_pw=(tmp_d*zoom_factor)/(br[0]-tl[0]); Real src_ph=(tmp_d*zoom_factor)/(br[1]-tl[1]); RendDesc desc(renddesc); desc.clear_flags(); //desc.set_flags(RendDesc::PX_ASPECT); desc.set_tl(tl); desc.set_br(br); desc.set_wh(ceil_to_int(src_pw*(br[0]-tl[0])),ceil_to_int(src_ph*(br[1]-tl[1]))); //synfig::warning("surface to render: [%f,%f]-[%f,%f] %dx%d",desc.get_tl()[0],desc.get_tl()[1],desc.get_br()[0],desc.get_br()[1],desc.get_w(),desc.get_h()); if(desc.get_w()==0 && desc.get_h()==0) { cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_restore(cr); return true; } // Recalculate the pixel widths for the src renddesc src_pw=(desc.get_w())/(desc.get_br()[0]-desc.get_tl()[0]); src_ph=(desc.get_h())/(desc.get_br()[1]-desc.get_tl()[1]); cairo_surface_t* source=cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, desc.get_w(),desc.get_h()); cairo_surface_t* surface=cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA,renddesc.get_w(), renddesc.get_h()); cairo_t* subcr=cairo_create(source); cairo_scale(subcr, 1/desc.get_pw(), 1/desc.get_ph()); cairo_translate(subcr, -desc.get_tl()[0], -desc.get_tl()[1]); if(!context.accelerated_cairorender(subcr,quality,desc,&stageone)) return false; cairo_destroy(subcr); int surfacew, surfaceh, sourcew, sourceh; CairoSurface csurface(surface); CairoSurface csource(source); csurface.map_cairo_image(); csource.map_cairo_image(); surfacew=csurface.get_w(); surfaceh=csurface.get_h(); sourcew=csource.get_w(); sourceh=csource.get_h(); CairoSurface::pen pen(csurface.begin()); // Do the warp { int x,y; float u,v; Point point,tmp; for(y=0,point[1]=renddesc.get_tl()[1];y<surfaceh;y++,pen.inc_y(),pen.dec_x(x),point[1]+=1.0/ph) { for(x=0,point[0]=renddesc.get_tl()[0];x<surfacew;x++,pen.inc_x(),point[0]+=1.0/pw) { tmp=transform_forward(point); const float z(transform_backward_z(tmp)); if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon)) { csurface[y][x]=Color::alpha(); continue; } u=(tmp[0]-tl[0])*src_pw; v=(tmp[1]-tl[1])*src_ph; if(u<0 || v<0 || u>=sourcew || v>=sourceh || isnan(u) || isnan(v)) csurface[y][x]=context.get_cairocolor(tmp); else { // CUBIC if(quality<=4) csurface[y][x]=csource.cubic_sample_cooked(u,v); // INTEPOLATION_LINEAR else if(quality<=6) csurface[y][x]=csource.linear_sample_cooked(u,v); else // NEAREST_NEIGHBOR csurface[y][x]=csource[floor_to_int(v)][floor_to_int(u)]; } } if((y&31)==0 && cb) { if(!stagetwo.amount_complete(y,surfaceh)) return false; } } } #endif if(cb && !cb->amount_complete(10000,10000)) return false; csurface.unmap_cairo_image(); csource.unmap_cairo_image(); cairo_surface_destroy(source); cairo_save(cr); cairo_translate(cr, renddesc.get_tl()[0], renddesc.get_tl()[1]); cairo_scale(cr, renddesc.get_pw(), renddesc.get_ph()); cairo_set_source_surface(cr, surface, 0, 0); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); cairo_restore(cr); cairo_surface_destroy(surface); return true; }
bool CurveWarp::accelerated_cairorender(Context context, cairo_t *cr, int quality, const RendDesc &renddesc_, ProgressCallback *cb)const { Point start_point=param_start_point.get(Point()); Point end_point=param_end_point.get(Point()); SuperCallback stageone(cb,0,9000,10000); SuperCallback stagetwo(cb,9000,10000,10000); RendDesc renddesc(renddesc_); // Untransform the render desc if(!cairo_renddesc_untransform(cr, renddesc)) return false; int x,y; const Real pw(renddesc.get_pw()),ph(renddesc.get_ph()); Point tl(renddesc.get_tl()); Point br(renddesc.get_br()); const int w(renddesc.get_w()); const int h(renddesc.get_h()); // find a bounding rectangle for the context we need to render // todo: find a better way of doing this - this way doesn't work Rect src_rect(transform(tl)); Point pos1, pos2; Real dist, along; Real min_dist(999999), max_dist(-999999), min_along(999999), max_along(-999999); #define UPDATE_DIST \ if (dist < min_dist) min_dist = dist; \ if (dist > max_dist) max_dist = dist; \ if (along < min_along) min_along = along; \ if (along > max_along) max_along = along // look along the top and bottom edges pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1]; for (x = 0; x < w; x++, pos1[0] += pw, pos2[0] += pw) { src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST; src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST; } // look along the left and right edges pos1[0] = tl[0]; pos2[0] = br[0]; pos1[1] = pos2[1] = tl[1]; for (y = 0; y < h; y++, pos1[1] += ph, pos2[1] += ph) { src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST; src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST; } // look along the diagonals const int max_wh(std::max(w,h)); const Real inc_x((br[0]-tl[0])/max_wh),inc_y((br[1]-tl[1])/max_wh); pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1]; for (x = 0; x < max_wh; x++, pos1[0] += inc_x, pos2[0] = pos1[0], pos1[1]+=inc_y, pos2[1]-=inc_y) { src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST; src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST; } #if 0 // look at each blinepoint std::vector<synfig::BLinePoint>::const_iterator iter; for (iter=bline.begin(); iter!=bline.end(); iter++) src_rect.expand(transform(iter->get_vertex()+origin, &dist, &along)); UPDATE_DIST; #endif Point src_tl(src_rect.get_min()); Point src_br(src_rect.get_max()); Vector ab((end_point - start_point).norm()); Angle::tan ab_angle(ab[1], ab[0]); Real used_length = max_along - min_along; Real render_width = max_dist - min_dist; int src_w = (abs(used_length*Angle::cos(ab_angle).get()) + abs(render_width*Angle::sin(ab_angle).get())) / abs(pw); int src_h = (abs(used_length*Angle::sin(ab_angle).get()) + abs(render_width*Angle::cos(ab_angle).get())) / abs(ph); Real src_pw((src_br[0] - src_tl[0]) / src_w); Real src_ph((src_br[1] - src_tl[1]) / src_h); if (src_pw > abs(pw)) { src_w = int((src_br[0] - src_tl[0]) / abs(pw)); src_pw = (src_br[0] - src_tl[0]) / src_w; } if (src_ph > abs(ph)) { src_h = int((src_br[1] - src_tl[1]) / abs(ph)); src_ph = (src_br[1] - src_tl[1]) / src_h; } #define MAXPIX 10000 if (src_w > MAXPIX) src_w = MAXPIX; if (src_h > MAXPIX) src_h = MAXPIX; // this is an attempt to remove artifacts around tile edges - the // cubic interpolation uses at most 2 pixels either side of the // target pixel, so add an extra 2 pixels around the tile on all // sides src_tl -= (Point(src_pw,src_ph)*2); src_br += (Point(src_pw,src_ph)*2); src_w += 4; src_h += 4; src_pw = (src_br[0] - src_tl[0]) / src_w; src_ph = (src_br[1] - src_tl[1]) / src_h; // set up a renddesc for the context to render RendDesc src_desc(renddesc); //src_desc.clear_flags(); src_desc.set_tl(src_tl); src_desc.set_br(src_br); src_desc.set_wh(src_w, src_h); // New expanded renddesc values const double wpw=src_desc.get_pw(); const double wph=src_desc.get_ph(); const double wtlx=src_desc.get_tl()[0]; const double wtly=src_desc.get_tl()[1]; // render the context onto a new surface cairo_surface_t* csource, *cresult; csource=cairo_surface_create_similar(cairo_get_target(cr),CAIRO_CONTENT_COLOR_ALPHA, src_w, src_h); cresult=cairo_surface_create_similar(cairo_get_target(cr),CAIRO_CONTENT_COLOR_ALPHA, w, h); cairo_t *subcr=cairo_create(csource); cairo_scale(subcr, 1/wpw, 1/wph); cairo_translate(subcr, -wtlx, -wtly); if(!context.accelerated_cairorender(subcr,quality,src_desc,&stageone)) return false; // don't needed anymore cairo_destroy(subcr); //access to pixels CairoSurface source(csource); source.map_cairo_image(); CairoSurface result(cresult); result.map_cairo_image(); float u,v; Point pos, tmp; if(quality<=4) // CUBIC for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph) { for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw) { tmp=transform(pos); u=(tmp[0]-src_tl[0])/src_pw; v=(tmp[1]-src_tl[1])/src_ph; if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v)) result[y][x]=CairoColor(context.get_color(tmp)).premult_alpha(); else result[y][x]=source.cubic_sample_cooked(u,v); } if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false; } else if (quality<=6) // INTERPOLATION_LINEAR for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph) { for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw) { tmp=transform(pos); u=(tmp[0]-src_tl[0])/src_pw; v=(tmp[1]-src_tl[1])/src_ph; if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v)) result[y][x]=CairoColor(context.get_color(tmp)).premult_alpha(); else result[y][x]=source.linear_sample_cooked(u,v); } if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false; } else // NEAREST_NEIGHBOR for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph) { for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw) { tmp=transform(pos); u=(tmp[0]-src_tl[0])/src_pw; v=(tmp[1]-src_tl[1])/src_ph; if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v)) result[y][x]=CairoColor(context.get_color(tmp)).premult_alpha(); else result[y][x]=source[floor_to_int(v)][floor_to_int(u)]; } if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false; } result.unmap_cairo_image(); source.unmap_cairo_image(); cairo_surface_destroy(csource); // Now paint it on the context cairo_save(cr); cairo_translate(cr, tl[0], tl[1]); cairo_scale(cr, pw, ph); cairo_set_source_surface(cr, cresult, 0, 0); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); cairo_restore(cr); cairo_surface_destroy(cresult); // Mark our progress as finished if(cb && !cb->amount_complete(10000,10000)) return false; return true; }
bool CurveWarp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const { Point start_point=param_start_point.get(Point()); Point end_point=param_end_point.get(Point()); SuperCallback stageone(cb,0,9000,10000); SuperCallback stagetwo(cb,9000,10000,10000); int x,y; const Real pw(renddesc.get_pw()),ph(renddesc.get_ph()); Point tl(renddesc.get_tl()); Point br(renddesc.get_br()); const int w(renddesc.get_w()); const int h(renddesc.get_h()); // find a bounding rectangle for the context we need to render // todo: find a better way of doing this - this way doesn't work Rect src_rect(transform(tl)); Point pos1, pos2; Real dist, along; Real min_dist(999999), max_dist(-999999), min_along(999999), max_along(-999999); #define UPDATE_DIST \ if (dist < min_dist) min_dist = dist; \ if (dist > max_dist) max_dist = dist; \ if (along < min_along) min_along = along; \ if (along > max_along) max_along = along // look along the top and bottom edges pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1]; for (x = 0; x < w; x++, pos1[0] += pw, pos2[0] += pw) { src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST; src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST; } // look along the left and right edges pos1[0] = tl[0]; pos2[0] = br[0]; pos1[1] = pos2[1] = tl[1]; for (y = 0; y < h; y++, pos1[1] += ph, pos2[1] += ph) { src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST; src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST; } // look along the diagonals const int max_wh(std::max(w,h)); const Real inc_x((br[0]-tl[0])/max_wh),inc_y((br[1]-tl[1])/max_wh); pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1]; for (x = 0; x < max_wh; x++, pos1[0] += inc_x, pos2[0] = pos1[0], pos1[1]+=inc_y, pos2[1]-=inc_y) { src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST; src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST; } #if 0 // look at each blinepoint std::vector<synfig::BLinePoint>::const_iterator iter; for (iter=bline.begin(); iter!=bline.end(); iter++) src_rect.expand(transform(iter->get_vertex()+origin, &dist, &along)); UPDATE_DIST; #endif Point src_tl(src_rect.get_min()); Point src_br(src_rect.get_max()); Vector ab((end_point - start_point).norm()); Angle::tan ab_angle(ab[1], ab[0]); Real used_length = max_along - min_along; Real render_width = max_dist - min_dist; int src_w = (abs(used_length*Angle::cos(ab_angle).get()) + abs(render_width*Angle::sin(ab_angle).get())) / abs(pw); int src_h = (abs(used_length*Angle::sin(ab_angle).get()) + abs(render_width*Angle::cos(ab_angle).get())) / abs(ph); Real src_pw((src_br[0] - src_tl[0]) / src_w); Real src_ph((src_br[1] - src_tl[1]) / src_h); if (src_pw > abs(pw)) { src_w = int((src_br[0] - src_tl[0]) / abs(pw)); src_pw = (src_br[0] - src_tl[0]) / src_w; } if (src_ph > abs(ph)) { src_h = int((src_br[1] - src_tl[1]) / abs(ph)); src_ph = (src_br[1] - src_tl[1]) / src_h; } #define MAXPIX 10000 if (src_w > MAXPIX) src_w = MAXPIX; if (src_h > MAXPIX) src_h = MAXPIX; // this is an attempt to remove artifacts around tile edges - the // cubic interpolation uses at most 2 pixels either side of the // target pixel, so add an extra 2 pixels around the tile on all // sides src_tl -= (Point(src_pw,src_ph)*2); src_br += (Point(src_pw,src_ph)*2); src_w += 4; src_h += 4; src_pw = (src_br[0] - src_tl[0]) / src_w; src_ph = (src_br[1] - src_tl[1]) / src_h; // set up a renddesc for the context to render RendDesc src_desc(renddesc); src_desc.clear_flags(); src_desc.set_tl(src_tl); src_desc.set_br(src_br); src_desc.set_wh(src_w, src_h); // render the context onto a new surface Surface source; source.set_wh(src_w,src_h); if(!context.accelerated_render(&source,quality,src_desc,&stageone)) return false; float u,v; Point pos, tmp; surface->set_wh(w,h); surface->clear(); if(quality<=4) // CUBIC for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph) { for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw) { tmp=transform(pos); u=(tmp[0]-src_tl[0])/src_pw; v=(tmp[1]-src_tl[1])/src_ph; if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v)) (*surface)[y][x]=context.get_color(tmp); else (*surface)[y][x]=source.cubic_sample(u,v); } if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false; } else if (quality<=6) // INTERPOLATION_LINEAR for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph) { for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw) { tmp=transform(pos); u=(tmp[0]-src_tl[0])/src_pw; v=(tmp[1]-src_tl[1])/src_ph; if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v)) (*surface)[y][x]=context.get_color(tmp); else (*surface)[y][x]=source.linear_sample(u,v); } if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false; } else // NEAREST_NEIGHBOR for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph) { for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw) { tmp=transform(pos); u=(tmp[0]-src_tl[0])/src_pw; v=(tmp[1]-src_tl[1])/src_ph; if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v)) (*surface)[y][x]=context.get_color(tmp); else (*surface)[y][x]=source[floor_to_int(v)][floor_to_int(u)]; } if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false; } // Mark our progress as finished if(cb && !cb->amount_complete(10000,10000)) return false; return true; }