bool synfig::Target_Scanline::render(ProgressCallback *cb) { SuperCallback super_cb; int // i=0, total_frames, quality=get_quality(), frame_start, frame_end; Time t=0, time_start, time_end; assert(canvas); curr_frame_=0; if( !init() ){ if(cb) cb->error(_("Target initialization failure")); return false; } // If the description's end frame is equal to // the start frame, then it is assumed that we // are rendering only one frame. Correct it. if(desc.get_frame_end()==desc.get_frame_start()) desc.set_frame_end(desc.get_frame_start()+1); frame_start=desc.get_frame_start(); frame_end=desc.get_frame_end(); time_start=desc.get_time_start(); time_end=desc.get_time_end(); // Calculate the number of frames total_frames=frame_end-frame_start; //RendDesc rend_desc=desc; try { // Grab the time int i=next_frame(t); //synfig::info("1time_set_to %s",t.get_string().c_str()); if(i>1) do{ //if(total_frames>1) //for(i=0,t=time_start;i<total_frames;i++) //{ //t=((time_end-time_start)*((Real)i/(Real)total_frames)).round(desc.get_frame_rate())+time_start; // If we have a callback, and it returns // false, go ahead and bail. (it may be a user cancel) if(cb && !cb->amount_complete(total_frames-(i-1),total_frames)) return false; // Set the time that we wish to render if(!get_avoid_time_sync() || canvas->get_time()!=t) canvas->set_time(t); Context context; #ifdef SYNFIG_OPTIMIZE_LAYER_TREE Canvas::Handle op_canvas; if (!getenv("SYNFIG_DISABLE_OPTIMIZE_LAYER_TREE")) { op_canvas = Canvas::create(); op_canvas->set_file_name(canvas->get_file_name()); optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas); context=op_canvas->get_context(); } else context=canvas->get_context(); #else context=canvas->get_context(); #endif // If the quality is set to zero, then we // use the parametric scanline-renderer. if(quality==0) { if(threads_<=0) { if(!synfig::render(context,this,desc,0)) return false; } else { if(!synfig::render_threaded(context,this,desc,0,threads_)) return false; } } else // If quality is set otherwise, then we use the accelerated renderer { #if USE_PIXELRENDERING_LIMIT if(desc.get_w()*desc.get_h() > PIXEL_RENDERING_LIMIT) { Surface surface; int rowheight = PIXEL_RENDERING_LIMIT/desc.get_w(); if (!rowheight) rowheight = 1; // TODO: render partial lines to stay within the limit? int rows = desc.get_h()/rowheight; int lastrowheight = desc.get_h() - rows*rowheight; rows++; synfig::info("Render broken up into %d block%s %d pixels tall, and a final block %d pixels tall", rows-1, rows==2?"":"s", rowheight, lastrowheight); // loop through all the full rows if(!start_frame()) { throw(string("add_frame(): target panic on start_frame()")); return false; } for(int i=0; i < rows; ++i) { RendDesc blockrd = desc; //render the strip at the normal size unless it's the last one... if(i == rows-1) { if(!lastrowheight) break; blockrd.set_subwindow(0,i*rowheight,desc.get_w(),lastrowheight); } else { blockrd.set_subwindow(0,i*rowheight,desc.get_w(),rowheight); } if(!context.accelerated_render(&surface,quality,blockrd,0)) { if(cb)cb->error(_("Accelerated Renderer Failure")); return false; }else { int y; int rowspan=sizeof(Color)*surface.get_w(); Surface::pen pen = surface.begin(); int yoff = i*rowheight; for(y = 0; y < blockrd.get_h(); y++, pen.inc_y()) { Color *colordata= start_scanline(y + yoff); if(!colordata) { throw(string("add_frame(): call to start_scanline(y) returned NULL")); return false; } if(get_remove_alpha()) { for(int i = 0; i < surface.get_w(); i++) colordata[i] = Color::blend(surface[y][i],desc.get_bg_color(),1.0f); } else memcpy(colordata,surface[y],rowspan); if(!end_scanline()) { throw(string("add_frame(): target panic on end_scanline()")); return false; } } } } end_frame(); }else //use normal rendering... { #endif Surface surface; if(!context.accelerated_render(&surface,quality,desc,0)) { // For some reason, the accelerated renderer failed. if(cb)cb->error(_("Accelerated Renderer Failure")); return false; } else { // Put the surface we renderer // onto the target. if(!add_frame(&surface)) { if(cb)cb->error(_("Unable to put surface on target")); return false; } } #if USE_PIXELRENDERING_LIMIT } #endif } }while((i=next_frame(t))); else { // Set the time that we wish to render if(!get_avoid_time_sync() || canvas->get_time()!=t) canvas->set_time(t); Context context; #ifdef SYNFIG_OPTIMIZE_LAYER_TREE Canvas::Handle op_canvas; if (!getenv("SYNFIG_DISABLE_OPTIMIZE_LAYER_TREE")) { op_canvas = Canvas::create(); op_canvas->set_file_name(canvas->get_file_name()); optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas); context=op_canvas->get_context(); } else context=canvas->get_context(); #else context=canvas->get_context(); #endif // If the quality is set to zero, then we // use the parametric scanline-renderer. if(quality==0) { if(threads_<=0) { if(!synfig::render(context,this,desc,cb)) return false; } else { if(!synfig::render_threaded(context,this,desc,cb,threads_)) return false; } } else // If quality is set otherwise, then we use the accelerated renderer { #if USE_PIXELRENDERING_LIMIT if(desc.get_w()*desc.get_h() > PIXEL_RENDERING_LIMIT) { Surface surface; int totalheight = desc.get_h(); int rowheight = PIXEL_RENDERING_LIMIT/desc.get_w(); if (!rowheight) rowheight = 1; // TODO: render partial lines to stay within the limit? int rows = desc.get_h()/rowheight; int lastrowheight = desc.get_h() - rows*rowheight; rows++; synfig::info("Render broken up into %d block%s %d pixels tall, and a final block %d pixels tall", rows-1, rows==2?"":"s", rowheight, lastrowheight); // loop through all the full rows if(!start_frame()) { throw(string("add_frame(): target panic on start_frame()")); return false; } for(int i=0; i < rows; ++i) { RendDesc blockrd = desc; //render the strip at the normal size unless it's the last one... if(i == rows-1) { if(!lastrowheight) break; blockrd.set_subwindow(0,i*rowheight,desc.get_w(),lastrowheight); } else { blockrd.set_subwindow(0,i*rowheight,desc.get_w(),rowheight); } SuperCallback sc(cb, i*rowheight, (i+1)*rowheight, totalheight); if(!context.accelerated_render(&surface,quality,blockrd,&sc)) { if(cb)cb->error(_("Accelerated Renderer Failure")); return false; }else { int y; int rowspan=sizeof(Color)*surface.get_w(); Surface::pen pen = surface.begin(); int yoff = i*rowheight; for(y = 0; y < blockrd.get_h(); y++, pen.inc_y()) { Color *colordata= start_scanline(y + yoff); if(!colordata) { throw(string("add_frame(): call to start_scanline(y) returned NULL")); return false; } if(get_remove_alpha()) { for(int i = 0; i < surface.get_w(); i++) colordata[i] = Color::blend(surface[y][i],desc.get_bg_color(),1.0f); } else memcpy(colordata,surface[y],rowspan); if(!end_scanline()) { throw(string("add_frame(): target panic on end_scanline()")); return false; } } } //I'm done with this part sc.amount_complete(100,100); } end_frame(); }else { #endif Surface surface; if(!context.accelerated_render(&surface,quality,desc,cb)) { if(cb)cb->error(_("Accelerated Renderer Failure")); return false; } else { // Put the surface we renderer // onto the target. if(!add_frame(&surface)) { if(cb)cb->error(_("Unable to put surface on target")); return false; } } #if USE_PIXELRENDERING_LIMIT } #endif } } } catch(String str) { if(cb)cb->error(_("Caught string :")+str); return false; } catch(std::bad_alloc) { if(cb)cb->error(_("Ran out of memory (Probably a bug)")); return false; } catch(...) { if(cb)cb->error(_("Caught unknown error, rethrowing...")); throw; } return true; }
bool Layer_SphereDistort::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const { RENDER_TRANSFORMED_IF_NEED(__FILE__, __LINE__) /* Things to consider: 1) Block expansion for distortion (ouch... quality level??) 2) Bounding box clipping 3) Super sampling for better visual quality (based on the quality level?) 4) Interpolation type for sampling (based on quality level?) //things to defer until after super sampling, non-linear interpolation */ //bounding box reject Vector center=param_center.get(Vector()); double radius=param_radius.get(double()); double percent=param_amount.get(double()); int type=param_type.get(int()); bool clip=param_clip.get(bool()); { Rect sphr; sphr.set_point(center[0]-radius,center[1]-radius); sphr.expand(center[0]+radius,center[1]+radius); //get the bounding box of the transform Rect windr; //and the bounding box of the rendering windr.set_point(renddesc.get_tl()[0],renddesc.get_tl()[1]); windr.expand(renddesc.get_br()[0],renddesc.get_br()[1]); //test bounding boxes for collision if( (type == TYPE_NORMAL && !intersect(sphr,windr)) || (type == TYPE_DISTH && (sphr.minx >= windr.maxx || windr.minx >= sphr.maxx)) || (type == TYPE_DISTV && (sphr.miny >= windr.maxy || windr.miny >= sphr.maxy)) ) { //warning("Spherize: Bounding box reject"); if (clip) { surface->set_wh(renddesc.get_w(), renddesc.get_h()); surface->clear(); return true; } else return context.accelerated_render(surface,quality,renddesc,cb); } //warning("Spherize: Bounding box accept"); } //Ok, so we overlap some... now expand the window for rendering RendDesc r = renddesc; Surface background; Real pw = renddesc.get_pw(),ph = renddesc.get_ph(); int nl=0,nt=0,nr=0,nb=0, nw=0,nh=0; Point tl = renddesc.get_tl(), br = renddesc.get_br(); { //must enlarge window by pixel coordinates so go! //need to figure out closest and farthest point and distort THOSE Point origin[4] = {tl,tl,br,br}; Vector v[4] = {Vector(0,br[1]-tl[1]), Vector(br[0]-tl[0],0), Vector(0,tl[1]-br[1]), Vector(tl[0]-br[0],0)}; Point close(0,0); Real t = 0; Rect expandr(tl,br); //expandr.set_point(tl[0],tl[1]); //expandr.expand(br[0],br[1]); //warning("Spherize: Loop through lines and stuff"); for(int i=0; i<4; ++i) { //warning("Spherize: %d", i); Vector p_o = center-origin[i]; //project onto left line t = (p_o*v[i])/v[i].mag_squared(); //clamp if(t < 0) t = 0; if(t > 1) t = 1; close = origin[i] + v[i]*t; //now get transforms and expand the rectangle to accommodate Point p = sphtrans(close,center,radius,percent,type); expandr.expand(p[0],p[1]); p = sphtrans(origin[i],center,radius,percent,type); expandr.expand(p[0],p[1]); p = sphtrans(origin[i]+v[i],center,radius,percent,type); expandr.expand(p[0],p[1]); } /*warning("Spherize: Bounding box (%f,%f)-(%f,%f)", expandr.minx,expandr.miny,expandr.maxx,expandr.maxy);*/ //now that we have the bounding rectangle of ALL the pixels (should be...) //order it so that it's in the same orientation as the tl,br pair //warning("Spherize: Organize like tl,br"); Point ntl(0,0),nbr(0,0); //sort x if(tl[0] < br[0]) { ntl[0] = expandr.minx; nbr[0] = expandr.maxx; } else { ntl[0] = expandr.maxx; nbr[0] = expandr.minx; } //sort y if(tl[1] < br[1]) { ntl[1] = expandr.miny; nbr[1] = expandr.maxy; } else { ntl[1] = expandr.maxy; nbr[1] = expandr.miny; } //now expand the window as needed Vector temp = ntl-tl; //pixel offset nl = (int)(temp[0]/pw)-1; nt = (int)(temp[1]/ph)-1; temp = nbr - br; nr = (int)(temp[0]/pw)+1; nb = (int)(temp[1]/ph)+1; nw = renddesc.get_w() + nr - nl; nh = renddesc.get_h() + nb - nt; //warning("Spherize: Setting subwindow (%d,%d) (%d,%d) (%d,%d)",nl,nt,nr,nb,nw,nh); r.set_subwindow(nl,nt,nw,nh); /*r = renddesc; nw = r.get_w(), nh = r.get_h(); nl = 0, nt = 0;*/ } //warning("Spherize: render background"); if(!context.accelerated_render(&background,quality,r,cb)) { warning("SphereDistort: Layer below failed"); return false; } //now distort and check to make sure we aren't overshooting our bounds here int w = renddesc.get_w(), h = renddesc.get_h(); surface->set_wh(w,h); Point sample = tl, sub = tl, trans(0,0); float xs = 0,ys = 0; int y=0,x=0; Real invpw = 1/pw, invph = 1/ph; Surface::pen p = surface->begin(); Point rtl = r.get_tl(); //warning("Spherize: About to transform"); for(y = 0; y < h; ++y, sample[1] += ph, p.inc_y()) { sub = sample; for(x = 0; x < w; ++x, sub[0] += pw, p.inc_x()) { bool clipped; trans=sphtrans(sub,center,radius,percent,type,clipped); if(clip && clipped) { p.put_value(Color::alpha()); continue; } xs = (trans[0]-rtl[0])*invpw; ys = (trans[1]-rtl[1])*invph; if(!(xs >= 0 && xs < nw && ys >= 0 && ys < nh)) { //warning("Spherize: we failed to account for %f,%f",xs,ys); p.put_value(context.get_color(trans));//Color::alpha()); continue; } //sample at that pixel location based on the quality if(quality <= 4) // cubic p.put_value(background.cubic_sample(xs,ys)); else if(quality <= 5) // cosine p.put_value(background.cosine_sample(xs,ys)); else if(quality <= 6) // linear p.put_value(background.linear_sample(xs,ys)); else // nearest p.put_value(background[round_to_int(ys)][round_to_int(xs)]); } p.dec_x(w); } return true; }
bool synfig::Target_Scanline::render(ProgressCallback *cb) { SuperCallback super_cb; int frames=0, total_frames, quality=get_quality(), frame_start, frame_end; Time t=0; assert(canvas); curr_frame_=0; if( !init() ){ if(cb) cb->error(_("Target initialization failure")); return false; } frame_start=desc.get_frame_start(); frame_end=desc.get_frame_end(); ContextParams context_params(desc.get_render_excluded_contexts()); // Calculate the number of frames total_frames=frame_end-frame_start+1; if(total_frames<=0)total_frames=1; try { //synfig::info("1time_set_to %s",t.get_string().c_str()); if(total_frames>=1) { do{ // Grab the time frames=next_frame(t); // If we have a callback, and it returns // false, go ahead and bail. (it may be a user cancel) if(cb && !cb->amount_complete(total_frames-frames,total_frames)) return false; Context context; // pass the Render Method to the context context=canvas->get_context(context_params); context.set_render_method(SOFTWARE); // Set the time that we wish to render if(!get_avoid_time_sync() || canvas->get_time()!=t) canvas->set_time(t); #ifdef SYNFIG_OPTIMIZE_LAYER_TREE Canvas::Handle op_canvas; if (!getenv("SYNFIG_DISABLE_OPTIMIZE_LAYER_TREE")) { op_canvas = Canvas::create(); op_canvas->set_file_name(canvas->get_file_name()); optimize_layers(canvas->get_time(), canvas->get_context(context_params), op_canvas); context=op_canvas->get_context(context_params); } else context=canvas->get_context(context_params); #else context=canvas->get_context(context_params); #endif // If the quality is set to zero, then we // use the parametric scanline-renderer. if(quality==0 && get_engine().empty()) { if(threads_<=0) { if(!synfig::render(context,this,desc,0)) return false; } else { if(!synfig::render_threaded(context,this,desc,0,threads_)) return false; } } else // If quality is set otherwise, then we use the accelerated renderer { #if USE_PIXELRENDERING_LIMIT if(desc.get_w()*desc.get_h() > PIXEL_RENDERING_LIMIT) { SurfaceSW::Handle surfacesw(new SurfaceSW()); Surface &surface = surfacesw->get_surface(); int rowheight = PIXEL_RENDERING_LIMIT/desc.get_w(); if (!rowheight) rowheight = 1; // TODO: render partial lines to stay within the limit? int rows = desc.get_h()/rowheight; int lastrowheight = desc.get_h() - rows*rowheight; rows++; synfig::info("Render broken up into %d block%s %d pixels tall, and a final block %d pixels tall", rows-1, rows==2?"":"s", rowheight, lastrowheight); // loop through all the full rows if(!start_frame()) { throw(string("add_frame(): target panic on start_frame()")); return false; } for(int i=0; i < rows; ++i) { RendDesc blockrd = desc; //render the strip at the normal size unless it's the last one... if(i == rows-1) { if(!lastrowheight) break; blockrd.set_subwindow(0,i*rowheight,desc.get_w(),lastrowheight); } else { blockrd.set_subwindow(0,i*rowheight,desc.get_w(),rowheight); } if(!call_renderer(context,surfacesw,quality,blockrd,0)) { if(cb)cb->error(_("Accelerated Renderer Failure")); return false; }else { int y; int rowspan=sizeof(Color)*surface.get_w(); Surface::pen pen = surface.begin(); int yoff = i*rowheight; for(y = 0; y < blockrd.get_h(); y++, pen.inc_y()) { Color *colordata= start_scanline(y + yoff); if(!colordata) { throw(string("add_frame(): call to start_scanline(y) returned NULL")); return false; } switch(get_alpha_mode()) { case TARGET_ALPHA_MODE_FILL: for(int i = 0; i < surface.get_w(); i++) colordata[i] = Color::blend(surface[y][i],desc.get_bg_color(),1.0f); break; case TARGET_ALPHA_MODE_EXTRACT: for(int i = 0; i < surface.get_w(); i++) { float a=surface[y][i].get_a(); colordata[i] = Color(a,a,a,a); } break; case TARGET_ALPHA_MODE_REDUCE: for(int i = 0; i < surface.get_w(); i++) colordata[i] = Color(surface[y][i].get_r(),surface[y][i].get_g(),surface[y][i].get_b(),1.0f); break; case TARGET_ALPHA_MODE_KEEP: memcpy(colordata,surface[y],rowspan); break; } if(!end_scanline()) { throw(string("add_frame(): target panic on end_scanline()")); return false; } } } } end_frame(); }else //use normal rendering... { #endif SurfaceSW::Handle surfacesw(new SurfaceSW()); Surface &surface = surfacesw->get_surface(); if(!call_renderer(context,surfacesw,quality,desc,0)) { // For some reason, the accelerated renderer failed. if(cb)cb->error(_("Accelerated Renderer Failure")); return false; } // Put the surface we renderer // onto the target. if(!add_frame(&surface)) { if(cb)cb->error(_("Unable to put surface on target")); return false; } #if USE_PIXELRENDERING_LIMIT } #endif } }while(frames); } else { // Set the time that we wish to render if(!get_avoid_time_sync() || canvas->get_time()!=t) canvas->set_time(t); Context context; #ifdef SYNFIG_OPTIMIZE_LAYER_TREE Canvas::Handle op_canvas; if (!getenv("SYNFIG_DISABLE_OPTIMIZE_LAYER_TREE")) { op_canvas = Canvas::create(); op_canvas->set_file_name(canvas->get_file_name()); optimize_layers(canvas->get_time(), canvas->get_context(context_params), op_canvas); context=op_canvas->get_context(context_params); } else context=canvas->get_context(context_params); #else context=canvas->get_context(context_params); #endif // If the quality is set to zero, then we // use the parametric scanline-renderer. if(quality==0 && get_engine().empty()) { if(threads_<=0) { if(!synfig::render(context,this,desc,cb)) return false; } else { if(!synfig::render_threaded(context,this,desc,cb,threads_)) return false; } } else // If quality is set otherwise, then we use the accelerated renderer { #if USE_PIXELRENDERING_LIMIT if(desc.get_w()*desc.get_h() > PIXEL_RENDERING_LIMIT) { SurfaceSW::Handle surfacesw(new SurfaceSW()); Surface &surface = surfacesw->get_surface(); int totalheight = desc.get_h(); int rowheight = PIXEL_RENDERING_LIMIT/desc.get_w(); if (!rowheight) rowheight = 1; // TODO: render partial lines to stay within the limit? int rows = desc.get_h()/rowheight; int lastrowheight = desc.get_h() - rows*rowheight; rows++; synfig::info("Render broken up into %d block%s %d pixels tall, and a final block %d pixels tall", rows-1, rows==2?"":"s", rowheight, lastrowheight); // loop through all the full rows if(!start_frame()) { throw(string("add_frame(): target panic on start_frame()")); return false; } for(int i=0; i < rows; ++i) { RendDesc blockrd = desc; //render the strip at the normal size unless it's the last one... if(i == rows-1) { if(!lastrowheight) break; blockrd.set_subwindow(0,i*rowheight,desc.get_w(),lastrowheight); } else { blockrd.set_subwindow(0,i*rowheight,desc.get_w(),rowheight); } SuperCallback sc(cb, i*rowheight, (i+1)*rowheight, totalheight); if(!call_renderer(context,surfacesw,quality,blockrd,&sc)) { if(cb)cb->error(_("Accelerated Renderer Failure")); return false; } int y; int rowspan=sizeof(Color)*surface.get_w(); Surface::pen pen = surface.begin(); int yoff = i*rowheight; for(y = 0; y < blockrd.get_h(); y++, pen.inc_y()) { Color *colordata= start_scanline(y + yoff); if(!colordata) { throw(string("add_frame(): call to start_scanline(y) returned NULL")); return false; } switch(get_alpha_mode()) { case TARGET_ALPHA_MODE_FILL: for(int i = 0; i < surface.get_w(); i++) colordata[i] = Color::blend(surface[y][i],desc.get_bg_color(),1.0f); break; case TARGET_ALPHA_MODE_EXTRACT: for(int i = 0; i < surface.get_w(); i++) { float a=surface[y][i].get_a(); colordata[i] = Color(a,a,a,a); } break; case TARGET_ALPHA_MODE_REDUCE: for(int i = 0; i < surface.get_w(); i++) colordata[i] = Color(surface[y][i].get_r(),surface[y][i].get_g(),surface[y][i].get_b(),1.0f); break; case TARGET_ALPHA_MODE_KEEP: memcpy(colordata,surface[y],rowspan); break; } if(!end_scanline()) { throw(string("add_frame(): target panic on end_scanline()")); return false; } } //I'm done with this part sc.amount_complete(100,100); } end_frame(); }else { #endif SurfaceSW::Handle surfacesw(new SurfaceSW()); Surface &surface = surfacesw->get_surface(); if(!call_renderer(context,surfacesw,quality,desc,cb)) { if(cb)cb->error(_("Accelerated Renderer Failure")); return false; } // Put the surface we renderer // onto the target. if(!add_frame(&surface)) { if(cb)cb->error(_("Unable to put surface on target")); return false; } #if USE_PIXELRENDERING_LIMIT } #endif } } } catch(String str) { if(cb)cb->error(_("Caught string :")+str); return false; } catch(std::bad_alloc) { if(cb)cb->error(_("Ran out of memory (Probably a bug)")); return false; } catch(...) { if(cb)cb->error(_("Caught unknown error, rethrowing...")); throw; } return true; }