
/*
 * CINELERRA
 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
 * Copyright (C) 2003-2016 Cinelerra CV contributors
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "clip.h"
#include "bccmodels.h"
#include "bchash.h"
#include "filexml.h"
#include "flip.h"
#include "flipwindow.h"
#include "language.h"

#include <stdint.h>
#include <string.h>

REGISTER_PLUGIN(FlipMain)






FlipConfig::FlipConfig()
{
	flip_horizontal = 0;
	flip_vertical = 0;
}

void FlipConfig::copy_from(FlipConfig &that)
{
	flip_horizontal = that.flip_horizontal;
	flip_vertical = that.flip_vertical;
}

int FlipConfig::equivalent(FlipConfig &that)
{
	return flip_horizontal == that.flip_horizontal &&
		flip_vertical == that.flip_vertical;
}

void FlipConfig::interpolate(FlipConfig &prev,
	FlipConfig &next,
	long prev_frame,
	long next_frame,
	long current_frame)
{
	this->flip_horizontal = prev.flip_horizontal;
	this->flip_vertical = prev.flip_vertical;
}










FlipMain::FlipMain(PluginServer *server)
 : PluginVClient(server)
{

}

FlipMain::~FlipMain()
{

}

const char* FlipMain::plugin_title() { return N_("Flip"); }
int FlipMain::is_realtime() { return 1; }


#define SWAP_PIXELS(type, components, in, out) \
{ \
	type temp = in[0]; \
	in[0] = out[0]; \
	out[0] = temp; \
 \
 	temp = in[1]; \
	in[1] = out[1]; \
	out[1] = temp; \
 \
 	temp = in[2]; \
	in[2] = out[2]; \
	out[2] = temp; \
 \
	if(components == 4) \
	{ \
 		temp = in[3]; \
		in[3] = out[3]; \
		out[3] = temp; \
	} \
}

#define FLIP_MACRO(type, components) \
{ \
	type **input_rows, **output_rows; \
	type *input_row, *output_row; \
	input_rows = ((type**)frame->get_rows()); \
	output_rows = ((type**)frame->get_rows()); \
 \
	if(config.flip_vertical) \
	{ \
		for(i = 0, j = h - 1; i < h / 2; i++, j--) \
		{ \
			input_row = input_rows[i]; \
			output_row = output_rows[j]; \
			for(k = 0; k < w; k++) \
			{ \
				SWAP_PIXELS(type, components, output_row, input_row); \
				output_row += components; \
				input_row += components; \
			} \
		} \
	} \
 \
	if(config.flip_horizontal) \
	{ \
		for(i = 0; i < h; i++) \
		{ \
			input_row = input_rows[i]; \
			output_row = output_rows[i] + (w - 1) * components; \
			for(k = 0; k < w / 2; k++) \
			{ \
				SWAP_PIXELS(type, components, output_row, input_row); \
				input_row += components; \
				output_row -= components; \
			} \
		} \
	} \
}

int FlipMain::process_buffer(VFrame *frame,
		int64_t start_position,
		double frame_rate)
{
	int i, j, k;
	int w = frame->get_w();
	int h = frame->get_h();
	int colormodel = frame->get_color_model();

	load_configuration();

	read_frame(frame,
		0,
		get_source_position(),
		get_framerate(),
		get_use_opengl());



	if(get_use_opengl())
	{
		if(config.flip_vertical || config.flip_horizontal)
			return run_opengl();
		else
			return 0;
	}

	switch(colormodel)
	{
		case BC_RGB888:
		case BC_YUV888:
			FLIP_MACRO(unsigned char, 3);
			break;
		case BC_RGB_FLOAT:
			FLIP_MACRO(float, 3);
			break;
		case BC_RGB161616:
		case BC_YUV161616:
			FLIP_MACRO(uint16_t, 3);
			break;
		case BC_RGBA8888:
		case BC_YUVA8888:
			FLIP_MACRO(unsigned char, 4);
			break;
		case BC_RGBA_FLOAT:
			FLIP_MACRO(float, 4);
			break;
		case BC_RGBA16161616:
		case BC_YUVA16161616:
			FLIP_MACRO(uint16_t, 4);
			break;
	}
	return 0;
}


NEW_WINDOW_MACRO(FlipMain, FlipWindow)
LOAD_CONFIGURATION_MACRO(FlipMain, FlipConfig)

void FlipMain::update_gui()
{
	if(thread)
	{
		load_configuration();
		thread->window->lock_window();
		((FlipWindow*)thread->window)->flip_vertical->update((int)config.flip_vertical);
		((FlipWindow*)thread->window)->flip_horizontal->update((int)config.flip_horizontal);
		thread->window->unlock_window();
	}
}

void FlipMain::save_data(KeyFrame *keyframe)
{
	FileXML output;

// cause data to be stored directly in text
	output.set_shared_output(keyframe->xbuf);
	output.tag.set_title("FLIP");
	output.append_tag();
	if(config.flip_vertical)
	{
		output.tag.set_title("VERTICAL");
		output.append_tag();
		output.tag.set_title("/VERTICAL");
		output.append_tag();
	}

	if(config.flip_horizontal)
	{
		output.tag.set_title("HORIZONTAL");
		output.append_tag();
		output.tag.set_title("/HORIZONTAL");
		output.append_tag();
	}
	output.tag.set_title("/FLIP");
	output.append_tag();
	output.append_newline();
	output.terminate_string();
// data is now in *text
}

void FlipMain::read_data(KeyFrame *keyframe)
{
	FileXML input;

	input.set_shared_input(keyframe->xbuf);

	int result = 0;
	config.flip_vertical = config.flip_horizontal = 0;

	while(!result)
	{
		result = input.read_tag();

		if(!result)
		{
			if(input.tag.title_is("VERTICAL"))
			{
				config.flip_vertical = 1;
			}
			else
			if(input.tag.title_is("HORIZONTAL"))
			{
				config.flip_horizontal = 1;
			}
		}
	}
}


int FlipMain::handle_opengl()
{
#ifdef HAVE_GL
	get_output()->to_texture();
	get_output()->enable_opengl();
	get_output()->init_screen();
	get_output()->bind_texture(0);

	if(config.flip_vertical && !config.flip_horizontal)
	{
		get_output()->draw_texture(0,
			0,
			get_output()->get_w(),
			get_output()->get_h(),
			0,
			get_output()->get_h(),
			get_output()->get_w(),
			0);
	}

	if(config.flip_horizontal && !config.flip_vertical)
	{
		get_output()->draw_texture(0,
			0,
			get_output()->get_w(),
			get_output()->get_h(),
			get_output()->get_w(),
			0,
			0,
			get_output()->get_h());
	}

	if(config.flip_vertical && config.flip_horizontal)
	{
		get_output()->draw_texture(0,
			0,
			get_output()->get_w(),
			get_output()->get_h(),
			get_output()->get_w(),
			get_output()->get_h(),
			0,
			0);
	}

	get_output()->set_opengl_state(VFrame::SCREEN);
#endif
	return 0;
}



