Blame synfig-core/src/synfig/palette.cpp

Carlos Lopez a09598
/* === S Y N F I G ========================================================= */
Carlos Lopez a09598
/*!	\file palette.cpp
Carlos Lopez a09598
**	\brief Template File
Carlos Lopez a09598
**
Carlos Lopez a09598
**	$Id$
Carlos Lopez a09598
**
Carlos Lopez a09598
**	\legal
Carlos Lopez a09598
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
Carlos Lopez 84df8e
**	Copyright (c) 2010 Nikita Kitaev
Carlos Lopez 84df8e
**	Copyright (c) 2010 Carlos Lรณpez
Carlos Lopez a09598
**
Carlos Lopez a09598
**	This package is free software; you can redistribute it and/or
Carlos Lopez a09598
**	modify it under the terms of the GNU General Public License as
Carlos Lopez a09598
**	published by the Free Software Foundation; either version 2 of
Carlos Lopez a09598
**	the License, or (at your option) any later version.
Carlos Lopez a09598
**
Carlos Lopez a09598
**	This package is distributed in the hope that it will be useful,
Carlos Lopez a09598
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
Carlos Lopez a09598
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Carlos Lopez a09598
**	General Public License for more details.
Carlos Lopez a09598
**	\endlegal
Carlos Lopez a09598
*/
Carlos Lopez a09598
/* ========================================================================= */
Carlos Lopez a09598
Carlos Lopez a09598
/* === H E A D E R S ======================================================= */
Carlos Lopez a09598
Carlos Lopez a09598
#ifdef USING_PCH
Carlos Lopez a09598
#	include "pch.h"
Carlos Lopez a09598
#else
Carlos Lopez a09598
#ifdef HAVE_CONFIG_H
Carlos Lopez a09598
#	include <config.h></config.h>
Carlos Lopez a09598
#endif
Carlos Lopez a09598
Carlos Lopez a09598
#include "palette.h"
Carlos Lopez a09598
#include "surface.h"
Carlos Lopez a09598
#include "general.h"
bw 94d8a6
#include <synfig localization.h=""></synfig>
Carlos Lopez a09598
#include <fstream></fstream>
Carlos Lopez a09598
#include <iostream></iostream>
Diego Barrios Romero 807989
#include <sstream></sstream>
Carlos Lopez a09598
Carlos Lopez a09598
#endif
Carlos Lopez a09598
Carlos Lopez a09598
/* === U S I N G =========================================================== */
Carlos Lopez a09598
Carlos Lopez a09598
using namespace std;
Carlos Lopez a09598
using namespace etl;
Carlos Lopez a09598
using namespace synfig;
Carlos Lopez a09598
Carlos Lopez a09598
/* === M A C R O S ========================================================= */
Carlos Lopez a09598
Diego Barrios Romero 9fcbd0
#define PALETTE_SYNFIG_FILE_COOKIE	"SYNFIGPAL1.0"
Diego Barrios Romero 9fcbd0
#define PALETTE_SYNFIG_EXT ".spal"
Diego Barrios Romero 9fcbd0
#define PALETTE_GIMP_FILE_COOKIE "GIMP Palette"
Diego Barrios Romero 9fcbd0
#define PALETTE_GIMP_EXT ".gpl"
Diego Barrios Romero 9fcbd0
Carlos Lopez a09598
/* === G L O B A L S ======================================================= */
Carlos Lopez a09598
Carlos Lopez a09598
bool weight_less_than(const PaletteItem& lhs,const PaletteItem& rhs)
Carlos Lopez a09598
{
Carlos Lopez a09598
	return lhs.weight
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
bool luma_less_than(const PaletteItem& lhs,const PaletteItem& rhs)
Carlos Lopez a09598
{
Carlos Lopez a09598
	return lhs.color.get_y()
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
bool luma_less_than(const PaletteItem& lhs,const float& rhs)
Carlos Lopez a09598
{
Carlos Lopez a09598
	return lhs.color.get_y()
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
/* === P R O C E D U R E S ================================================= */
Carlos Lopez a09598
Carlos Lopez a09598
/* === M E T H O D S ======================================================= */
Carlos Lopez a09598
Carlos Lopez a09598
Palette::Palette():
Carlos Lopez a09598
	name_(_("Unnamed"))
Carlos Lopez a09598
{
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Palette::Palette(const String& name_):
Carlos Lopez a09598
	name_(name_)
Carlos Lopez a09598
{
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
void
Carlos Lopez a09598
PaletteItem::add(const Color& x,int xweight)
Carlos Lopez a09598
{
Carlos Lopez a09598
	color=(color*weight+x*xweight)/(weight+xweight);
Carlos Lopez a09598
	weight+=xweight;
Carlos Lopez a09598
}
Carlos Lopez a09598
a4bbdd
Palette::Palette(const Surface& surface, int max_colors, const Gamma &gamma):
Carlos Lopez a09598
	name_(_("Surface Palette"))
Carlos Lopez a09598
{
Carlos Lopez a09598
	max_colors-=2;
Carlos Lopez a09598
	for(int i=0;(signed)size()<(max_colors-1) && i
Carlos Lopez a09598
        int x=rand()%surface.get_w();
Carlos Lopez a09598
        int y=rand()%surface.get_h();
Carlos Lopez a09598
Carlos Lopez a09598
			float dist;
Carlos Lopez a09598
			Color color(surface[y][x]);
Carlos Lopez a09598
Carlos Lopez a09598
			if(empty())
Carlos Lopez a09598
			{
Carlos Lopez a09598
				push_back(color);
Carlos Lopez a09598
				continue;
Carlos Lopez a09598
			}
Carlos Lopez a09598
Carlos Lopez a09598
			if(color.get_a()==0)
Carlos Lopez a09598
			{
Carlos Lopez a09598
				if(front().color.get_a()!=0)
Carlos Lopez a09598
					insert(begin(),Color(1,0,1,0));
Carlos Lopez a09598
				front().weight+=400;
Carlos Lopez a09598
				continue;
Carlos Lopez a09598
			}
Carlos Lopez a09598
a4bbdd
			iterator iter(find_closest(color, gamma, &dist));
Carlos Lopez a09598
			if(sqrt(dist)<0.005)
Carlos Lopez a09598
			{
Carlos Lopez a09598
				iter->add(color);
Carlos Lopez a09598
				continue;
Carlos Lopez a09598
			}
Carlos Lopez a09598
Carlos Lopez a09598
			/*if(size()>=max_colors)
Carlos Lopez a09598
			{
Carlos Lopez a09598
				iterator iterlight(find_light());
Carlos Lopez a09598
				PaletteItem light(*iterlight);
Carlos Lopez a09598
				erase(iterlight);
a4bbdd
				find_closest(light.color, gamma)->add(light.color,light.weight);
Carlos Lopez a09598
			}
Carlos Lopez a09598
			*/
Carlos Lopez a09598
Carlos Lopez a09598
			push_back(color);
Carlos Lopez a09598
			continue;
Carlos Lopez a09598
    }
Carlos Lopez a09598
Carlos Lopez a09598
/*
Carlos Lopez a09598
Carlos Lopez a09598
	max_colors-=2;
Carlos Lopez a09598
	for(int y=0;y
Carlos Lopez a09598
		for(int x=0;x
Carlos Lopez a09598
		{
Carlos Lopez a09598
			float dist;
Carlos Lopez a09598
			Color color(surface[y][x]);
Carlos Lopez a09598
Carlos Lopez a09598
			if(empty())
Carlos Lopez a09598
			{
Carlos Lopez a09598
				push_back(color);
Carlos Lopez a09598
				continue;
Carlos Lopez a09598
			}
Carlos Lopez a09598
Carlos Lopez a09598
			if(color.get_a()==0)
Carlos Lopez a09598
			{
Carlos Lopez a09598
				if(front().color.get_a()!=0)
Carlos Lopez a09598
					insert(begin(),Color(1,0,1,0));
Carlos Lopez a09598
				front().weight+=400;
Carlos Lopez a09598
				continue;
Carlos Lopez a09598
			}
Carlos Lopez a09598
a4bbdd
			iterator iter(find_closest(color, gamma, &dist));
Carlos Lopez a09598
			if(sqrt(dist)<0.005)
Carlos Lopez a09598
			{
Carlos Lopez a09598
				iter->add(color);
Carlos Lopez a09598
				continue;
Carlos Lopez a09598
			}
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
			push_back(color);
Carlos Lopez a09598
			continue;
Carlos Lopez a09598
		}
Carlos Lopez a09598
	sort(rbegin(),rend());
Carlos Lopez a09598
Carlos Lopez a09598
	iterator iter;
Carlos Lopez a09598
Carlos Lopez a09598
	iterator best_match(begin());
Carlos Lopez a09598
	while((signed)size()>max_colors)
Carlos Lopez a09598
	{
Carlos Lopez a09598
		PaletteItem item(back());
Carlos Lopez a09598
		pop_back();
a4bbdd
		find_closest(item.color, gamma)->add(item.color,item.weight);
Carlos Lopez a09598
	}
Carlos Lopez a09598
*/
Carlos Lopez a09598
	push_back(Color::black());
Carlos Lopez a09598
	push_back(Color::white());
Carlos Lopez a09598
Carlos Lopez a09598
//	sort(begin(),end(),&luma_less_than);
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Palette::const_iterator
a4bbdd
Palette::find_closest(const Color& color, const Gamma &gamma, float* dist)const
Carlos Lopez a09598
{
Carlos Lopez a09598
	// For the sake of avoiding cut-and-paste
Carlos Lopez a09598
	// bugs, we'll just use the non-const
Carlos Lopez a09598
	// find_closest()... It doesn't change anything
Carlos Lopez a09598
	// anyway.
a4bbdd
	return const_cast<palette*>(this)->find_closest(color, gamma, dist);</palette*>
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Palette::iterator
a4bbdd
Palette::find_closest(const Color& color, const Gamma &gamma, float* dist)
Carlos Lopez a09598
{
Carlos Lopez a09598
	iterator iter;
Carlos Lopez a09598
Carlos Lopez a09598
	iterator best_match(begin());
Carlos Lopez a09598
	float best_dist(1000000);
Carlos Lopez a09598
a4bbdd
	const Color prep = gamma.apply(color);
a4bbdd
	const float prep_y(prep.get_y()*prep.get_a());
a4bbdd
	const float prep_u(prep.get_u());
a4bbdd
	const float prep_v(prep.get_v());
Carlos Lopez a09598
Carlos Lopez a09598
	for(iter=begin();iter!=end();++iter)
Carlos Lopez a09598
	{
a4bbdd
		const Color ic = gamma.apply(iter->color);
a4bbdd
		const float diff_y(prep_y - ic.get_y()*ic.get_a());
a4bbdd
		const float diff_u(prep_u - ic.get_u());
a4bbdd
		const float diff_v(prep_v - ic.get_v());
a4bbdd
		const float diff_a(prep.get_a() - ic.get_a());
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
		const float dist(
Carlos Lopez a09598
			diff_y*diff_y*1.5f+
Carlos Lopez a09598
			diff_a*diff_a+
Carlos Lopez a09598
Carlos Lopez a09598
			diff_u*diff_u+
Carlos Lopez a09598
			diff_v*diff_v
Carlos Lopez a09598
Carlos Lopez a09598
			// cross product
Carlos Lopez a09598
			/*abs(
a4bbdd
				prep_u*ic.get_u()-
a4bbdd
				prep_v*ic.get_v()
Carlos Lopez a09598
			)*/
Carlos Lopez a09598
		);
Carlos Lopez a09598
		if(dist
Carlos Lopez a09598
		{
Carlos Lopez a09598
			best_dist=dist;
Carlos Lopez a09598
			best_match=iter;
Carlos Lopez a09598
		}
Carlos Lopez a09598
	}
Carlos Lopez a09598
	if(dist)
Carlos Lopez a09598
		*dist=best_dist;
Carlos Lopez a09598
Carlos Lopez a09598
	return best_match;
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
Palette::iterator
Carlos Lopez a09598
Palette::find_heavy()
Carlos Lopez a09598
{
Carlos Lopez a09598
	iterator iter;
Carlos Lopez a09598
Carlos Lopez a09598
	iterator best_match(begin());
Carlos Lopez a09598
Carlos Lopez a09598
	for(iter=begin();iter!=end();++iter)
Carlos Lopez a09598
	{
Carlos Lopez a09598
		if(iter->weight>best_match->weight)
Carlos Lopez a09598
			best_match=iter;
Carlos Lopez a09598
	}
Carlos Lopez a09598
Carlos Lopez a09598
	return best_match;
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Palette::iterator
Carlos Lopez a09598
Palette::find_light()
Carlos Lopez a09598
{
Carlos Lopez a09598
	iterator iter;
Carlos Lopez a09598
Carlos Lopez a09598
	iterator best_match(begin());
Carlos Lopez a09598
Carlos Lopez a09598
	for(iter=begin();iter!=end();++iter)
Carlos Lopez a09598
	{
Carlos Lopez a09598
		if(iter->weight<best_match->weight)</best_match->
Carlos Lopez a09598
			best_match=iter;
Carlos Lopez a09598
	}
Carlos Lopez a09598
Carlos Lopez a09598
	return best_match;
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Palette
a4bbdd
Palette::grayscale(int steps, ColorReal gamma)
Carlos Lopez a09598
{
Carlos Lopez a09598
	Palette ret;
Carlos Lopez a09598
	for(int i=0;i
Carlos Lopez a09598
	{
a4bbdd
		ColorReal amount = i/ColorReal(steps-1);
a4bbdd
		ColorReal y = Gamma::calculate(amount, gamma);
Carlos Lopez a09598
		ret.push_back(
Carlos Lopez a09598
			PaletteItem(
a4bbdd
				Color(y, y, y),
a4bbdd
				strprintf(_("%0.2f%% Gray"), amount) ));
Carlos Lopez a09598
	}
Carlos Lopez a09598
	return ret;
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
void
Carlos Lopez a09598
Palette::save_to_file(const synfig::String& filename)const
Carlos Lopez a09598
{
Carlos Lopez a09598
	const_iterator iter;
Carlos Lopez a09598
Carlos Lopez a09598
	std::ofstream file(filename.c_str());
Carlos Lopez a09598
Carlos Lopez a09598
	if(!file)
Carlos Lopez a09598
		throw strprintf(_("Unable to open %s for write"),filename.c_str());
Carlos Lopez a09598
Diego Barrios Romero 9fcbd0
	file<
Carlos Lopez ac6eb5
	file<
Carlos Lopez a09598
	for(iter=begin();iter!=end();++iter)
Carlos Lopez a09598
	{
Carlos Lopez ac6eb5
		file<<iter->name.c_str()<</iter->
Carlos Lopez a09598
		file
Carlos Lopez a09598
			<<iter->color.get_r()<</iter->
Carlos Lopez a09598
			<<iter->color.get_g()<</iter->
Carlos Lopez a09598
			<<iter->color.get_b()<</iter->
Carlos Lopez a09598
			<<iter->color.get_a()<</iter->
Carlos Lopez a09598
Carlos Lopez a09598
	}
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Palette
Carlos Lopez a09598
Palette::load_from_file(const synfig::String& filename)
Carlos Lopez a09598
{
Carlos Lopez a09598
	std::ifstream file(filename.c_str());
Carlos Lopez a09598
Carlos Lopez a09598
	if(!file)
Carlos Lopez a09598
		throw strprintf(_("Unable to open %s for read"),filename.c_str());
Carlos Lopez a09598
Carlos Lopez a09598
	Palette ret;
Diego Barrios Romero 9fcbd0
	String line("");
Diego Barrios Romero 9fcbd0
	String ext(filename_extension(filename));
Diego Barrios Romero 9fcbd0
Carlos Lopez a09598
Diego Barrios Romero 9fcbd0
	if (ext==PALETTE_SYNFIG_EXT)
Diego Barrios Romero 9fcbd0
	{
Diego Barrios Romero 9fcbd0
		getline(file,line);
Carlos Lopez a09598
Diego Barrios Romero 9fcbd0
		if(line!=PALETTE_SYNFIG_FILE_COOKIE)
Diego Barrios Romero 9fcbd0
			throw strprintf(_("%s does not appear to be a valid %s palette file"),filename.c_str(),"Synfig");
Carlos Lopez a09598
Diego Barrios Romero 9fcbd0
		getline(file,ret.name_);
Carlos Lopez a09598
Diego Barrios Romero 9fcbd0
		while(!file.eof())
Diego Barrios Romero 9fcbd0
		{
Diego Barrios Romero 9fcbd0
			PaletteItem item;
Diego Barrios Romero 9fcbd0
			String n;
Diego Barrios Romero 9fcbd0
			float r, g, b, a;
Diego Barrios Romero 9fcbd0
			getline(file,item.name);
Diego Barrios Romero 9fcbd0
			file >> r >> g >> b >> a;
Diego Barrios Romero 9fcbd0
			item.color.set_r(r);
Diego Barrios Romero 9fcbd0
			item.color.set_g(g);
Diego Barrios Romero 9fcbd0
			item.color.set_b(b);
Diego Barrios Romero 9fcbd0
			item.color.set_a(a);
Diego Barrios Romero 9fcbd0
Diego Barrios Romero 9fcbd0
			// file ends in new line
Diego Barrios Romero 9fcbd0
			if (!file.eof())
Diego Barrios Romero 9fcbd0
				ret.push_back(item);
Diego Barrios Romero 9fcbd0
		}
Diego Barrios Romero 9fcbd0
	}
Diego Barrios Romero 9fcbd0
	else if (ext==PALETTE_GIMP_EXT)
Carlos Lopez a09598
	{
Diego Barrios Romero 9fcbd0
		/*
Diego Barrios Romero 9fcbd0
		file format: GPL (GIMP Palette) file should have the following layout:
Diego Barrios Romero 9fcbd0
		GIMP Palette
Diego Barrios Romero 9fcbd0
		Name: <palette name=""></palette>
Diego Barrios Romero 807989
		[Columns: <number>]</number>
Diego Barrios Romero 807989
		[#]
Diego Barrios Romero 807989
		[# Optional comments]
Diego Barrios Romero 807989
		[#]
Diego Barrios Romero 9fcbd0
		<value r=""> <value g=""> <value b=""> <swatch name=""></swatch></value></value></value>
Diego Barrios Romero 9fcbd0
		<value r=""> <value g=""> <value b=""> <swatch name=""></swatch></value></value></value>
Diego Barrios Romero 9fcbd0
		... ...
Diego Barrios Romero 807989
		[<new line="">]</new>
Diego Barrios Romero 9fcbd0
		*/
Diego Barrios Romero 9fcbd0
Diego Barrios Romero 807989
		do {
Diego Barrios Romero 9fcbd0
			getline(file,line);
Diego Barrios Romero 807989
		} while (!file.eof() && line != PALETTE_GIMP_FILE_COOKIE);
Diego Barrios Romero 9fcbd0
Diego Barrios Romero 807989
		if (line != PALETTE_GIMP_FILE_COOKIE)
Diego Barrios Romero 9fcbd0
			throw strprintf(_("%s does not appear to be a valid %s palette file"),filename.c_str(),"GIMP");
Diego Barrios Romero 9fcbd0
Diego Barrios Romero a4c795
Diego Barrios Romero 807989
		bool has_color = false;
Diego Barrios Romero 807989
Diego Barrios Romero 807989
		do
Diego Barrios Romero a4c795
		{
Diego Barrios Romero 807989
			getline(file, line);
Diego Barrios Romero a4c795
Diego Barrios Romero 807989
			if (!line.empty() && line.substr(0,5) == "Name:")
Diego Barrios Romero 807989
				ret.name_ = String(line.substr(6));
Diego Barrios Romero 807989
			else if (!line.empty() && line.substr(0,8) == "Columns:")
Diego Barrios Romero 807989
				; // Ignore columns
Diego Barrios Romero 807989
			else if (!line.empty() && line.substr(0,1) == "#")
Diego Barrios Romero 807989
				; // Ignore comments
Diego Barrios Romero 807989
			else if (!line.empty())
Diego Barrios Romero 807989
			{
Diego Barrios Romero 807989
				// not empty line not part of the header => color
Diego Barrios Romero 807989
				has_color = true;
Diego Barrios Romero 807989
				// line contains the first color so we put it back in (including \n)
Diego Barrios Romero 807989
				for (int i = line.length()+1; i; i--)
Diego Barrios Romero 807989
					file.unget();
Diego Barrios Romero 807989
			}
Diego Barrios Romero 807989
		} while (!file.eof() && !has_color);
Diego Barrios Romero 9fcbd0
Diego Barrios Romero 807989
		while(!file.eof() && has_color)
Diego Barrios Romero 9fcbd0
		{
Diego Barrios Romero 9fcbd0
			PaletteItem item;
Diego Barrios Romero 9fcbd0
			float r, g, b;
Diego Barrios Romero 9fcbd0
Diego Barrios Romero 807989
			stringstream ss;
Diego Barrios Romero 807989
			getline (file, line);
Diego Barrios Romero 9fcbd0
Diego Barrios Romero 807989
			if (!line.empty())
Diego Barrios Romero 807989
			{
Carlos Lopez ac6eb5
				ss << line.c_str();
Diego Barrios Romero a4c795
Diego Barrios Romero 807989
			 	ss >> r >> g >> b;
Diego Barrios Romero 807989
				getline(ss, item.name);
Diego Barrios Romero 807989
a4bbdd
				item.color.set_r(r/255);
a4bbdd
				item.color.set_g(g/255);
a4bbdd
				item.color.set_b(b/255);
Diego Barrios Romero 807989
				// Alpha is 1 by default
Diego Barrios Romero 807989
				item.color.set_a(1);
Diego Barrios Romero 9fcbd0
Diego Barrios Romero 9fcbd0
				ret.push_back(item);
Diego Barrios Romero 807989
			}
Diego Barrios Romero 9fcbd0
		}
Carlos Lopez a09598
	}
Diego Barrios Romero 9fcbd0
	else
Diego Barrios Romero 9fcbd0
		throw strprintf(_("%s does not appear to be a supported palette file"),filename.c_str());
Carlos Lopez a09598
Carlos Lopez a09598
	return ret;
Carlos Lopez a09598
}