Ejemplo n.º 1
0
void PhotoScaleUnit::process_package(LoadPackage *package)
{
	PhotoScalePackage *pkg = (PhotoScalePackage*)package;
	VFrame *input = server->plugin->get_input();
	int w = input->get_w();
	int h = input->get_h();

	
	switch(input->get_color_model())
	{
		case BC_RGB_FLOAT:
			SCAN_BORDER(float, 3, 0);
			break;
		case BC_RGBA_FLOAT:
			SCAN_BORDER(float, 4, 0);
			break;
		case BC_RGB888:
			SCAN_BORDER(unsigned char, 3, 0);
			break;
		case BC_YUV888:
			SCAN_BORDER(unsigned char, 3, 1);
			break;
		case BC_RGBA8888:
		case BC_YUVA8888:
			SCAN_BORDER(unsigned char, 4, 0);
			break;
	}
}
Ejemplo n.º 2
0
int RecVideoOverlay::
overlay(VFrame *out)
{
	VFrame *in = vframe;
	int xx = x * scale, yy = y * scale;
	int w = in->get_w(), h = in->get_h();
	int ww = w * scale, hh = h * scale;
	BC_WindowBase::get_cmodels()->transfer(out->get_rows(), in->get_rows(),
		out->get_y(), out->get_u(), out->get_v(),
		in->get_y(), in->get_u(), in->get_v(),
		0, 0, w, h, xx, yy, ww, hh,
		in->get_color_model(), out->get_color_model(), 0,
		in->get_bytes_per_line(), out->get_bytes_per_line());
	return ticks > 0 && --ticks == 0 ? 1 : 0;
}
Ejemplo n.º 3
0
BC_Bitmap::BC_Bitmap(BC_WindowBase *parent_window, unsigned char *png_data)
{
// Decompress data into a temporary vframe
    VFrame frame;

    frame.read_png(png_data);

// Initialize the bitmap
    initialize(parent_window,
               frame.get_w(),
               frame.get_h(),
               parent_window->get_color_model(),
               0);

// Copy the vframe to the bitmap
    read_frame(&frame, 0, 0, w, h);
}
Ejemplo n.º 4
0
void BC_Toggle::calculate_extents(BC_WindowBase *gui, 
	VFrame **images,
	int bottom_justify,
	int *text_line,
	int *w,
	int *h,
	int *toggle_x,
	int *toggle_y,
	int *text_x,
	int *text_y, 
	int *text_w,
	int *text_h, 
	const char *caption)
{
	BC_Resources *resources = get_resources();
	VFrame *frame = images[0];
	*w = frame->get_w();
	*h = frame->get_h();
	*toggle_x = 0;
	*toggle_y = 0;
	*text_x = *w + 5;
	*text_y = 0;
	*text_w = 0;
	*text_h = 0;

	if(caption)
	{
		*text_w = gui->get_text_width(MEDIUMFONT, caption);
		*text_h = gui->get_text_height(MEDIUMFONT);

		if(resources->toggle_highlight_bg)
		{
			*text_w += resources->toggle_text_margin * 2;
			*text_h = MAX(*text_h, resources->toggle_highlight_bg->get_h());
		}

		if(*text_h > *h)
		{
			*toggle_y = (*text_h - *h) >> 1;
			*h = *text_h;
		}
		else
Ejemplo n.º 5
0
int OilEffect::process_realtime(VFrame *input, VFrame *output)
{
    need_reconfigure |= load_configuration();



//printf("OilEffect::process_realtime %f %d\n", config.radius, config.use_intensity);
    this->input = input;
    this->output = output;

    if(EQUIV(config.radius, 0))
    {
        if(input->get_rows()[0] != output->get_rows()[0])
            output->copy_from(input);
    }
    else
    {
        if(input->get_rows()[0] == output->get_rows()[0])
        {
            if(!temp_frame) temp_frame = new VFrame(0,
                                                        -1,
                                                        input->get_w(),
                                                        input->get_h(),
                                                        input->get_color_model(),
                                                        -1);
            temp_frame->copy_from(input);
            this->input = temp_frame;
        }


        if(!engine)
        {
            engine = new OilServer(this, (PluginClient::smp + 1));
        }

        engine->process_packages();
    }



    return 0;
}
Ejemplo n.º 6
0
int FileDB::read_frame(VFrame *frame)
{
	int sw, sh;
	mdb->attachDb();
	int result = seq_no < clip_size ? 0 : 1;
	if( !result ) {
		int n = seq_no++;
		if( !n ) { frame_id = -1; }
		else if( n >= prefix_size ) n += suffix_offset;
		result = mdb->get_sequences(clip_id, n);
		if( !result && mdb->timeline_sequence_no() == n )
			frame_id = mdb->timeline_frame_id();
	}
	VFrame *fp = frame->get_w() == swidth && frame->get_h() == sheight &&
			frame->get_color_model() == BC_YUV420P ? frame :
		!vframe ? (vframe = new VFrame(swidth,sheight,BC_YUV420P)) :
		vframe;
	if( !result ) {
		if( frame_id < 0 )
			memset(fp->get_y(), 0, swidth*sheight);
		else
			result = mdb->get_image(frame_id, fp->get_y(), sw,sh);
	}
//printf("seq_no=%d, result=%d\n",seq_no,result);
	mdb->detachDb();
	if( !result ) {
		memset(fp->get_u(),0x80,swidth/2 * sheight/2);
		memset(fp->get_v(),0x80,swidth/2 * sheight/2);
	}
	if( !result && fp == vframe ) {
		BC_CModels::transfer(frame->get_rows(), fp->get_rows(),
			frame->get_y(), frame->get_u(), frame->get_v(),
			fp->get_y(), fp->get_u(), fp->get_v(),
			0, 0, fp->get_w(), fp->get_h(),
			0, 0, frame->get_w(), frame->get_h(),
			fp->get_color_model(), frame->get_color_model(), 0,
			fp->get_bytes_per_line(), swidth);
	}
	return result;
}
Ejemplo n.º 7
0
void InterpolateVideo::average()
{
	VFrame *frame = get_output();
	int w = frame->get_w();
	int h = frame->get_h();

	switch(frame->get_color_model())
	{
		case BC_RGB_FLOAT:
			AVERAGE(float, float, 3, 1);
			break;
		case BC_RGB888:
		case BC_YUV888:
			AVERAGE(unsigned char, int, 3, 0xff);
			break;
		case BC_RGBA_FLOAT:
			AVERAGE(float, float, 4, 1);
			break;
		case BC_RGBA8888:
		case BC_YUVA8888:
			AVERAGE(unsigned char, int, 4, 0xff);
			break;
	}
}
Ejemplo n.º 8
0
int FileYUV::write_frames(VFrame ***layers, int len)
{
	int result;

	// only one layer supported
	VFrame **frames = layers[0];
	VFrame *frame;

	for (int n = 0; n < len; n++) 
	{
		frame = frames[n];

		// short cut for direct copy routines
		if (frame->get_color_model() == BC_COMPRESSED) 
		{
			long frame_size = frame->get_compressed_size();
			if (incoming_asset->format == FILE_YUV) 
				return stream->write_frame_raw(frame->get_data(), frame_size);

			// decode and write an encoded frame
			if (FFMPEG::codec_id(incoming_asset->vcodec) != CODEC_ID_NONE) 
			{
				if (! ffmpeg) 
				{
					ffmpeg = new FFMPEG(incoming_asset);
					ffmpeg->init(incoming_asset->vcodec);
				}
				
				ensure_temp(incoming_asset->width, incoming_asset->height); 
				int result = ffmpeg->decode(frame->get_data(), frame_size, temp);

				// some formats are decoded one frame later
				if (result == FFMPEG_LATENCY) 
				{
					// remember to write the last frame
					pipe_latency++;
					return 0;
				}

				if (result) 
				{
					delete ffmpeg;
					ffmpeg = 0;
					return 1;
				}


				uint8_t *yuv[3];
				yuv[0] = temp->get_y();
				yuv[1] = temp->get_u();
				yuv[2] = temp->get_v();
				return stream->write_frame(yuv);
			}
		}

		// process through a temp frame only if necessary
		if (! cmodel_is_planar(frame->get_color_model()) ||
		    (frame->get_w() != stream->get_width()) ||
		    (frame->get_h() != stream->get_height())) 
		{
			ensure_temp(asset->width, asset->height);
			FFMPEG::convert_cmodel(frame, temp);
			frame = temp;
		}

		uint8_t *yuv[3];
		yuv[0] = frame->get_y();
		yuv[1] = frame->get_u();
		yuv[2] = frame->get_v();
		result = stream->write_frame(yuv);
		if (result) return result;
	}

	return 0;
}
Ejemplo n.º 9
0
void MaskUnit::process_package(LoadPackage *package)
{
	MaskPackage *ptr = (MaskPackage*)package;

	int start_row = SHRT_MIN;         // part for which mask exists
	int end_row;
	if(engine->recalculate)
	{
		VFrame *mask;
		if(engine->feather > 0) 
			mask = engine->temp_mask;
		else
			mask = engine->mask;

SET_TRACE
// Generated oversampling frame
		int mask_w = mask->get_w();
		int mask_h = mask->get_h();
		int mask_color_model = mask->get_color_model();
		int oversampled_package_w = mask_w * OVERSAMPLE;
		int oversampled_package_h = (ptr->row2 - ptr->row1) * OVERSAMPLE;
//printf("MaskUnit::process_package 1\n");

SET_TRACE

		int local_first_nonempty_rowspan = SHRT_MIN;
		int local_last_nonempty_rowspan = SHRT_MIN;

		if (!row_spans || row_spans_h != mask_h * OVERSAMPLE) {
			int i;	
			if (row_spans) {   /* size change */
				for (i = 0; i < row_spans_h; i++) 
					free(row_spans[i]);
				delete [] row_spans;
			}
			row_spans_h = mask_h * OVERSAMPLE;
			row_spans = new short *[mask_h * OVERSAMPLE]; 
			for (i= 0; i<mask_h * OVERSAMPLE; i++) {
				/* we use malloc so we can use realloc */
				row_spans[i] = (short *)malloc(sizeof(short) * NUM_SPANS);
				/* [0] is initialized later */
				row_spans[i][1] = NUM_SPANS;
			}
		}

SET_TRACE
//printf("MaskUnit::process_package 1 %d\n", engine->point_sets.total);

SET_TRACE

// Draw bezier curves onto span buffer
//struct timeval start_time;
//gettimeofday(&start_time, 0);

		for(int k = 0; k < engine->point_sets.total; k++)
		{
			int old_x, old_y;
			old_x = SHRT_MIN; // sentinel
			ArrayList<MaskPoint*> *points = engine->point_sets.values[k];

			if(points->total < 2) continue;
//printf("MaskUnit::process_package 2 %d %d\n", k, points->total);
			for (int i = ptr->row1 * OVERSAMPLE; i < ptr->row2 * OVERSAMPLE; i++) 
				row_spans[i][0] = 2; /* initialize to zero */ 
			(ptr->row1*OVERSAMPLE, ptr->row2*OVERSAMPLE); // init just my rows
			for(int i = 0; i < points->total; i++)
			{
				MaskPoint *point1 = points->values[i];
				MaskPoint *point2 = (i >= points->total - 1) ? 
					points->values[0] : 
					points->values[i + 1];

				float x0 = point1->x;
				float y0 = point1->y;
				float x1 = point1->x + point1->control_x2;
				float y1 = point1->y + point1->control_y2;
				float x2 = point2->x + point2->control_x1;
				float y2 = point2->y + point2->control_y1;
				float x3 = point2->x;
				float y3 = point2->y;

				// possible optimization here... since these coordinates are bounding box for curve
				// we can continue with next curve if they are out of our range

				// forward differencing bezier curves implementation taken from GPL code at
				// http://cvs.sourceforge.net/viewcvs.py/guliverkli/guliverkli/src/subtitles/Rasterizer.cpp?rev=1.3

				float cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0;

				// [-1 +3 -3 +1]
				// [+3 -6 +3  0]
				// [-3 +3  0  0]
				// [+1  0  0  0]

		 		cx3 = (-  x0 + 3*x1 - 3*x2 + x3) * OVERSAMPLE;
				cx2 = ( 3*x0 - 6*x1 + 3*x2) * OVERSAMPLE;
				cx1 = (-3*x0 + 3*x1) * OVERSAMPLE;
				cx0 = (   x0) * OVERSAMPLE;

				cy3 = (-  y0 + 3*y1 - 3*y2 + y3) * OVERSAMPLE;
				cy2 = ( 3*y0 - 6*y1 + 3*y2) * OVERSAMPLE;
				cy1 = (-3*y0 + 3*y1) * OVERSAMPLE;
				cy0 = (   y0 - ptr->row1) * OVERSAMPLE;

				float maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
				float maxaccel2 = fabs(2*cx2) + fabs(6*cx3);

				float maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
				float h = 1.0;

				if(maxaccel > 8.0 * OVERSAMPLE) h = sqrt((8.0 * OVERSAMPLE) / maxaccel);

				for(float t = 0.0; t < 1.0; t += h)
				{
					int x = (int) (cx0 + t*(cx1 + t*(cx2 + t*cx3)));
					int y = (int) (cy0 + t*(cy1 + t*(cy2 + t*cy3)));

					if (old_x != SHRT_MIN) 
						draw_line_clamped(old_x, old_y, x, y, oversampled_package_w, oversampled_package_h, ptr->row1 * OVERSAMPLE);
					old_x = x;
					old_y = y;
				}

				int x = (int)(x3 * OVERSAMPLE);
				int y = (int)((y3 - ptr->row1) * OVERSAMPLE);
				draw_line_clamped(old_x, old_y, x, y, oversampled_package_w, oversampled_package_h, ptr->row1 * OVERSAMPLE);
				old_x = (int)x;
				old_y = (int)y;
		
			}
//printf("MaskUnit::process_package 1\n");

			// Now we have ordered spans ready!
			//printf("Segment : %i , row1: %i\n", oversampled_package_h, ptr->row1);
			uint16_t value;
			if (mask_color_model == BC_A8)
				value = (int)((float)engine->value / 100 * 0xff);
			else
				value = (int)((float)engine->value / 100 * 0xffff);	// also for BC_A_FLOAT

			/* Scaneline sampling, inspired by Graphics gems I, page 81 */
			for (int i = ptr->row1; i < ptr->row2; i++) 
			{
				short min_x = SHRT_MAX;
				short max_x = SHRT_MIN;
				int j; 				/* universal counter for 0..OVERSAMPLE-1 */
				short *span;			/* current span - set inside loops with j */
				short span_p[OVERSAMPLE];	/* pointers to current positions in spans */
				#define P (span_p[j])		/* current span pointer */
				#define MAXP (span[0])		/* current span length */
				int num_empty_spans = 0;
				/* get the initial span pointers ready */
				for (j = 0; j < OVERSAMPLE; j++)
				{	
					span = row_spans[j + i * OVERSAMPLE];
					P = 2;              /* starting pointers to spans */
						/* hypotetical hypotetical fix goes here: take care that there is maximum one empty span for every subpixel */ 
					if (MAXP != 2) {                                        /* if span is not empty */
						if (span[2] < min_x) min_x = span[2];           /* take start of the first span */
						if (span[MAXP-1] > max_x) max_x = span[MAXP-1]; /* and end of last */
					} else              
					{	/* span is empty */
						num_empty_spans ++;	
					}	
				}
				if (num_empty_spans == OVERSAMPLE)
					continue; /* no work for us here */
				else 
				{       /* if we have engaged first nonempty rowspan...	remember it to speed up mask applying */
					if (local_first_nonempty_rowspan < 0 || i < local_first_nonempty_rowspan) 
						local_first_nonempty_rowspan = i;  
					if (i > local_last_nonempty_rowspan) local_last_nonempty_rowspan = i;
				}
				/* we have some pixels to fill, do coverage calculation for span */

				void *output_row = (unsigned char*)mask->get_rows()[i];
				min_x = min_x / OVERSAMPLE;
				max_x = (max_x + OVERSAMPLE - 1) / OVERSAMPLE;
				
				/* printf("row %i, pixel range: %i %i, spans0: %i\n", i, min_x, max_x, row_spans[i*OVERSAMPLE][0]-2); */

				/* this is not a full loop, since we jump trough h if possible */
				for (int h = min_x; h <= max_x; h++) 
				{
					short pixelleft = h * OVERSAMPLE;  /* leftmost subpixel of pixel*/
					short pixelright = pixelleft + OVERSAMPLE - 1; /* rightmost subpixel of pixel */
					uint32_t coverage = 0;
					int num_left = 0;               /* number of spans that have start left of the next pixel */
					short right_end = SHRT_MAX;     /* leftmost end of any span - right end of a full scanline */
					short right_start = SHRT_MAX;   /* leftmost start of any span - left end of empty scanline */

					for (j=0; j< OVERSAMPLE; j++) 
					{	
						char chg = 1;
						span = row_spans[j + i * OVERSAMPLE];
						while (P < MAXP && chg)
						{
						//	printf("Sp: %i %i\n", span[P], span[P+1]);
							if (span[P] == span[P+1])           /* ignore empty spans */
							{
								P +=2;
								continue;
							}
							if (span[P] <= pixelright)          /* if span start is before the end of pixel */
								coverage += MIN(span[P+1], pixelright)  /* 'clip' the span to pixel */
		                                                          - MAX(span[P], pixelleft) + 1;
							if (span[P+1] <= pixelright) 
								P += 2;
							else 
								chg = 0;
						} 
						if (P == MAXP) 
							num_left = -OVERSAMPLE; /* just take care that num_left cannot equal OVERSAMPLE or zero again */
						else	
						{ 
							if (span[P] <= pixelright)  /* if span starts before subpixel in the pixel on the right */
							{    /* useful for determining filled space till next non-fully-filled pixel */
								num_left ++;						
								if (span[P+1] < right_end) right_end = span[P+1]; 
							} else 
							{    /* useful for determining empty space till next non-empty pixel */
								if (span[P] < right_start) right_start = span[P]; 
							}
						}
					}
					// calculate coverage
					coverage *= value;
					coverage /= OVERSAMPLE * OVERSAMPLE;

					// when we have multiple masks the highest coverage wins
					switch (mask_color_model)
					{
					case BC_A8:
						if (((unsigned char *) output_row)[h] < coverage)
							((unsigned char*)output_row)[h] = coverage;
						break;
					case BC_A16:
						if (((uint16_t *) output_row)[h] < coverage)
							((uint16_t *) output_row)[h] = coverage;
						break;
					case BC_A_FLOAT:
						if (((float *) output_row)[h] < coverage/float(0xffff))
							((float *) output_row)[h] = coverage/float(0xffff);
						break;
					}
					/* possible optimization: do joining of multiple masks by span logics, not by bitmap logics*/
					
					if (num_left == OVERSAMPLE) 
					{
						/* all current spans start more left than next pixel */
						/* this means we can probably (if lucky) draw a longer horizontal line */
						right_end = (right_end / OVERSAMPLE) - 1; /* last fully covered pixel */
						if (right_end > h)
						{
							if (mask_color_model == BC_A8) 
								memset((char *)output_row + h + 1, value, right_end - h);
							else {
								/* we are f****d, since there is no 16bit memset */
								if (mask_color_model == BC_A16) {
									for (int z = h +1; z <= right_end; z++)
										((uint16_t *) output_row)[z] =  value;
								} else {
									for (int z = h +1; z <= right_end; z++)
										((float *) output_row)[z] =  value/float(0xffff);
								}
							}
							h = right_end;  
						}
					} else 
					if (num_left == 0) 
					{
						/* all current spans start right of next pixel */ 
						/* this means we can probably (if lucky) skip some pixels */
						right_start = (right_start / OVERSAMPLE) - 1; /* last fully empty pixel */
						if (right_start > h)
						{
							h = right_start;
						}
					}
				}
			}
		}
		engine->protect_data.lock();
		if (local_first_nonempty_rowspan < engine->first_nonempty_rowspan)
			engine->first_nonempty_rowspan = local_first_nonempty_rowspan;
		if (local_last_nonempty_rowspan > engine->last_nonempty_rowspan)
			engine->last_nonempty_rowspan = local_last_nonempty_rowspan;
		engine->protect_data.unlock();
	

//		int64_t dif= get_difference(&start_time);
//		printf("diff: %lli\n", dif);
	}	/* END OF RECALCULATION! */
Ejemplo n.º 10
0
void BluebananaUnit::process_package(LoadPackage *package){
  BluebananaPackage *pkg = (BluebananaPackage*)package;
  BluebananaEngine *engine = (BluebananaEngine*)pkg->engine;

  VFrame *frame = engine->data;
  int w = frame->get_w();
  int h = frame->get_h();
  int ant = plugin->ants_counter;
  int gui_open = plugin->gui_open();
  int show_ants = plugin->config.mark && gui_open;
  int j;

  int active = plugin->config.active;
  int use_mask = plugin->config.use_mask;
  int capture_mask = plugin->config.capture_mask;
  int invert_selection = plugin->config.invert_selection;

  float *Hl = (plugin->config.Hsel_active &&
               (plugin->config.Hsel_lo!=0 ||
                plugin->config.Hsel_hi!=360)) ? plugin->hue_select_alpha_lookup : NULL;
  float *Sl = (plugin->config.Ssel_active &&
               (plugin->config.Ssel_lo!=0  ||
                plugin->config.Ssel_hi!=100)) ? plugin->sat_select_alpha_lookup : NULL;
  float *Vl = (plugin->config.Vsel_active &&
               (plugin->config.Vsel_lo!=0 ||
                plugin->config.Vsel_hi!=100)) ? plugin->val_select_alpha_lookup : NULL;

  float Hal = plugin->config.Hadj_active ? plugin->config.Hadj_val/60.f : 0.f;

  float *Sal = (plugin->config.Sadj_active &&
                (plugin->config.Sadj_lo!=0 ||
                 plugin->config.Sadj_hi!=100 ||
                 plugin->config.Sadj_gamma!=1)) ? plugin->sat_adj_lookup : NULL;
  float *Val = (plugin->config.Vadj_active &&
                (plugin->config.Vadj_lo!=0 ||
                 plugin->config.Vadj_hi!=100 ||
                 plugin->config.Vadj_gamma!=1)) ? plugin->val_adj_lookup : NULL;
  float *Ral = (plugin->config.Radj_active &&
                (plugin->config.Radj_lo!=0 ||
                 plugin->config.Radj_hi!=100 ||
                 plugin->config.Radj_gamma!=1)) ? plugin->red_adj_lookup : NULL;
  float *Gal = (plugin->config.Gadj_active &&
                (plugin->config.Gadj_lo!=0 ||
                 plugin->config.Gadj_hi!=100 ||
                 plugin->config.Gadj_gamma!=1)) ? plugin->green_adj_lookup : NULL;
  float *Bal = (plugin->config.Badj_active &&
                (plugin->config.Badj_lo!=0 ||
                 plugin->config.Badj_hi!=100 ||
                 plugin->config.Badj_gamma!=1)) ? plugin->blue_adj_lookup : NULL;

  float Sas = plugin->sat_adj_toe_slope;
  float Vas = plugin->val_adj_toe_slope;
  float Ras = plugin->red_adj_toe_slope;
  float Gas = plugin->green_adj_toe_slope;
  float Bas = plugin->blue_adj_toe_slope;

  float Aal = plugin->config.Oadj_active ? plugin->config.Oadj_val*.01 : 1.f;

  float Vscale = (plugin->config.Vadj_hi-plugin->config.Vadj_lo) / 100.f;
  float Vshift = plugin->config.Vadj_lo / 100.f;
  float Vgamma = plugin->config.Vadj_gamma;

  int doRGB = Ral || Gal || Bal;

  int doHSV = (Hal!=0) || Sal || Val;

  int doSEL = ( Hl || Sl || Vl ) && (active || show_ants);

  int shaping = plugin->config.Fsel_active && doSEL &&
    (plugin->config.Fsel_lo || plugin->config.Fsel_hi || plugin->config.Fsel_over);

  int byte_advance=0;
  int have_alpha=1;

#define SPLIT 128

  int tasks = engine->get_total_packages()*16;
  int taski,rowi,coli;

  /* do as much work entirely local to thread memory as possible */
  unsigned char row_fragment[SPLIT*16];
  float *selection_fullframe=NULL;
  float  selection[SPLIT];

  float Rvec[SPLIT];
  float Gvec[SPLIT];
  float Bvec[SPLIT];

  float Avec[SPLIT];
  float Hvec[SPLIT];
  float Svec[SPLIT];
  float Vvec[SPLIT];

  float *Rhist = pkg->Rhist;
  float *Ghist = pkg->Ghist;
  float *Bhist = pkg->Bhist;
  float *Hhist = pkg->Hhist;
  float *Shist = pkg->Shist;
  float *Vhist = pkg->Vhist;
  float Htotal=0.f;
  float Hweight=0.f;

  float *Hhr = pkg->Hhr;
  float *Hhg = pkg->Hhg;
  float *Hhb = pkg->Hhb;
  float *Shr = pkg->Shr;
  float *Shg = pkg->Shg;
  float *Shb = pkg->Shb;
  float *Vhr = pkg->Vhr;
  float *Vhg = pkg->Vhg;
  float *Vhb = pkg->Vhb;

  memset(Rhist,0,sizeof(pkg->Rhist));
  memset(Ghist,0,sizeof(pkg->Ghist));
  memset(Bhist,0,sizeof(pkg->Bhist));
  memset(Hhist,0,sizeof(pkg->Hhist));
  memset(Shist,0,sizeof(pkg->Shist));
  memset(Vhist,0,sizeof(pkg->Vhist));

  memset(Hhr,0,sizeof(pkg->Hhr));
  memset(Hhg,0,sizeof(pkg->Hhg));
  memset(Hhb,0,sizeof(pkg->Hhb));
  memset(Shr,0,sizeof(pkg->Shr));
  memset(Shg,0,sizeof(pkg->Shg));
  memset(Shb,0,sizeof(pkg->Shb));
  memset(Vhr,0,sizeof(pkg->Vhr));
  memset(Vhg,0,sizeof(pkg->Vhg));
  memset(Vhb,0,sizeof(pkg->Vhb));

  /* If we're doing fill shaping, we need to compute base selection
     for the entire frame before shaping. */

  if(shaping){
    engine->set_task(tasks*2,"shaping_even");
    while ( (taski = engine->next_task()) >= 0){

    /* operate on discontinuous, interleaved sections of the source
       buffer in two passes.  Although we could take extra steps to
       make cache contention across cores completely impossible, the
       extra locking and central join required isn't worth it.  It's
       preferable to make contention merely highly unlikely. */

      int start_row, end_row;
      if(taski<tasks){
        start_row = (taski*2)*h/(tasks*2);
        end_row = (taski*2+1)*h/(tasks*2);
      }else{
        start_row = ((taski-tasks)*2+1)*h/(tasks*2);
        end_row = ((taski-tasks)*2+2)*h/(tasks*2);
      }

      for(rowi = start_row; rowi<end_row; rowi++){
        unsigned char *row = frame->get_rows()[rowi];

        for(coli=0;coli<w;coli+=SPLIT){

          int todo = (w-coli>SPLIT)?SPLIT:(w-coli);
          float *A = engine->selection_workA+w*rowi+coli;

          switch(frame->get_color_model()) {
          case BC_RGB888:
            rgb8_to_RGB((unsigned char *)row,Rvec,Gvec,Bvec,todo);
            row += todo*3;
            break;
          case BC_RGBA8888:
            rgba8_to_RGBA((unsigned char *)row,Rvec,Gvec,Bvec,Avec,todo);
            row += todo*4;
            break;
          case BC_RGB_FLOAT:
            rgbF_to_RGB((float *)row,Rvec,Gvec,Bvec,todo);
            row += todo*12;
            break;
          case BC_RGBA_FLOAT:
            rgbaF_to_RGBA((float *)row,Rvec,Gvec,Bvec,Avec,todo);
            row += todo*16;
            break;
          case BC_YUV888:
            yuv8_to_RGB((unsigned char *)row,Rvec,Gvec,Bvec,todo);
            row += todo*3;
            break;
          case BC_YUVA8888:
            yuva8_to_RGBA((unsigned char *)row,Rvec,Gvec,Bvec,Avec,todo);
            row += todo*4;
            break;
          }

          for(j = 0; j < todo; j++)
            RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);

          if(Hl)
            for(j = 0; j < todo; j++)
              selection[j]=sel_lookup(Hvec[j]*.166666667f,Hl);
          else
            for(j = 0; j < todo; j++)
              selection[j]=1.f;

          if(Sl)
            for(j = 0; j < todo; j++)
              selection[j]*=sel_lookup(Svec[j],Sl);

          if(Vl)
            for(j = 0; j < todo; j++)
              selection[j]*=sel_lookup(Vvec[j],Vl);

          /* lock the memcpy to prevent pessimal cache coherency
             interleave across cores. */
          pthread_mutex_lock(&engine->copylock);
          memcpy(A,selection,sizeof(*selection)*todo);
          pthread_mutex_unlock(&engine->copylock);
        }
      }
    }

    /* Perform fill shaping on the selection */
    selection_fullframe = plugin->fill_selection(engine->selection_workA,
                                                 engine->selection_workB, w, h, engine);
  }

  /* row-by-row color modification and histogram feedback */
  engine->set_task(tasks*2,"modification_even");
  while((taski = engine->next_task())>=0){

    /* operate on discontinuous, interleaved sections of the source
       buffer in two passes.  Although we could take extra steps to
       make cache contention across cores completely impossible, the
       extra locking and central join required isn't worth it.  It's
       preferable to make contention merely highly unlikely. */

    int start_row, end_row;
    if(taski<tasks){
      start_row = (taski*2)*h/(tasks*2);
      end_row = (taski*2+1)*h/(tasks*2);
    }else{
      start_row = ((taski-tasks)*2+1)*h/(tasks*2);
      end_row = ((taski-tasks)*2+2)*h/(tasks*2);
    }

    for(rowi = start_row; rowi<end_row; rowi++){
      unsigned char *row = frame->get_rows()[rowi];

      for(int coli=0;coli<w;coli+=SPLIT){
        int todo = (w-coli>SPLIT)?SPLIT:(w-coli);
        int have_selection = 0;

        /* convert from pipeline color format */
        if(active || show_ants || (use_mask && capture_mask && have_alpha)){

          switch(frame->get_color_model()) {
          case BC_RGB888:
            pthread_mutex_lock(&engine->copylock);
            memcpy(row_fragment,row,todo*3);
            pthread_mutex_unlock(&engine->copylock);
            rgb8_to_RGB(row_fragment,Rvec,Gvec,Bvec,todo);
            byte_advance = todo*3;
            have_alpha=0;
            break;

          case BC_RGBA8888:
            pthread_mutex_lock(&engine->copylock);
            memcpy(row_fragment,row,todo*4);
            pthread_mutex_unlock(&engine->copylock);
            rgba8_to_RGBA(row_fragment,Rvec,Gvec,Bvec,Avec,todo);
            byte_advance = todo*4;
            have_alpha=1;
            break;

          case BC_RGB_FLOAT:
            pthread_mutex_lock(&engine->copylock);
            memcpy(row_fragment,row,todo*12);
            pthread_mutex_unlock(&engine->copylock);
            rgbF_to_RGB((float *)row_fragment,Rvec,Gvec,Bvec,todo);
            byte_advance = todo*12;
            have_alpha=0;
            break;

          case BC_RGBA_FLOAT:
            pthread_mutex_lock(&engine->copylock);
            memcpy(row_fragment,row,todo*16);
            pthread_mutex_unlock(&engine->copylock);
            rgbaF_to_RGBA((float *)row_fragment,Rvec,Gvec,Bvec,Avec,todo);
            byte_advance = todo*16;
            have_alpha=1;
            break;

          case BC_YUV888:
            pthread_mutex_lock(&engine->copylock);
            memcpy(row_fragment,row,todo*3);
            pthread_mutex_unlock(&engine->copylock);
            yuv8_to_RGB(row_fragment,Rvec,Gvec,Bvec,todo);
            byte_advance = todo*3;
            have_alpha=0;
            break;

          case BC_YUVA8888:
            pthread_mutex_lock(&engine->copylock);
            memcpy(row_fragment,row,todo*4);
            pthread_mutex_unlock(&engine->copylock);
            yuva8_to_RGBA(row,Rvec,Gvec,Bvec,Avec,todo);
            byte_advance = todo*4;
            have_alpha=1;
            break;
          }

          if(doSEL)
            /* generate initial HSV values [if selection active] */
            for(j = 0; j < todo; j++)
              RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);


          float selection_test=todo;

          if(selection_fullframe){

            /* get the full-frame selection data we need into thread-local storage */
            /* the full-frame data is read-only at this point, no need to lock */
            float *sf = selection_fullframe + rowi*w + coli;
            selection_test=0.f;
            for(j = 0; j < todo; j++)
              selection_test += selection[j] = sf[j];
            have_selection=1;

          }else{

            /* selection computation when no full-frame shaping */
            if(Hl){
              selection_test=0.f;
              for(j = 0; j < todo; j++)
                selection_test += selection[j] = sel_lookup(Hvec[j]*.166666667f,Hl);
              have_selection=1;
            }

            if(Sl){
              if(have_selection){
                if(selection_test>SELECT_THRESH){
                  selection_test=0.f;
                  for(j = 0; j < todo; j++)
                    selection_test += selection[j] *= sel_lookup(Svec[j],Sl);
                }
              }else{
                selection_test=0.f;
                for(j = 0; j < todo; j++)
                  selection_test += selection[j] = sel_lookup(Svec[j],Sl);
                have_selection=1;
              }
            }

            if(Vl){
              if(have_selection){
                if(selection_test>SELECT_THRESH){
                  selection_test=0.f;
                  for(j = 0; j < todo; j++)
                    selection_test += selection[j] *= sel_lookup(Vvec[j],Vl);
                }
              }else{
                selection_test=0.f;
                for(j = 0; j < todo; j++)
                  selection_test += selection[j] = sel_lookup(Vvec[j],Vl);
                have_selection=1;
              }
            }
          }

          /* selection modification according to config */
          if(use_mask && have_alpha){
            if(!have_selection){
              /* selection consists only of mask */
              selection_test=0.;
              for(j = 0; j < todo; j++)
                selection_test += selection[j] = Avec[j];
              have_selection=1;
            }else{
              if(invert_selection){
                if(selection_test < SELECT_THRESH){
                  /* fully selected after invert, clip to mask */
                  selection_test=0.f;
                  for(j = 0; j < todo; j++)
                    selection_test += selection[j] = Avec[j];
                }else if (selection_test >= todo-SELECT_THRESH){
                  /* fully deselected after invert */
                  selection_test=0.;
                }else{
                  /* partial selection after invert, clip to mask */
                  selection_test=0.f;
                  for(j = 0; j < todo; j++)
                    selection_test += selection[j] = Avec[j]*(1.f-selection[j]);
                }
              }else{
                if(selection_test < SELECT_THRESH){
                  /* fully deselected */
                }else if (selection_test >= todo-SELECT_THRESH){
                  /* fully selected, clip to mask */
                  selection_test=0.f;
                  for(j = 0; j < todo; j++)
                    selection_test += selection[j] = Avec[j];
                }else{
                  /* partial selection, clip to mask */
                  selection_test=0.f;
                  for(j = 0; j < todo; j++)
                    selection_test += selection[j] *= Avec[j];
                }
              }
            }
            if(selection_test < SELECT_THRESH){
              /* skip processing this fragment */
              /* we're using a mask; if the mask is set to capture, we
                 need to restore alpha before skipping */
              if(capture_mask){
                switch(frame->get_color_model()) {
                case BC_RGBA8888:
                  unmask_rgba8(row_fragment,todo);
                  break;
                case BC_RGBA_FLOAT:
                  unmask_rgbaF((float *)row_fragment,todo);
                  break;
                case BC_YUVA8888:
                  unmask_yuva8(row_fragment,todo);
                  break;
                }
                pthread_mutex_lock(&engine->copylock);
                memcpy(row,row_fragment,byte_advance);
                pthread_mutex_unlock(&engine->copylock);
              }

              row+=byte_advance;
              continue;
            }
            if(selection_test > todo-SELECT_THRESH)
              have_selection=0; // fully selected
          }else{
            if(have_selection){
              if(invert_selection){
                if(selection_test < SELECT_THRESH){
                  /* fully selected after inversion */
                  selection_test=todo;
                }else if (selection_test >= todo-SELECT_THRESH){
                  /* fully deselected after invert, skip fragment */
                  row+=byte_advance;
                  continue;
                }else{
                  /* partial selection */
                  selection_test=0.f;
                  for(j = 0; j < todo; j++)
                    selection_test += selection[j] = (1.f-selection[j]);
                }
              }else{
                if(selection_test < SELECT_THRESH){
                  /* fully deselected, skip fragment */
                  row+=byte_advance;
                  continue;
                }else if (selection_test >= todo-SELECT_THRESH){
                  /* fully selected */
                  selection_test=todo;
                }else{
                  /* partial selection; already calculated */
                }
              }
              if(selection_test < SELECT_THRESH){
                  row+=byte_advance;
                  continue; // inactive fragment
              }
              if(selection_test > todo-SELECT_THRESH)
                have_selection=0; // fully selected
            }else{
              if(invert_selection){
                  row+=byte_advance;
                  continue;
              }
            }
          }
        }

        if(active){

          /* red adjust */
          if(Ral) {
            if(have_selection){
              for(j = 0; j < todo; j++)
                if(selection[j]>SELECT_THRESH) Rvec[j]=adj_lookup(Rvec[j],Ral,Ras);
            }else{
              for(j = 0; j < todo; j++)
                Rvec[j] = adj_lookup(Rvec[j],Ral,Ras);
            }
          }
          /* red histogram */
          if(gui_open){
            if(have_selection){
              for(j = 0; j < todo; j++)
                if(selection[j]>SELECT_THRESH) Rhist[(int)CLAMP((Rvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
            }else{
              for(j = 0; j < todo; j++)
                Rhist[(int)CLAMP((Rvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
            }
          }

          /* green adjust */
          if(Gal) {
            if(have_selection){
              for(j = 0; j < todo; j++)
                if(selection[j]>SELECT_THRESH) Gvec[j]=adj_lookup(Gvec[j],Gal,Gas);
            }else{
              for(j = 0; j < todo; j++)
                Gvec[j] = adj_lookup(Gvec[j],Gal,Gas);
            }
          }
          /* green histogram */
          if(gui_open){
            if(have_selection){
              for(j = 0; j < todo; j++)
                if(selection[j]>SELECT_THRESH) Ghist[(int)CLAMP((Gvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
            }else{
              for(j = 0; j < todo; j++)
                Ghist[(int)CLAMP((Gvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
            }
          }

          /* blue adjust */
          if(Bal) {
            if(have_selection){
              for(j = 0; j < todo; j++)
                if(selection[j]>SELECT_THRESH) Bvec[j]=adj_lookup(Bvec[j],Bal,Bas);
            }else{
              for(j = 0; j < todo; j++)
                Bvec[j] = adj_lookup(Bvec[j],Bal,Bas);
            }
          }
          /* blue histogram */
          if(gui_open){
            if(have_selection){
              for(j = 0; j < todo; j++)
                if(selection[j]>SELECT_THRESH) Bhist[(int)CLAMP((Bvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
            }else{
              for(j = 0; j < todo; j++)
                Bhist[(int)CLAMP((Bvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
            }
          }

          /* compute HSV values or update values from earlier selection work if
             they've changed */
          if( (!doSEL || doRGB) && // not yet computed, or since modified
              (doHSV || gui_open) ) // needed for HSV mod and/or histograms
            for(j = 0; j < todo; j++)
              RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);

          if(doHSV){
            /* H modification */
            /* don't bother checking selection, H adj is lightweight */
            if(Hal){
              for(j = 0; j < todo; j++){
                Hvec[j] += Hal;
                if(Hvec[j]<0.f)Hvec[j]+=6.f;
                if(Hvec[j]>=6.f)Hvec[j]-=6.f;
              }
            }
            /* H histogram */
            /* this is pre-shift RGB data; shift hue later to save an HSV->RGB transform */
            if(gui_open){
              if(have_selection){
                for(j = 0; j < todo; j++){
                  if(selection[j]>SELECT_THRESH){
                    float weight = selection[j]*Svec[j];
                    int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
                    Htotal += selection[j];
                    Hweight += weight;
                    Hhist[bin] += weight;
                    Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
                    Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
                    Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
                  }
                }
              }else{
                for(j = 0; j < todo; j++){
                  float weight = Svec[j];
                  int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
                  Htotal += 1.f;
                  Hweight += weight;
                  Hhist[bin] += weight;
                  Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
                  Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
                  Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
                }
              }
            }

            /* S modification */
            if(Sal) {
              if(have_selection){
                for(j = 0; j < todo; j++)
                  if(selection[j]>SELECT_THRESH) Svec[j] = adj_lookup(Svec[j],Sal,Sas);
              }else{
                for(j = 0; j < todo; j++)
                  Svec[j] = adj_lookup(Svec[j],Sal,Sas);
              }
            }

            /* This is unrolled a few times below...
               Although we're using HSV, we don't want hue/saturation
               changes to have a strong effect on apparent brightness.
               Apply a correction to V (not Y!) based on luma change. */
            /* Calculate new RGB values at same time */
            if(Hal || Sal){
              if(have_selection){
                for(j = 0; j < todo; j++){
                  if(selection[j]>SELECT_THRESH){
                    HSpV_correct_RGB(Hvec[j],Svec[j],Vvec[j],Rvec[j],Gvec[j],Bvec[j]);
                    /* run S histogram at the same time as we've got
                       the RGB data */
                    if(gui_open){
                      int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
                      Shist[bin] += selection[j];
                      Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
                      Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
                      Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
                    }
                  }
                }
              }else{
                for(j = 0; j < todo; j++){
                  HSpV_correct_RGB(Hvec[j],Svec[j],Vvec[j],Rvec[j],Gvec[j],Bvec[j]);
                  if(gui_open){
                    int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
                    Shist[bin] += 1.f;
                    Shr[bin>>HRGBSHIFT] += Rvec[j];
                    Shg[bin>>HRGBSHIFT] += Gvec[j];
                    Shb[bin>>HRGBSHIFT] += Bvec[j];
                  }
                }
              }
            }else{
              if(gui_open){
                if(have_selection){
                  for(j = 0; j < todo; j++){
                    if(selection[j]>SELECT_THRESH){
                      int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
                      Shist[bin] += selection[j];
                      Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
                      Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
                      Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
                    }
                  }
                }else{
                  for(j = 0; j < todo; j++){
                    int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
                    Shist[bin] += 1.f;
                    Shr[bin>>HRGBSHIFT] += Rvec[j];
                    Shg[bin>>HRGBSHIFT] += Gvec[j];
                    Shb[bin>>HRGBSHIFT] += Bvec[j];
                  }
                }
              }
Ejemplo n.º 11
0
void ThresholdUnit::render_data(LoadPackage *package)
{
	const ThresholdPackage *pkg = (ThresholdPackage*)package;
	const ThresholdConfig *config = & server->plugin->config;
	VFrame *data = server->data;
	const int min = (int)(config->min * 0xffff);
	const int max = (int)(config->max * 0xffff);
	const int w = data->get_w();
	const int h = data->get_h();
	
	const TYPE r_low = scale_to_range<TYPE>(config->low_color.r);
	const TYPE g_low = scale_to_range<TYPE>(config->low_color.g);
	const TYPE b_low = scale_to_range<TYPE>(config->low_color.b);
	const TYPE a_low = scale_to_range<TYPE>(config->low_color.a);
	
	const TYPE r_mid = scale_to_range<TYPE>(config->mid_color.r);
	const TYPE g_mid = scale_to_range<TYPE>(config->mid_color.g);
	const TYPE b_mid = scale_to_range<TYPE>(config->mid_color.b);
	const TYPE a_mid = scale_to_range<TYPE>(config->mid_color.a);
	
	const TYPE r_high = scale_to_range<TYPE>(config->high_color.r);
	const TYPE g_high = scale_to_range<TYPE>(config->high_color.g);
	const TYPE b_high = scale_to_range<TYPE>(config->high_color.b);
	const TYPE a_high = scale_to_range<TYPE>(config->high_color.a);

	TYPE y_low,  u_low,  v_low;
	TYPE y_mid,  u_mid,  v_mid;
	TYPE y_high, u_high, v_high;

	if (USE_YUV)
	{
		rgb_to_yuv(*server->yuv, r_low,  g_low,  b_low,  y_low,  u_low,  v_low);
		rgb_to_yuv(*server->yuv, r_mid,  g_mid,  b_mid,  y_mid,  u_mid,  v_mid);
		rgb_to_yuv(*server->yuv, r_high, g_high, b_high, y_high, u_high, v_high);
	}

	for(int i = pkg->start; i < pkg->end; i++)
	{
		TYPE *in_row = (TYPE*)data->get_rows()[i];
		TYPE *out_row = in_row;
		for(int j = 0; j < w; j++)
		{
			if (USE_YUV)
			{
				const int y = get_component(in_row[0]);
				if (y < min)
				{
					*out_row++ = y_low;
					*out_row++ = u_low;
					*out_row++ = v_low;
					if(COMPONENTS == 4) *out_row++ = a_low;
				}
				else if (y < max)
				{
					*out_row++ = y_mid;
					*out_row++ = u_mid;
					*out_row++ = v_mid;
					if(COMPONENTS == 4) *out_row++ = a_mid;
				}
				else
				{
					*out_row++ = y_high;
					*out_row++ = u_high;
					*out_row++ = v_high;
					if(COMPONENTS == 4) *out_row++ = a_high;
				}
			}
			else
			{
				const int r = get_component(in_row[0]);
				const int g = get_component(in_row[1]);
				const int b = get_component(in_row[2]);
				const int y = (r * 76 + g * 150 + b * 29) >> 8;
				if (y < min)
				{
					*out_row++ = r_low;
					*out_row++ = g_low;
					*out_row++ = b_low;
					if(COMPONENTS == 4) *out_row++ = a_low;
				}
				else if (y < max)
				{
					*out_row++ = r_mid;
					*out_row++ = g_mid;
					*out_row++ = b_mid;
					if(COMPONENTS == 4) *out_row++ = a_mid;
				}
				else
				{
					*out_row++ = r_high;
					*out_row++ = g_high;
					*out_row++ = b_high;
					if(COMPONENTS == 4) *out_row++ = a_high;
				}
			}
			in_row += COMPONENTS;
		}
	}
}
Ejemplo n.º 12
0
int Overlay::process_buffer(VFrame **frame,
	int64_t start_position,
	double frame_rate)
{
	load_configuration();


printf("Overlay::process_buffer mode=%d\n", config.mode);
	if(!temp) temp = new VFrame(0,
		-1,
		frame[0]->get_w(),
		frame[0]->get_h(),
		frame[0]->get_color_model(),
		-1);

	if(!overlayer)
		overlayer = new OverlayFrame(get_project_smp() + 1);

	int step;
	VFrame *output;

	if(config.direction == OverlayConfig::BOTTOM_FIRST)
	{
		input_layer1 = get_total_buffers() - 1;
		input_layer2 = -1;
		step = -1;
	}
	else
	{
		input_layer1 = 0;
		input_layer2 = get_total_buffers();
		step = 1;
	}

	if(config.output_layer == OverlayConfig::TOP)
	{
		output_layer = 0;
	}
	else
	{
		output_layer = get_total_buffers() - 1;
	}



// Direct copy the first layer
	output = frame[output_layer];
	read_frame(output,
		input_layer1,
		start_position,
		frame_rate,
		get_use_opengl());

	if(get_total_buffers() == 1) return 0;



	current_layer = input_layer1;
	if(get_use_opengl())
		run_opengl();

	for(int i = input_layer1 + step; i != input_layer2; i += step)
	{
		read_frame(temp,
			i,
			start_position,
			frame_rate,
			get_use_opengl());

// Call the opengl handler once for each layer
		if(get_use_opengl())
		{
			current_layer = i;
			run_opengl();
		}
		else
		{
			overlayer->overlay(output,
				temp,
				0,
				0,
				output->get_w(),
				output->get_h(),
				0,
				0,
				output->get_w(),
				output->get_h(),
				1,
				config.mode,
				NEAREST_NEIGHBOR);
		}
	}


	return 0;
}