Newer
Older
Import / research / ui / toolkit / src / backup / Painter.cpp
#include <string>
#include <vector>
#include <cstdarg>
#include <cstdio>
#include "Painter.h"
#include "Graphics.h"
#include "Common.h"
#include "MemoryMapping.h"


extern int DecodePNG(std::vector<unsigned char>& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32);


BEGIN_NAMESPACE


const char* Font::Helvetica = "Helvetica";
const char* Font::Times = "Times";
const char* Font::Courier = "Courier";
const char* Font::OldEnglish = "OldEnglish";
const char* Font::Monospace = "monospace";
const char* Font::Fantasy = "fantasy";
const char* Font::Cursive = "cursive";
const char* Font::UIFont = "Segeo UI";


struct PainterData
{
	PixelBuffer*  m_target;
	uint32_t      m_penColor;
	uint32_t      m_brushColor;
	int           m_fontSize;
	std::string   m_fontFamily;
};


struct PixmapData
{
	PixelBuffer          m_target;
	std::vector<uint8_t> m_bits;
};


Pixmap::Pixmap(unsigned char* a_memoryBuffer, int a_width, int a_height, int a_depth)
{
	m_data = new PixmapData;
}


Pixmap::Pixmap(const char* a_fileName)
{
	//static std::map<const char*,PixmapData> pixmapCache;
	m_data = new PixmapData;
	MemoryMappingData* mapping = MemoryMapping_Open(a_fileName);
	// For 32bit builds, this code won't support input PNG files that are more than 4GiB, but neither can it support
	// having a buffer as large as that for storing the pixels in to, so perhaps best just to use 64bit builds if dealing with massive images.
	DecodePNG(m_data->m_bits, (long unsigned&)m_data->m_target.m_width, (long unsigned&)m_data->m_target.m_height, (uint8_t*)MemoryMapping_GetAddress(mapping), (size_t)MemoryMapping_GetSize(mapping), true);
	MemoryMapping_Close(mapping);

	/*
	// need to swap red and blue channels for some reason
	for (int i = 0; i < m_data->m_target.m_width * m_data->m_target.m_height; i++)
	{
		uint8_t red = m_data->m_bits[i*4+1];
		m_data->m_bits[i*4+1] = m_data->m_bits[i*4+3];
		m_data->m_bits[i*4+3] = red;
	}
	*/

	//TODO: for OpenGL, making the width/height a power of two would be better
  //      need to check that. Possibly the power of two requirement might only
  //      be needed if texture wrapping is needed and its okay if not wrapping.
  //      Probably should carefully check the specs.
	m_data->m_target.m_pixels = (uint32_t*)&m_data->m_bits[0];
	m_data->m_target.m_strideBytes = m_data->m_target.m_width * sizeof(uint32_t);
	m_data->m_target.m_format = PF_ARGB8888;
}


Pixmap::~Pixmap()
{
	delete m_data;
}


const uint32_t* Pixmap::bits() const
{
	return m_data->m_target.m_pixels;
}


uint32_t Pixmap::width() const
{
	return m_data->m_target.m_width;
}


uint32_t Pixmap::height() const
{
	return m_data->m_target.m_height;
}


PixelBuffer& Pixmap::targetBuffer()
{
	return m_data->m_target;
}



PainterData* newPainterData()
{
	PainterData* data = new PainterData;
	data->m_target = 0;
	data->m_penColor = 0xffffffff;
	data->m_brushColor = 0xff000000;
	data->m_fontSize = 12;
	data->m_fontFamily = "Arial";
	return data;
}


Painter::Painter()
{
	m_data = newPainterData();
}


Painter::Painter(PaintTarget* a_window)
{
	m_data = newPainterData();
	m_data->m_target = &a_window->targetBuffer();
}


Painter::~Painter()
{
	delete m_data;
}


void Painter::setPen(uint32_t a_color)
{
	m_data->m_penColor = a_color;
}


void Painter::setBrush(uint32_t a_color)
{
	m_data->m_brushColor = a_color;
}


void Painter::setFontFamily(const char* a_fontName)
{
	m_data->m_fontFamily = a_fontName;
}


void Painter::setFontSize(uint32_t a_size)
{
	m_data->m_fontSize = a_size;
}


void Painter::drawText(int a_x, int a_y, const char* a_formattedUtf8String)
{
	DrawText(m_data->m_target, m_data->m_penColor, m_data->m_fontFamily.c_str(), m_data->m_fontSize, a_x, a_y, a_formattedUtf8String);
}


Size Painter::textExtents(const char* a_formattedUtf8String)
{
  Size s;
	GetTextExtents(m_data->m_target, m_data->m_fontFamily.c_str(), m_data->m_fontSize, a_formattedUtf8String, s.m_width, s.m_height);
  return s;
}


