Česky
Kamil Dudka

GED 2006 (C++)

File detail

Name:Downloaddraw.cc [Download]
Location: ged2006 > src
Size:13.9 KB
Last modification:2007-08-29 02:16

Source code

/*
 * File: draw.cc - Draw commands
 * Project: GED - bitmap editor (ICP)
 * Author: Kamil Dudka, xdudka00
 * Team: xdudka00, xfilak01, xhefka00, xhradi08
 * Created: 2006-04-11
 */
 
#include <iostream>
#include <sstream>
#include <cstring>
 
#include <ft2build.h>
#include FT_FREETYPE_H
 
#include "global.h"
#include "gedconf.h"
#include "draw.h"
 
using GlobalH::swap;
 
namespace {
	const char* toolLabels [iDrawToolCnt] = {
		"",
		"Brush",
		"Rubber",
		"Line",
		"Box",
		"Circle",
		"Ellipse",
		"Text",
		"Select"
	};
 
	// Read Pixel from stream
	std::istream &operator>> (std::istream &s, Pixel &color) {
		int i;
		s >> i; color.red = i;
		s >> i; color.green = i;
		s >> i; color.blue = i;
		s >> i; color.alpha = i;
		return s;
	}
 
	// Write Pixel to stream
	std::ostream &operator<< (std::ostream &s, const Pixel &color) {
		s << " " << static_cast<int> (color.red);
		s << " " << static_cast<int> (color.green);
		s << " " << static_cast<int> (color.blue);
		s << " " << static_cast<int> (color.alpha);
		return s;
	}
 
	// Read Point from stream
	std::istream &operator>> (std::istream &s, Point &pos) {
		s >> pos.x >> pos.y;
		return s;
	}
 
	// Write Point to stream
	std::ostream &operator<< (std::ostream &s, const Point &pos) {
		s << " " << pos.x << " " << pos.y;
		return s;
	}
 
	// Read Rect from stream
	std::istream &operator>> (std::istream &s, Rect &rect) {
		s >> rect.at.x >> rect.at.y >> rect.size.width >> rect.size.height;
		return s;
	}
 
	// Write Rect to stream
	std::ostream &operator<< (std::ostream &s, const Rect &rect) {
		s << " " << rect.at.x << " " << rect.at.y;
		s << " " << rect.size.width << " " << rect.size.height;
		return s;
	}
 
	// Weight awerage of two colors
	inline Pixel WeightAverage (Pixel a, Pixel b, unsigned char ratio) {
		const int aScale= ratio;
		const int bScale= UCHAR_MAX - ratio;
 
		const unsigned long red= a.red*aScale + b.red*bScale;
		const unsigned long green= a.green*aScale + b.green*bScale;
		const unsigned long blue= a.blue*aScale + b.blue*bScale;
		const unsigned long alpha= a.alpha*aScale + b.alpha*bScale;
 
		Pixel tmp= {
			red / UCHAR_MAX,
			green / UCHAR_MAX,
			blue / UCHAR_MAX,
			alpha / UCHAR_MAX
		};
		return tmp;
	}
}
 
const char *const BrushCmd::szCmdName = toolLabels [TOOL_BRUSH];
const char *const LineCmd::szCmdName = toolLabels [TOOL_LINE];
const char *const RectCmd::szCmdName = toolLabels [TOOL_RECT];
const char *const CircleCmd::szCmdName = toolLabels [TOOL_CIRCLE];
const char *const EllipseCmd::szCmdName = toolLabels [TOOL_ELLIPSE];
const char *const TextCmd::szCmdName = toolLabels [TOOL_TEXT];
const char *const SelectCmd::szCmdName = toolLabels [TOOL_SELECT];
const char *const GrayScaleCmd::szCmdName = "Grayscale";
const char *const InvertCmd::szCmdName = "Invert";
 
/*
 * Return tool "et" label
 */
const char *DrawContext::toolLabel (EDrawTool et)
{
	return toolLabels [et];
}
 
