#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