#include <string>
#include <vector>
#include <cstdarg>
#include <cstdio>
#include "Painter.h"
#include "PicoPNG.h"
#include "Graphics.h"
#include "Utils.h"
#include "MemoryMapping.h"
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
{
CUTE_PaintTarget* m_target;
uint32_t m_penColor;
uint32_t m_brushColor;
int m_fontSize;
std::string m_fontFamily;
};
struct PixmapData
{
CUTE_PaintTarget 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));
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
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 = CUTE_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;
}
CUTE_PaintTarget& 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_utf8String, ...)
{
char string[1025];
string[1024] = 0;
va_list vaargs;
va_start(vaargs, a_utf8String);
vsnprintf(string, 1024, a_utf8String, vaargs); //TODO: dynamicly grow string to be as large as needed (see string_format in utils)
va_end(vaargs);
CUTE_DrawText(m_data->m_target, m_data->m_penColor, m_data->m_fontFamily.c_str(), m_data->m_fontSize, a_x, a_y, string);
}
void Painter::textExtents(int& a_width, int& a_height, const char* a_utf8String, ...)
{
char string[1025];
string[1024] = 0;
va_list vaargs;
va_start(vaargs, a_utf8String);
vsnprintf(string, 1024, a_utf8String, vaargs); //TODO: dynamicly grow string to be as large as needed (see string_format in utils)
va_end(vaargs);
CUTE_GetTextExtents(m_data->m_target, m_data->m_fontFamily.c_str(), m_data->m_fontSize, string, a_width, a_height);
}
void Painter::drawRectangle(int a_x, int a_y, int a_width, int a_height, bool a_alphaBlending)
{
if ( a_alphaBlending )
CUTE_DrawRectangleAlpha(m_data->m_target, m_data->m_brushColor, a_x, a_y, a_width, a_height);
else
CUTE_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)
{
CUTE_PaintTarget* 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)
{
CUTE_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, CUTE_Gradient a_gradient, int a_width, int a_height)
{
CUTE_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)
{
CUTE_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 )
CUTE_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
CUTE_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)
{
CUTE_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)
{
CUTE_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