/*
 * Return tool "et" id from label "text"
 */
EDrawTool DrawContext::toolByLabel (const char *text)
{
	for (int et= TOOL_NOTOOL; et<iDrawToolCnt; et++)
		if (0==std::strcmp (text, toolLabels [et]))
			return static_cast<EDrawTool>(et);
 
	return TOOL_NOTOOL;
}
 
/*
 * Construct brush command
 */
BrushCmd::BrushCmd (FrameBuffer *pFB, const DrawContext &dc, bool bBack):
	SlowUndoSelectCmd (pFB)
{
	if (bBack) {
		_iBrushWidth = dc.bgBrushWidth;
		_brushColor = dc.bgColor;
	} else {
		_iBrushWidth = dc.fgBrushWidth;
		_brushColor = dc.fgColor;
	}
}
 
/*
 * Brush command export
 */
std::string BrushCmd::args ()
{
	std::ostringstream s;
	s << _brushColor;
 
	for (
			std::set<int>::iterator i = _pixSet.begin ();
			i != _pixSet.end ();
			i ++)
	{
		Point pos (
				(*i) % _fb.size().width,
				(*i) / _fb.size().width);
		s << pos;
	}
 
	return s.str();
}
 
/*
 * Brush command import
 */
BrushCmd::BrushCmd (FrameBuffer *pFB, const std::string &args):
	SlowUndoSelectCmd (pFB), _iBrushWidth (1)
{
	std::istringstream s (args);
	s >> _brushColor;
	while (s) {
		Point pixel;
		s >> pixel;
		add (pixel);
	}
}
 
/*
 * Add pixel into brush command pixmap
 */
void BrushCmd::add (Point pos)
{
	for (int y = pos.y; y < pos.y+_iBrushWidth; y++)
		for (int x = pos.x; x < pos.x+_iBrushWidth; x++) {
			// Index to bitmap array
			int curr = y*_fb.size().width + x;
 
			// Add pixel to map
			_pixSet.insert (curr);
		}
}
 
namespace {
	/*
	 * Pixel draw encapsulation for BrushCmd::exec()
	 */
	class BrushPixel {
		FrameBuffer &_fb;
		Pixel _color;
	public:
		BrushPixel (FrameBuffer &fb, Pixel &color): _fb (fb), _color (color) { }
		void operator() (const int iPixel) const {
			const int iWidth= _fb.size().width;
			const Point pos (
					iPixel % iWidth,
					iPixel / iWidth);
			_fb.setPixel (pos, _color);
		}
	};
}
 
/*
 * Execute brush command
 */
void BrushCmd::exec ()
{
	// Save Undo
	SlowUndoSelectCmd::exec ();
 
	// Draw all pixels
	BrushPixel pen (_fb, _brushColor);
	std::for_each (_pixSet.begin(), _pixSet.end(), pen);
}
 
/*
 * Construct placement command (abstract class)
 */
PlacementCmd::PlacementCmd (FrameBuffer *pFB, Rect &placement, DrawContext &dc, bool bBack):
	SlowUndoSelectCmd (pFB), _place (placement)
{
	if (bBack) {
		_iBrushWidth = dc.bgBrushWidth;
		_brushColor = dc.bgColor;
	} else {
		_iBrushWidth = dc.fgBrushWidth;
		_brushColor = dc.fgColor;
	}
}
 
/*
 * Placement command export (abstract class)
 */
std::string PlacementCmd::args ()
{
	std::ostringstream s;
	s << _iBrushWidth << _brushColor << _place;
 
	return s.str();
}
 
/*
 * Placement command import (abstract class)
 */
PlacementCmd::PlacementCmd (FrameBuffer *pFB, const std::string &args):
	SlowUndoSelectCmd (pFB)
{
	std::istringstream s (args);
	s >> _iBrushWidth >> _brushColor >> _place;
}
 
/*
 * Imperess selected brush at position "pos"
 */
