// C RunTime Header Files
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <stdint.h>
#include <time.h>
// C++ Headers
#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <map>
#ifdef _WIN32
// Windows Headers
#define Rectangle WinRectangle
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#undef Rectangle
#include <mmsystem.h>
#endif
// Project Headers
#include "PicoPNG.h"
#include "Graphics.h"
#include "Utils.h"
#include "MemoryMapping.h"
#include "Window.h"
BEGIN_NAMESPACE
// void CUTE_LogDebugMessage(const char* a_formatString, ...);
void CUTE_LogMessage(/*int a_winId, */ CUTE_LogLevel a_level, const char* a_utf8TextFmt, ...);
// Using CUTE_Debug can be used for quickly adding debug output, and in release builds the calls to it shouldn't
// produce any code (if NDEBUG is defined which the ANSI C standard defines for disabling assert in release builds)
#ifdef NDEBUG
#define CUTE_Debug(a_utf8TextFmt, ...) ((void)0)
#else
#define CUTE_Debug(a_utf8TextFmt, ...) CUTE_LogMessage(CUTE_DEBUG, a_utf8TextFmt, __VA_ARGS__)
#endif
static uint32_t blendColors(uint32_t a_color1, uint32_t a_color2, uint8_t a_alpha)
{
unsigned alpha2 = 255 - a_alpha;
unsigned red = ((a_color1 >> 16) & 0xff) * a_alpha + ((a_color2 >> 16) & 0xff) * alpha2;
unsigned green = ((a_color1 >> 8) & 0xff) * a_alpha + ((a_color2 >> 8) & 0xff) * alpha2;
unsigned blue = ((a_color1 >> 0) & 0xff) * a_alpha + ((a_color2 >> 0) & 0xff) * alpha2;
return 0xff000000 | ((red & 0xff00) << 8) | (green & 0xff00) | (blue >> 8);
}
static uint32_t blendColorsF(uint32_t a_color1, uint32_t a_color2, float a_alpha)
{
float alpha2 = 1.0f - a_alpha;
float red = float((a_color1 >> 16) & 0xff) * a_alpha + float((a_color2 >> 16) & 0xff) * alpha2;
float green = float((a_color1 >> 8) & 0xff) * a_alpha + float((a_color2 >> 8) & 0xff) * alpha2;
float blue = float((a_color1 >> 0) & 0xff) * a_alpha + float((a_color2 >> 0) & 0xff) * alpha2;
return 0xff000000 | ((int(red) & 0xff) << 16) | ((int(green) & 0xff) << 8) | (int(blue) & 0xff);
}
/*
template <typename functor>
void CUTE_ForEachPixel(CUTE_PaintTarget* a_target, uint32_t a_color, int a_x, int a_y, int a_width, int a_height)
{
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
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]);
functor.func();
}
}
}
*/
void CUTE_DrawRectangleAlpha(CUTE_PaintTarget* a_target, uint32_t a_color, int a_x, int a_y, int a_width, int a_height)
{
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
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 = blendColors(*dst, a_color, a_color >> 24);
}
}
}
void CUTE_DrawRectangle(CUTE_PaintTarget* a_target, uint32_t a_color, int a_x, int a_y, int a_width, int a_height)
{
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
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;
}
}
}
/*
a_x = (a_x < 0) ? 0 : a_x;
a_y = (a_y < 0) ? 0 : a_y;
int lastX = ((a_width + a_x) > a_target->m_width) ? a_target->m_width : a_width + a_x;
int lastY = ((a_height + a_y) > a_target->m_height) ? a_target->m_height : a_height + a_y;
for (int j = a_y; j < lastY; j++)
{
uint32_t *dst = &(a_target->m_pixels[j*a_target->m_strideBytes/4]);
for (int i = a_x; i < lastX; i++)
{
dst[i] = a_color;
}
}
}
*/
void CUTE_DrawEllipse(CUTE_PaintTarget* a_target, uint32_t a_color, int a_x, int a_y, int a_width, int a_height, bool a_smoothEdge)
{
int alpha = (a_color >> 24) & 0xff;
if ( alpha == 0 )
return;
int centerX = a_width/2;
int centerY = a_height/2;
int scale = 64; // This is to stop overflows with 32bit ints
if ( a_width < 256 && a_height < 256 )
scale = 1;
if ( alpha == 255 )
{
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
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]);
int a = (i - centerX) * a_height / scale;
int b = (j - centerY) * a_width / scale;
int c1 = a_width * a_height / (2 * scale);
int c2 = (a_width-1) * (a_height-1) / (2 * scale);
//int c2 = ((a_width+1) * (a_height+1)) / (2 * scale);
int innerR = c1*c1;
int outerR = c2*c2;
int dist = a*a + b*b;
int diffR = outerR - innerR;
int distR = dist - innerR;
int alpha = ((distR * 255) / diffR);
if ( dist < outerR )
*dst = a_color;
else if ( dist < innerR )
*dst = blendColors(a_color, *dst, alpha);
//a_antiAlias
}
}
} else {
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
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]);
int a = (i - centerX) * a_height / scale;
int b = (j - centerY) * a_width / scale;
int c = a_width * a_height / (2 * scale);
if ( (a*a + b*b) < c*c )
*dst = (alpha<<24) | (blendColors(a_color, *dst, alpha) & 0xffffff);
}
}
}
}
void CUTE_DrawGradient(CUTE_PaintTarget* a_target, CUTE_Gradient a_gradient, int a_x, int a_y, int a_width, int a_height)
{
for ( int j = 0; j < a_height; j++ )
for ( int i = 0; i < a_width; i++ )
{
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 )
{
float dist = 0.0;
if (a_gradient.m_type == CUTE_RADIAL_GRADIENT)
{
int centerX = a_gradient.m_data.m_radial.m_centerX;
int centerY = a_gradient.m_data.m_radial.m_centerY;
//float errLUT[16] = { 1.1, -1.1, 2.2, -2.2, 3.3, -3.3, 4.4, -4.4, 5.5, -5.5, 6.6, -6.6, 7.7, -7.7, 8.8, -8.8 };
//int dist = (int)(errLUT[rand() % 4] + sqrt(float((centerX - i)*(centerX - i) + (centerY - j)*(centerY - j))) * 256 / a_gradient.m_data.m_radial.m_distance);
dist = sqrt(float((centerX - i)*(centerX - i) + (centerY - j)*(centerY - j))) / a_gradient.m_data.m_radial.m_distance;
/*
float distf = sqrt(float((centerX - i)*(centerX - i) + (centerY - j)*(centerY - j))) * 256 / a_gradient.m_data.m_radial.m_distance;
float frac = distf - floor(distf) - 0.51;
float frac2 = frac + 0.7;
float frac3 = float(j*8) / a_height + float(i-90) / a_width;
if ( frac < 0.1 && frac > -0.1 )
frac = ( frac < 0.0 ) ? -0.081 : 0.08;
int dist = (int)(float(frac * 64) - float(frac2 * frac3) + distf);
//dist += ;
*/
//dist = (dist > 255) ? 255 : ((dist < 0) ? 0 : dist);
}
else if (a_gradient.m_type == CUTE_LINEAR_GRADIENT)
{
/*
m = dy / dx
y = mx + c;
c = y - mx;
m = (y - c) / x;
*/
// TODO:
int y1 = a_gradient.m_data.m_linear.m_y1;
int y2 = a_gradient.m_data.m_linear.m_y2;
if ( y <= y1 )
dist = 0;
else if ( y >= y2 )
dist = 1.0;
else {
dist = float(y2 - y) / (y2 - y1);
}
}
uint32_t *dst = &(a_target->m_pixels[y*a_target->m_strideBytes/4 + x]);
dist = (dist > 1.0f) ? 1.0f : ((dist < 0.0f) ? 0.0f : dist);
*dst = blendColorsF(a_gradient.m_color1, a_gradient.m_color2, dist);
}
}
}
void CUTE_DrawLine(CUTE_PaintTarget* a_target, uint32_t a_color, int a_x1, int a_y1, int a_x2, int a_y2)
{
float x = 0.0f;
float y = 0.0f;
float fdy = 1.0f;
float fdx = 1.0f;
int dx = a_x2 - a_x1;
int dy = a_y2 - a_y1;
int count = 1;
if ( !dy && !dx ) {
if ( a_x1 >= 0 && a_x1 < a_target->m_width && a_y1 >= 0 && a_y1 < a_target->m_height )
a_target->m_pixels[a_y1*a_target->m_strideBytes/4 + a_x1] = a_color;
return;
}
if ( dx*dx > dy*dy ) {
count = dx;
fdy = float(dy) / dx;
} else {
count = dy;
fdx = float(dx) / dy;
}
if ( count < 0 ) {
count = -count;
fdy = -fdy;
fdx = -fdx;
}
//count++;
for (; count--; x+=fdx, y+=fdy)
{
int xi = int(a_x1 + x);
int yi = int(a_y1 + y);
if ( xi >= 0 && xi < a_target->m_width && yi >= 0 && yi < a_target->m_height )
a_target->m_pixels[yi*a_target->m_strideBytes/4 + xi] = a_color;
}
}
void CUTE_DrawPixels(CUTE_PaintTarget* a_target, uint32_t* a_bits, int a_x, int a_y, int a_width, int a_height, int a_xOffset, int a_yOffset, int a_pixelsWidth, int a_pixelsHeight)
{
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
int x = i + a_x;
int y = j + a_y;
if ( y > 0 && y < a_target->m_height && x > 0 && x < a_target->m_width )
{
uint32_t *dst = &(a_target->m_pixels[(j+a_y)*a_target->m_strideBytes/4 + (i+a_x)]);
uint32_t src = a_bits[(j+a_yOffset)*a_pixelsWidth+(i+a_xOffset)];
if ( src & 0xff000000 )
*dst = src;
}
}
}
void CUTE_DrawPixelsAlpha(CUTE_PaintTarget* a_target, uint32_t* a_bits, int a_x, int a_y, int a_width, int a_height, int a_xOffset, int a_yOffset, int a_pixelsWidth, int a_pixelsHeight)
{
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
int x = i + a_x;
int y = j + a_y;
if ( y > 0 && y < a_target->m_height && x > 0 && x < a_target->m_width )
{
uint32_t *dst = &(a_target->m_pixels[(j+a_y)*a_target->m_strideBytes/4 + (i+a_x)]);
uint32_t src = a_bits[(j+a_yOffset)*a_pixelsWidth+(i+a_xOffset)];
if ( src & 0xff000000 )
*dst = blendColors(src, *dst, (src>>24) & 0xff);
}
}
}
void CUTE_DrawPixelsAlphaBlended(CUTE_PaintTarget* a_target, uint32_t* a_bits, int a_x, int a_y, int a_width, int a_height, int a_xOffset, int a_yOffset, int a_pixelsWidth, int a_pixelsHeight, int a_alpha)
{
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
int x = i + a_x;
int y = j + a_y;
if ( y > 0 && y < a_target->m_height && x > 0 && x < a_target->m_width )
{
uint32_t *dst = &(a_target->m_pixels[(j+a_y)*a_target->m_strideBytes/4 + (i+a_x)]);
uint32_t src = a_bits[(j+a_yOffset)*a_pixelsWidth+(i+a_xOffset)];
if ( src & 0xff000000 )
*dst = blendColors(src, *dst, a_alpha);
}
}
}
static bool TestFileExtension(const char* a_file, const char* a_ext)
{
size_t len = strlen(a_file);
if (len < 4 || strlen(a_ext) != 4)
return false;
for (int i = 0; i < 4; i++)
if (toupper(a_file[len-4+i]) != toupper(a_ext[i]))
return false;
return true;
}
static void LoadImage(const char* file, int a_w, int a_h, uint32_t*& a_pixels, bool chromaKeyed = true)
{
if (TestFileExtension(file, ".png"))
{
// It's a PNG image
MemoryMappingData* mapping = MemoryMapping_Open(file);
// 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.
size_t siz = (size_t)MemoryMapping_GetSize(mapping);
uint8_t* buf = (uint8_t*)MemoryMapping_GetAddress(mapping);
std::vector<unsigned char> out_image;
unsigned long image_width, image_height;
DecodePNG(out_image, image_width, image_height, buf, siz);
a_pixels = new uint32_t[a_w * a_h];
memcpy(a_pixels, out_image.data(), a_w * a_h * 4);
for ( int i = 0; i < a_w*a_h; i++ )
a_pixels[i] = ((a_pixels[i] << 16) & 0xff0000) | ((a_pixels[i] >> 16) & 0xff) | (a_pixels[i] & 0xff00ff00);
}
#ifdef _WIN32
else
{
HBITMAP hBMP = (HBITMAP)LoadImageA( NULL, file, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
BITMAPINFO info = { { sizeof(BITMAPINFOHEADER), a_w, -a_h, 1, 32, BI_RGB, 0, 0 } };
info.bmiHeader.biClrImportant = 0;
HBITMAP hbitmap = CreateDIBSection(0, &info, DIB_RGB_COLORS, (void**)&a_pixels, 0, 0);
HDC hdc = CreateCompatibleDC(0);
HDC handle = CreateCompatibleDC(hdc);
SelectObject(hdc, hbitmap);
SelectObject(handle, hBMP);
BitBlt(hdc, 0, 0, a_w, a_h, handle, 0, 0, SRCCOPY);
DeleteDC(handle);
DeleteDC(hdc);
}
#endif
if ( chromaKeyed )
for ( int i = 0; i < a_w*a_h; i++ )
if ( a_pixels[i] != 0x00ff00 )
a_pixels[i] |= 0xff000000;
}
class Image {
public:
Image() : pixels(0) {}
uint32_t *pixels;
unsigned long w, h;
};
void CUTE_DrawPixmapFile(CUTE_PaintTarget* a_target, const char* file, int x, int y, int x1, int y1, int x2, int y2)
{
static std::map<const char*,Image> imageMap; // pixmap cache
Image img = imageMap[file];
if (!img.pixels) {
LoadImage(file, img.w, img.h, img.pixels, false);
//LoadPNGImage(file, img.w, img.h, img.pixels);
imageMap[file] = img;
}
CUTE_DrawPixels(a_target, img.pixels, x, y, x2-x1+1, y2-y1+1, x1, y1, img.w, img.h);
}
/*
void CUTE_DrawNumber(CUTE_PaintTarget* a_target, uint32_t col, int x, int y, int num, int width)
{
// LoadImage("numbers.png", g_numbersPixelsW, g_numbersPixelsH, g_numbersPixels, false);
int divisor = 1;
for (int i = width; i != 0; i--, divisor*=10)
CUTE_DrawPixels(a_target, g_numbersPixels, x + (i-1)*16, y, 16, 30, 0, 30*((num/divisor)%10), g_numbersPixelsW, g_numbersPixelsH);
}
*/
/*
Windows specific
*/
#ifndef CLEARTYPE_QUALITY
# define CLEARTYPE_QUALITY 5
#endif
void CUTE_LogMessage(CUTE_LogLevel a_level, const char* a_formatString, ...)
{
char buf[1024];
va_list vaargs;
va_start(vaargs, a_formatString);
vsnprintf(buf, 1024, a_formatString, vaargs);
buf[1023] = 0; // Ensure null terminated
#ifdef _WIN32
OutputDebugStringA(buf);
#endif
puts(buf);
va_end(vaargs);
}
#ifdef _WIN32
void CUTE_GetTextExtents(CUTE_PaintTarget* a_target, const char* a_fontFamily, int a_size, const char* a_utf8String, int& a_width, int& a_height)
{
HDC hdc = CreateCompatibleDC(0);
HFONT hFont = CreateFontA(a_size, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, CLEARTYPE_QUALITY, 0, a_fontFamily);
SelectObject(hdc, hFont);
SIZE siz;
// std::wstring wstr = utf8_to_wstring(a_utf8String);
std::wstring wstr = String(a_utf8String).toWString();
if (GetTextExtentPoint32W(hdc, wstr.c_str(), (int)wstr.size(), &siz))
{
a_width = siz.cx;
a_height = siz.cy;
}
else
{
a_width = (int)strlen(a_utf8String) * (a_size / 2);
a_height = a_size;
}
DeleteDC(hdc);
DeleteObject(hFont);
}
void CUTE_DrawText(CUTE_PaintTarget* a_target, uint32_t a_color, const char* a_fontFamily, int a_size, int a_x, int a_y, const char* a_utf8String, ...)
{
const int fontSize = a_size;
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);
//printf("text: %s\n", string);
// TODO: Loop over the string and break on line-breaks and call some internal function which does the body of this function
int stringLen = (int)strlen(string);
int bufferWidth = stringLen * fontSize * 2;
int bufferHeight = fontSize * 2;
/*
SystemParametersInfo(SPI_SETFONTSMOOTHING,
TRUE,
0,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
SystemParametersInfo(SPI_SETFONTSMOOTHINGTYPE,
0,
(PVOID)FE_FONTSMOOTHINGCLEARTYPE,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
*/
HBITMAP hbitmap;
unsigned int* a_bits = 0;
BITMAPINFO info = { { sizeof(BITMAPINFOHEADER), bufferWidth, -bufferHeight, 1, WORD(32), BI_RGB, 0 } };
hbitmap = CreateDIBSection(0, &info, DIB_RGB_COLORS, (void**)&(a_bits), 0, 0);
int a_width = bufferWidth;
int a_height = bufferHeight;
int a_pixelsWidth = bufferWidth;
//int a_pixelsHeight = bufferHeight;
// memset(a_bits, 0, bufferWidth*bufferHeight*4);
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
if ( ((j+a_y) > 0) && ((j+a_y) < a_target->m_height) && ((i+a_x) > 0) && ((i+a_x) < a_target->m_width) )
{
a_bits[j*a_pixelsWidth+i] = a_target->m_pixels[(j+a_y)*a_target->m_strideBytes/4 + (i+a_x)];
}
}
HDC hdc = CreateCompatibleDC(0);
SelectObject(hdc, hbitmap);
//HFONT hFont = CreateFontA(fontSize, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, CLEARTYPE_NATURAL_QUALITY, 0, a_fontFamily); // "Segoe Script"); // "Fixedsys");
HFONT hFont = CreateFontA(fontSize, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, CLEARTYPE_QUALITY, 0, a_fontFamily); // "Segoe Script"); // "Fixedsys");
SelectObject(hdc, hFont);
//SetTextColor(hdc, RGB(0xff,0xff,0xff));
SetTextColor(hdc, RGB((a_color >> 16)&0xff,(a_color >> 8)&0xff,a_color&0xff));
SetBkMode(hdc, TRANSPARENT);
RECT rect = { 0, 0, bufferWidth, bufferHeight };
// std::wstring wstr = utf8_to_wstring(string);
std::wstring wstr = String(string).toWString();
DrawTextW(hdc, wstr.c_str(), -1, &rect, DT_LEFT);
DeleteDC(hdc);
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
if ( ((j+a_y) > 0) && ((j+a_y) < a_target->m_height) && ((i+a_x) > 0) && ((i+a_x) < a_target->m_width) )
{
a_target->m_pixels[(j+a_y)*a_target->m_strideBytes/4 + (i+a_x)] = a_bits[j*a_pixelsWidth+i];
}
}
/*
// Make some internal function which can be called by this and CUTE_DrawPixels
//CUTE_DrawPixels(a_target, backBuffer.m_target.m_pixels, a_x, a_y, 1000, 64, 0, 0, 1000, 64);
for (int j = 0; j < a_height; j++)
for (int i = 0; i < a_width; i++)
{
if ( ((j+a_y) > 0) && ((j+a_y) < a_target->m_height) && ((i+a_x) > 0) && ((i+a_x) < a_target->m_width) )
{
uint32_t *dst = &(a_target->m_pixels[(j+a_y)*a_target->m_strideBytes/4 + (i+a_x)]);
uint32_t src = a_bits[j*a_pixelsWidth+i];
if ( src )
*dst = blendColors(a_color, *dst, src & 0xff);
// *dst = a_color;
}
}
*/
DeleteObject(a_bits);
DeleteObject(hFont);
DeleteObject(hbitmap);
}
#endif
END_NAMESPACE