Blob Blame Raw
/*
    ......... 2016 Ivan Mahonin

    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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include <cstring>
#include <cmath>

#include <fstream>

#include "surface.h"


using namespace std;


Surface::Surface(int width, int height):
	buffer(new Color[width*height]),
	width(width),
	height(height),
	color(),
	x(),
	y(),
	blending(true)
{
	memset(buffer, 0, sizeof(Color)*width*height);
}

Surface::~Surface()
	{ delete[] buffer; }

unsigned int Surface::convert_channel(double c) {
	int i = (int)round(c*255.0);
	return i < 0 ? 0 : i > 255 ? 255 : (unsigned char)i;
}

unsigned int Surface::blend_channel(unsigned int a, unsigned int dest, unsigned int src)
{
	return ((dest*(255-a) + a*src) >> 8) & 0xff;
}

Surface::Color Surface::convert_color(double r, double g, double b, double a) {
	return  convert_channel(r)
		 | (convert_channel(g) << 8)
		 | (convert_channel(b) << 16)
		 | (convert_channel(a) << 24);
}

Surface::Color Surface::blend(Color dest, Color src) {
	unsigned int sa = src >> 24;
	if (sa >= 255) return src; else if (sa == 0) return dest;
	unsigned int da = (dest >> 24) + sa;
	if (da > 255) da = 255;
	return  blend_channel(sa, dest & 0xff, src & 0xff)
		 | (blend_channel(sa, (dest >> 8) & 0xff, (src >> 8) & 0xff) << 8)
		 | (blend_channel(sa, (dest >> 16) & 0xff, (src >> 16) & 0xff) << 16)
		 | (da << 24);
}

void Surface::line(Color color, int x0, int y0, int x1, int y1, bool blending) {
	if (x1 < x0) swap(x1, x0);
	if (y1 < y0) swap(y1, y0);
	int pdx = y1 - y0;
	int pdy = x0 - x1;
	int d = 0;
	for(int x = x0, y = y0; x <= x1 && y <= y1;) {
		if (blending) blend_point(x, y, color); else set_point(x, y, color);
		if (abs(d+pdx) < abs(d+pdy))
			{ d += pdx; ++x; } else { d += pdy; ++y; }
	}
}

void Surface::rect(Color color, int x0, int y0, int x1, int y1, bool blending) {
	if (x1 < x0) swap(x1, x0);
	if (y1 < y0) swap(y1, y0);
	for(int x = x0; x < x1; ++x)
		for(int y = y0; y < y1; ++y)
			if (blending) blend_point(x, y, color); else set_point(x, y, color);
}

void Surface::save(const string &filename) const
{
	// create file
	ofstream f(("results/" + filename).c_str(), ofstream::out | ofstream::trunc | ofstream::binary);

	// write header
	unsigned char targa_header[] = {
		0,    // Length of the image ID field (0 - no ID field)
		0,    // Whether a color map is included (0 - no colormap)
		2,    // Compression and color types (2 - uncompressed true-color image)
		0, 0, 0, 0, 0, // Color map specification (not need for us)
		0, 0, // X-origin
		0, 0, // Y-origin
		(unsigned char)(width & 0xff), // Image width
		(unsigned char)(width >> 8),
		(unsigned char)(height & 0xff), // Image height
		(unsigned char)(height >> 8),
		32,   // Bits per pixel
		0     // Image descriptor (keep zero for capability)
	};
	f.write((char*)targa_header, sizeof(targa_header));

	// write data
	if (true) {
		int line_size = 4*width;
		const char *end = (char*)buffer;
		const char *current = end + height*line_size;
		while(current > end) {
			current -= line_size;
			f.write(current, line_size);
		}
	} else {
		f.write((const char*)buffer, 4*width*height);
	}
}