void PlacementCmd::impressBrush (Point pos)
{
	// Brush metrics
	const int min = - (_iBrushWidth >> 1);
	const int max = min + _iBrushWidth;
	const int RR = max * max;
 
	// Position correction
	pos -= Point (min, min);
 
	// impress circle brush
	for (int y=min; y<max; y++)
		for (int x=min; x<max; x++)
			if ( (x*x + y*y) <= RR)
				_fb.setPixel (pos + Point (x, y), _brushColor);
}
 
/*
 * Execute line command
 */
void LineCmd::exec ()
{
	// Save undo
	SlowUndoSelectCmd::exec ();
 
	// Get line metrics
	int x1 = _place.at.x;
	int y1 = _place.at.y;
	int x2 = x1 + _place.size.width;
	int y2 = y1 + _place.size.height;
	int dx = abs (_place.size.width);
	int dy = abs (_place.size.height);
 
	// Swap axis if needed
	bool bInvAxis = dy>dx;
	if (bInvAxis) {
		swap (x1, y1);
		swap (x2, y2);
		swap (dx, dy);
	}
 
	// Swap points acoording to axis x
	if (x1>x2) {
		swap (x1, x2);
		swap (y1, y2);
	}
 
	// Step in axis y
	int iStepY = (y1>y2) ? (-1):1;
 
	// Bresenham algorithm
	const int k1 = 2*dy;
	const int k2 = k1 - 2*dx;
	int P=2*dy - dx;
	int x,y;
	for (x=x1, y=y1; x<=x2; x++) {
		if (P<0)
			P += k1;
		else {
			y += iStepY;
			P += k2;
		}
		if (bInvAxis)
			impressBrush (Point (y, x));
		else
			impressBrush (Point (x, y));
	}
}
 
/*
 * Execute rectanle command
 */
void RectCmd::exec ()
{
	// Save undo
	SlowUndoSelectCmd::exec ();
 
	// Draw rectangle
	const int xMax = _place.at.x + _place.size.width;
	const int yMax = _place.at.y + _place.size.height;
	for (int x=_place.at.x; x< xMax; x++)
		impressBrush (Point (x, _place.at.y));
	for (int y=_place.at.y; y< yMax; y++)
		impressBrush (Point (_place.at.x, y));
	for (int x=_place.at.x; x< xMax; x++)
		impressBrush (Point (x, yMax));
	for (int y=_place.at.y; y< yMax; y++)
		impressBrush (Point (xMax, y));
}
 
/*
 * Execute circle command
 */
void CircleCmd::exec ()
{
	// Save undo
	SlowUndoSelectCmd::exec ();
 
	// Draw circle (8x symetric) using Midpoint Algorithm
	int x= 0;
	int y= _iRadius;
	int d= 1-_iRadius;
 
	drawCirclePoints (x, y);
	while (y > x) {
		x++;
		if (d < 0) {
			d += 2*x+1;
		} else {
			y --;
			d += 2*(x-y+1);
		}
 
		drawCirclePoints (x, y);
	}
}
 
/*
 * Draw 8 points of circle
 */
void CircleCmd::drawCirclePoints (int x, int y)
{
	impressBrush (Point (_iCenterX - y, _iCenterY + x));
	impressBrush (Point (_iCenterX + y, _iCenterY + x));
	impressBrush (Point (_iCenterX - x, _iCenterY + y));
	impressBrush (Point (_iCenterX + x, _iCenterY + y));
	impressBrush (Point (_iCenterX - x, _iCenterY - y));
	impressBrush (Point (_iCenterX + x, _iCenterY - y));
	impressBrush (Point (_iCenterX - y, _iCenterY - x));
	impressBrush (Point (_iCenterX + y, _iCenterY - x));
 
}
 
/*
 * Execute ellipse command
 */