void Painter::drawRectangle(int a_x, int a_y, int a_width, int a_height, bool a_alphaBlending)
{
	if ( a_alphaBlending )
		DrawRectangleAlpha(m_data->m_target, m_data->m_brushColor, a_x, a_y, a_width, a_height);
	else
		DrawRectangle(m_data->m_target, m_data->m_brushColor, a_x, a_y, a_width, a_height);
}


void Painter::drawFocusRectangle(int a_x, int a_y, int a_width, int a_height)
{
	PixelBuffer* a_target = m_data->m_target;
	uint32_t a_color = m_data->m_penColor;

	// This code draws a dotted line that alternates between on and off 
	// following around the edge of the given rectangle. It carefully
	// handles the lines as they go around corners to keep the on-off pattern.
	for (int i = 0; i < a_width; i++)
	{
		int j = 0;
		int x = i + a_x;
		int y = j + a_y;
		if ( x >= 0 && x < a_target->m_width && y >= 0 && y < a_target->m_height ) {
			uint32_t *dst = &(a_target->m_pixels[y*a_target->m_strideBytes/4 + x]);
			*dst = a_color;
		}
		i++;
		if (i == a_width)
			break;
		j = a_height - 1;
		x = i + a_x - (a_height & 1);
		y = j + a_y;
		if ( x >= 0 && x < a_target->m_width && y >= 0 && y < a_target->m_height ) {
			uint32_t *dst = &(a_target->m_pixels[y*a_target->m_strideBytes/4 + x]);
			*dst = a_color;
		}
	}
	for (int j = 2; j < a_height; j+=2)  // TODO: handle cases where w or h are < 3
	{
		int i = 0;
		int x = i + a_x;
		int y = j + a_y;
		if ( x >= 0 && x < a_target->m_width && y >= 0 && y < a_target->m_height ) {
			uint32_t *dst = &(a_target->m_pixels[y*a_target->m_strideBytes/4 + x]);
			*dst = a_color;
		}
		i = a_width - 1;
		x = i + a_x;
		y = j + a_y - 1 + (a_width & 1);
		if ( x >= 0 && x < a_target->m_width && y >= 0 && y < a_target->m_height ) {
			uint32_t *dst = &(a_target->m_pixels[y*a_target->m_strideBytes/4 + x]);
			*dst = a_color;
		}
	}
}


void Painter::drawEllipse(int a_x, int a_y, int a_width, int a_height)
{
	DrawEllipse(m_data->m_target, m_data->m_brushColor, a_x, a_y, a_width, a_height, true);
}


void Painter::drawGradient(int a_x, int a_y, Gradient a_gradient, int a_width, int a_height)
{
	DrawGradient(m_data->m_target, a_gradient, a_x, a_y, a_width, a_height);
}


void Painter::drawLine(int a_x, int a_y, int a_x2, int a_y2)
{
	DrawLine(m_data->m_target, m_data->m_penColor, a_x, a_y, a_x2, a_y2);
}


void Painter::drawPixmap(int a_x, int a_y, const Pixmap& pixmap, int a_x1, int a_y1, int a_x2, int a_y2, bool a_alphaMask)
{
	if (a_x2 == -1)
		a_x2 = a_x1 + pixmap.width() - 1;

	if (a_y2 == -1)
		a_y2 = a_y1 + pixmap.height() - 1;

	if ( a_alphaMask )
		DrawPixels(m_data->m_target, (uint32_t*)pixmap.bits(), a_x, a_y, a_x2-a_x1+1, a_y2-a_y1+1, a_x1, a_y1, pixmap.width(), pixmap.height());
	else
		DrawPixelsAlpha(m_data->m_target, (uint32_t*)pixmap.bits(), a_x, a_y, a_x2-a_x1+1, a_y2-a_y1+1, a_x1, a_y1, pixmap.width(), pixmap.height());
}


void Painter::drawPixmapAlphaBlended(int a_x, int a_y, const Pixmap& pixmap, int a_x1, int a_y1, int a_x2, int a_y2, int a_alpha)
{
	DrawPixelsAlphaBlended(m_data->m_target, (uint32_t*)pixmap.bits(), a_x1, a_y1, a_x2, a_y2, a_x, a_y, pixmap.width(), pixmap.height(), a_alpha);
}


void Painter::drawPixelBuffer(int a_x, int a_y, const uint8_t* a_pixels, int a_pixelsWidth, int a_pixelsHeight, int a_pixelsDepth)
{
	DrawPixelsAlpha(m_data->m_target, (uint32_t*)a_pixels, a_x, a_y, a_pixelsWidth, a_pixelsHeight, 0, 0, a_pixelsWidth, a_pixelsHeight);
}


END_NAMESPACE