
/*
 * CINELERRA
 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
 *
 * 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 "filexml.h"
#include "language.h"
#include "blurzoom.h"
#include "blurzoomwindow.h"

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


PluginClient* new_plugin(PluginServer *server)
{
	return new BlurZoomMain(server);
}












BlurZoomConfig::BlurZoomConfig()
{
}

BlurZoomMain::BlurZoomMain(PluginServer *server)
 : PluginVClient(server)
{
}

BlurZoomMain::~BlurZoomMain()
{
}

char* BlurZoomMain::plugin_title() { return N_("RadioacTV"); }
int BlurZoomMain::is_realtime() { return 1; }

NEW_WINDOW_MACRO(BlurZoomMain, BlurZoomWindow)


int BlurZoomMain::load_configuration()
{
	return 1;
}


void BlurZoomMain::save_data(KeyFrame *keyframe)
{
}

void BlurZoomMain::read_data(KeyFrame *keyframe)
{
}


#define VIDEO_HWIDTH (buf_width/2)
#define VIDEO_HHEIGHT (buf_height/2)


#define MAGIC_THRESHOLD 40
#define RATIO 0.95

void BlurZoomMain::image_set_threshold_y(int threshold)
{
	y_threshold = threshold * 7; /* fake-Y value is timed by 7 */
}


void BlurZoomMain::set_table()
{
	int bits, x, y, tx, ty, xx;
	int ptr, prevptr;

	prevptr = (int)(0.5 + RATIO * (-VIDEO_HWIDTH) + VIDEO_HWIDTH);

	for(xx = 0; xx < (buf_width_blocks); xx++)
	{
		bits = 0;
		for(x = 0; x < 32; x++)
		{
			ptr = (int)(0.5 + RATIO * (xx * 32 + x - VIDEO_HWIDTH) + VIDEO_HWIDTH);
			bits = bits << 1;
			if(ptr != prevptr)
				bits |= 1;
			prevptr = ptr;
		}
		blurzoomx[xx] = bits;
	}

	ty = (int)(0.5 + RATIO * (-VIDEO_HHEIGHT) + VIDEO_HHEIGHT);
	tx = (int)(0.5 + RATIO*  (-VIDEO_HWIDTH) + VIDEO_HWIDTH);
	xx = (int)(0.5 + RATIO *( buf_width - 1 - VIDEO_HWIDTH) + VIDEO_HWIDTH);
	blurzoomy[0] = ty * buf_width + tx;
	prevptr = ty * buf_width + xx;

	for(y = 1; y < buf_height; y++)
	{
		ty = (int)(0.5 + RATIO * (y - VIDEO_HHEIGHT) + VIDEO_HHEIGHT);
		blurzoomy[y] = ty * buf_width + tx - prevptr;
		prevptr = ty * buf_width + xx;
	}
}

void BlurZoomMain::make_palette()
{
	int i;

#define DELTA (255 / (COLORS / 2 - 1))

	for(i = 0; i < COLORS / 2; i++)
	{
		palette_r[i] = i * DELTA;
		palette_g[i] = i * DELTA;
		palette_b[i] = i * DELTA;
	}

	for(i = 0; i < COLORS / 2; i++)
	{
		palette_r[i + COLORS / 2] = (i * DELTA);
		palette_g[i + COLORS / 2] = (i * DELTA);
		palette_b[i + COLORS / 2] = 255;
	}
}



















int BlurZoomMain::start_realtime()
{
	buf_width_blocks = project_frame_w / 32;
	buf_width = buf_width_blocks * 32;
	buf_height = project_frame_h;
	buf_area = buf_width * buf_height;
	buf_margin_left = (project_frame_w - buf_width) / 2;
	buf_margin_right = project_frame_w - buf_width - buf_margin_left;
	blurzoombuf = new unsigned char[buf_area * 2];
	blurzoomx = new int[buf_width];
	blurzoomy = new int[buf_height];

	set_table();
	make_palette();

	bzero(blurzoombuf, buf_area * 2);


	background = new uint16_t[project_frame_w * project_frame_h];
	diff = new unsigned char[project_frame_w * project_frame_h];
	image_set_threshold_y(MAGIC_THRESHOLD);

	blurzoom_server = new BlurZoomServer(this, 1, 1);
	return 0;
}

int BlurZoomMain::stop_realtime()
{
	delete blurzoom_server;





	delete [] blurzoombuf;
	delete [] blurzoomx;
	delete [] blurzoomy;





	delete [] background;
	delete [] diff;




	return 0;
}

int BlurZoomMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
{
	load_configuration();
	this->input_ptr = input_ptr;
	this->output_ptr = output_ptr;


	blurzoom_server->process_packages();

	return 0;
}





BlurZoomServer::BlurZoomServer(BlurZoomMain *plugin, int total_clients, total_packages)
 : LoadServer(total_clients, get_total_packages())
{
	this->plugin = plugin;
}


LoadClient* BlurZoomServer::new_client()
{
	return new BlurZoomClient(this);
}