void EllipseCmd::exec ()
{
	// Save undo
	SlowUndoSelectCmd::exec ();
 
	// Midpoint algorithm
	int x= 0;
	int y= _iHalfAxisB;
	const int AA= _iHalfAxisA * _iHalfAxisA;
	const int BB= _iHalfAxisB * _iHalfAxisB;
 
	int P= BB - AA*_iHalfAxisB + AA/4;
	while (AA*y > BB*x) {
		drawEllipsePoints (x, y);
		if (P < 0) {
			P += BB*(2*x+3);
			x++;
		} else {
			P += BB*(2*x+3) + AA*(2-2*y);
			x++;
			y--;
		}
	}
	P = BB*(x*x + x) + BB/4 + AA*(y-1)*(y-1) - AA*BB;
	while (y > 0) {
		drawEllipsePoints (x, y);
		if (P < 0) {
			P += BB*(2*x+2) + AA*(3-2*y);
			x++;
			y--;
		} else {
			P += AA*(3-2*y);
			y--;
		}
	}
}
 
/*
 * Draw 4 points of ellipse
 */
void EllipseCmd::drawEllipsePoints (int x, int y)
{
	impressBrush (Point (_iCenterX - x, _iCenterY + y));
	impressBrush (Point (_iCenterX + x, _iCenterY + y));
	impressBrush (Point (_iCenterX - x, _iCenterY - y));
	impressBrush (Point (_iCenterX + x, _iCenterY - y));
}
 
/*
 * Construct text command
 */
TextCmd::TextCmd (FrameBuffer *pFB, DrawContext dc, Point position, const char *szText):
	SlowUndoSelectCmd (pFB),
	_fgColor (dc.fgColor), _bgColor (dc.bgColor),
	_pos (position), _strText (szText)
{
	// Load font properties from configuration file
	init ();
 
	// Apply brush width
	_iSize *= dc.fgBrushWidth;
}
 
/*
 * Load font properties
 */
void TextCmd::init ()
{
	// Read data from configuration file
	std::string dpi;
	std::string size;
	GedConf::ptr()->getFont (_fontFile, dpi, size);
 
	// Save dpi
	std::istringstream s1 (dpi);
	s1 >> _iDpi;
 
	// Save base font size
	std::istringstream s2 (size);
	s2 >> _iSize;
}
 
/*
 * Text command export
 */
std::string TextCmd::args ()
{
	std::ostringstream s;
	s << _iSize << _fgColor << _bgColor << _pos << ' ' << _strText;
 
	return s.str();
}
 
/*
 * Text command import
 */
TextCmd::TextCmd (FrameBuffer *pFB, const std::string &args):
	SlowUndoSelectCmd (pFB)
{
	// Load font properties from configuration file
	init();
 
	// Read metrics
	std::istringstream s (args);
	s >> _iSize >> _fgColor >> _bgColor >> _pos;
 
	// Read text string
	getline (s, _strText);
	std::string::iterator b= _strText.begin ();
	if (b!= _strText.end() && *b==' ')
		_strText.erase (b);
}
 
/*
 * Execute text command
 */