LoadPackage* BlurZoomServer::new_package()
{
	return new BlurZoomPackage;
}



void BlurZoomServer::init_packages()
{
	for(int i = 0; i < get_total_packages(); i++)
	{
		BlurZoomPackage *package = (BlurZoomPackage*)get_package(i);
		package->row1 = plugin->input_ptr->get_h() / get_total_packages() * i;
		package->row2 = package->row1 + plugin->input_ptr->get_h() / get_total_packages();
		if(i >= get_total_packages() - 1)
			package->row2 = plugin->input_ptr->get_h();
	}
}








BlurZoomClient::BlurZoomClient(BlurZoomServer *server)
 : LoadClient(server)
{
	this->plugin = server->plugin;
}









/* Background image is refreshed every frame */
#define IMAGE_BGSUBTRACT_UPDATE_Y(result, \
	input_rows,  \
	type,  \
	components) \
{ \
	int i; \
	int R, G, B; \
	type *p; \
	int16_t *q; \
	unsigned char *r; \
	int v; \
 \
	q = (int16_t *)background; \
	r = diff; \
 \
	for(i = 0; i < project_frame_h; i++)  \
	{ \
		p = input_rows[j]; \
 \
		for(j = 0; j < project_frame_w; j++) \
		{ \
			if(sizeof(type) == 2) \
			{ \
				R = p[0] >> (8 - 1); \
				G = p[1] >> (8 - 2); \
				B = p[2] >> 8; \
			} \
			else \
			{ \
				R = p[0] << 1; \
				G = p[1] << 2; \
				B = p[2]; \
			} \
 \
			v = (R + G + B) - (int)(*q); \
			*q = (int16_t)(R + G + B); \
			*r = ((v + y_threshold) >> 24) | ((y_threshold - v) >> 24); \
 \
			p += components; \
			q++; \
			r++; \
		} \
	} \
 \
	result = diff; \
}




#define BLURZOOM_FINAL(type, components)
{
	for(y = 0; y < h; y++)
	{
		memcpy(output_rows[y],
			input_rows[y],
			buf_margin_left * plugin->input_ptr->get_bytes_per_pixel());


		for(x = 0; x < buf_width; x++)
		{
			for(c = 0; c < components; c++)
			{
				a = *src++ & 0xfefeff;
				b = palette[*p++];
				a += b;
				b = a & 0x1010100;
				*dest++ = a | (b - (b >> 8));
			}
		}

		memcpy(output_rows[y] + project_frame_w - buf_margin_right,
			input_rows[y] + project_frame_w - buf_margin_right,
			buf_margin_right * plugin->input_ptr->get_bytes_per_pixel());
	}
}



void BlurZoomClient::process_package(LoadPackage *package)
{
	BlurZoomPackage *local_package = (BlurZoomPackage*)package;
	unsigned char **input_rows = plugin->input_ptr->get_rows() + local_package->row1;
	unsigned char **output_rows = plugin->output_ptr->get_rows() + local_package->row1;
	int w = plugin->input_ptr->get_w();
	int h = local_package->row2 - local_package->row1;
	int x, y;
	int a, b, c;
	unsigned char *diff, *p;

	switch(plugin->input_ptr->get_color_model())
	{
		case BC_RGB888:
		case BC_YUV888:
			IMAGE_BGSUBTRACT_UPDATE_Y(diff,
				input_rows,
				uint8_t,
				3);
			break;
		case BC_RGBA8888:
		case BC_YUVA8888:
			IMAGE_BGSUBTRACT_UPDATE_Y(diff,
				input_rows,
				uint8_t,
				4);
			break;
		case BC_RGB161616:
		case BC_YUV161616:
			IMAGE_BGSUBTRACT_UPDATE_Y(diff,
				input_rows,
				uint16_t,
				3);
			break;
		case BC_RGBA16161616:
		case BC_YUVA16161616:
			IMAGE_BGSUBTRACT_UPDATE_Y(diff,
				input_rows,
				uint16_t,
				4);
			break;
	}


	diff += buf_margin_left;
	p = blurzoombuf;



	for(y = 0; y < buf_height; y++)
	{
		for(x = 0; x < buf_width; x++)
		{
			p[x] |= diff[x] >> 3;
		}

		diff += w;
		p += buf_width;
	}


// Assembly language only
	blurzoomcore();


	p = blurzoombuf;



	switch(plugin->input_ptr->get_color_model())
	{
		case BC_RGB888:
		case BC_YUV888:
			BLURZOOM_FINAL(uint8_t, 3);
			break;
		case BC_RGBA8888:
		case BC_YUVA8888:
			BLURZOOM_FINAL(uint8_t, 4);
			break;
		case BC_RGB161616:
		case BC_YUV161616:
			BLURZOOM_FINAL(uint16_t, 3);
			break;
		case BC_RGBA16161616:
		case BC_YUVA16161616:
			BLURZOOM_FINAL(uint16_t, 4);
			break;
	}




}



BlurZoomPackage::BlurZoomPackage()
{
}