void TextCmd::exec ()
{
	// Save undo
	SlowUndoSelectCmd::exec ();
 
	// FT_Library encapsulation
	class FreeTypeObject {
		FT_Library _ft;
	public:
		FreeTypeObject () {
			if (0!= FT_Init_FreeType (&_ft))
				throw ErrFreeType ();
		}
 
		~FreeTypeObject () {
			FT_Done_FreeType (_ft);
		}
 
		operator FT_Library&() { return _ft; }
	} lib;
 
	// FT_Face encapsulation
	class FaceObject {
		FT_Face _face;
	public:
		FaceObject (FT_Library &lib, const char *szFontFile) {
			if (0!= FT_New_Face (lib, szFontFile, 0, &_face))
				throw ErrFreeType ();
		}
 
		~FaceObject () {
			FT_Done_Face (_face);
		}
 
		operator FT_Face&() { return _face; }
	} face (lib, _fontFile.c_str ());
 
	// Set font metric
	if (0!= FT_Set_Char_Size (face, _iSize << 6, 0, _iDpi, 0))
		throw ErrFreeType ();
 
	// FT variables
	FT_GlyphSlot slot= FT_Face(face)->glyph;
	FT_Bitmap &bitmap= slot->bitmap;
	FT_Vector pen;
	pen.x = _pos.x << 6;
	pen.y = (_fb.size().height - _pos.y) << 6;
 
	// Draw text string
	for (std::string::iterator i=_strText.begin(); i!= _strText.end(); i++) {
		// Load glyph
		FT_Set_Transform (face, 0, &pen);
		if (0!= FT_Load_Char (face, *i, FT_LOAD_RENDER))
			throw ErrFreeType ();
 
		// Draw glyph
		const Point offset (slot->bitmap_left, _fb.size().height - slot->bitmap_top);
		for (int y=0; y< bitmap.rows; y++)
			for (int x=0; x< bitmap.width; x++) {
				unsigned char value= bitmap.buffer [y*bitmap.width + x];
				if (0== value)
					continue;
 
				// Draw pixel
				Point pos= Point (x, y) + offset;
				Pixel color= WeightAverage (_fgColor, _bgColor, value);
				_fb.setPixel (pos, color);
			}
 
		// Move to next position
		pen.x += slot->advance.x;
		pen.y += slot->advance.y;
	}
}
 
/*
 * Construct select command
 */
SelectCmd::SelectCmd (FrameBuffer *pFB, const Rect &sel):
	Cmd (pFB), _newSel (sel)
{
}
 
/*
 * Construct select-all command
 */
SelectCmd::SelectCmd (FrameBuffer *pFB):
	Cmd (pFB)
{
	_newSel.at = Point (0, 0);
	_newSel.size = _fb.size ();
}
 
/*
 * Select command export
 */
std::string SelectCmd::args ()
{
	std::ostringstream s;
	s << _newSel;
 
	return s.str();
}
 
/*
 * Select command import
 */
SelectCmd::SelectCmd (FrameBuffer *pFB, const std::string &args):
	Cmd (pFB)
{
	std::istringstream s (args);
	s >> _newSel;
}
 
/*
 * Execute select command
 */
void SelectCmd::exec ()
{
	_oldSel = _fb.select();
	_fb.select (_newSel);
}
 
/*
 * Revert select command
 */
void SelectCmd::unExec ()
{
	_fb.select (_oldSel);
}
 
/*
 * Execute filter "Grayscale"
 */
void GrayScaleCmd::exec ()
{
	// Save undo informations
	SlowUndoSelectCmd::exec();
 
	// Selected area range
	const int xMin= _fb.select().at.x;
	const int yMin= _fb.select().at.y;
	const int xMax= xMin + _fb.select().size.width;
	const int yMax= yMin + _fb.select().size.height;
 
	for (int y= yMin; y< yMax; y++)
		for (int x= xMin; x< xMax; x++) {
			Pixel &pix= _fb [y][x];
 
			// Weight average
			const int iIntensity= static_cast<int> (
					0.299* pix.red +
					0.587* pix.green +
					0.114* pix.blue);
 
			pix.red= pix.green= pix.blue= iIntensity;
		}
}
 
/*
 * Execute filter "Invert"
 */
void InvertCmd::exec ()
{
	// Save undo informations
	SlowUndoSelectCmd::exec();
 
	// Selected area range
	const int xMin= _fb.select().at.x;
	const int yMin= _fb.select().at.y;
	const int xMax= xMin + _fb.select().size.width;
	const int yMax= yMin + _fb.select().size.height;
 
	for (int y= yMin; y< yMax; y++)
		for (int x= xMin; x< xMax; x++) {
			Pixel &pix= _fb [y][x];
 
			// Invert all colors
			pix.red= UCHAR_MAX - pix.red;
			pix.green= UCHAR_MAX - pix.green;
			pix.blue= UCHAR_MAX - pix.blue;
		}
}