mirror of
https://github.com/The-Powder-Toy/The-Powder-Toy.git
synced 2025-01-17 06:18:22 +01:00
Refactor resize-related code
This commit is contained in:
parent
caab738184
commit
e5af4dab68
@ -13,17 +13,20 @@ int ThumbnailRendererTask::QueueSize()
|
||||
return queueSize;
|
||||
}
|
||||
|
||||
ThumbnailRendererTask::ThumbnailRendererTask(GameSave *save, int width, int height, bool autoRescale, bool decorations, bool fire) :
|
||||
Save(new GameSave(*save)),
|
||||
Width(width),
|
||||
Height(height),
|
||||
Decorations(decorations),
|
||||
Fire(fire),
|
||||
AutoRescale(autoRescale)
|
||||
ThumbnailRendererTask::ThumbnailRendererTask(GameSave const &save, Vec2<int> size, bool autoRescale, bool decorations, bool fire):
|
||||
save(std::make_unique<GameSave>(save)),
|
||||
size(size),
|
||||
decorations(decorations),
|
||||
fire(fire),
|
||||
autoRescale(autoRescale)
|
||||
{
|
||||
queueSize += 1;
|
||||
}
|
||||
|
||||
ThumbnailRendererTask::ThumbnailRendererTask(GameSave *save, int width, int height, bool autoRescale, bool decorations, bool fire):
|
||||
ThumbnailRendererTask(*save, Vec2(width, height), autoRescale, decorations, fire)
|
||||
{}
|
||||
|
||||
ThumbnailRendererTask::~ThumbnailRendererTask()
|
||||
{
|
||||
queueSize -= 1;
|
||||
@ -31,32 +34,17 @@ ThumbnailRendererTask::~ThumbnailRendererTask()
|
||||
|
||||
bool ThumbnailRendererTask::doWork()
|
||||
{
|
||||
thumbnail = std::unique_ptr<VideoBuffer>(SaveRenderer::Ref().Render(Save.get(), Decorations, Fire));
|
||||
thumbnail = std::unique_ptr<VideoBuffer>(SaveRenderer::Ref().Render(save.get(), decorations, fire));
|
||||
if (thumbnail)
|
||||
{
|
||||
if (AutoRescale)
|
||||
if (autoRescale)
|
||||
{
|
||||
int scaleX = (int)std::ceil((float)thumbnail->Width / Width);
|
||||
int scaleY = (int)std::ceil((float)thumbnail->Height / Height);
|
||||
int scale = scaleX > scaleY ? scaleX : scaleY;
|
||||
int newWidth = thumbnail->Width / scale, newHeight = thumbnail->Height / scale;
|
||||
thumbnail->Resize(newWidth, newHeight, true);
|
||||
newWidth = thumbnail->Width;
|
||||
newHeight = thumbnail->Height;
|
||||
if (newWidth > Width || newHeight > Height)
|
||||
{
|
||||
auto croppedWidth = newWidth > Width ? Width : newWidth;
|
||||
auto croppedHeight = newHeight > Height ? Height : newHeight;
|
||||
thumbnail->Crop(croppedWidth, croppedHeight, (newWidth - croppedWidth) / 2, (newHeight - croppedHeight) / 2);
|
||||
newWidth = thumbnail->Width;
|
||||
newHeight = thumbnail->Height;
|
||||
}
|
||||
Width = newWidth;
|
||||
Height = newHeight;
|
||||
thumbnail->ResizeToFit(size, true);
|
||||
size = thumbnail->Size();
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbnail->Resize(Width, Height, true);
|
||||
thumbnail->Resize(size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "common/Vec2.h"
|
||||
#include "tasks/AbandonableTask.h"
|
||||
|
||||
#include <memory>
|
||||
@ -7,16 +8,18 @@ class GameSave;
|
||||
class VideoBuffer;
|
||||
class ThumbnailRendererTask : public AbandonableTask
|
||||
{
|
||||
std::unique_ptr<GameSave> Save;
|
||||
int Width, Height;
|
||||
bool Decorations;
|
||||
bool Fire;
|
||||
bool AutoRescale;
|
||||
std::unique_ptr<GameSave> save;
|
||||
Vec2<int> size;
|
||||
bool decorations;
|
||||
bool fire;
|
||||
bool autoRescale;
|
||||
std::unique_ptr<VideoBuffer> thumbnail;
|
||||
|
||||
static int queueSize;
|
||||
|
||||
public:
|
||||
ThumbnailRendererTask(GameSave const &, Vec2<int> size, bool autoRescale, bool decorations, bool fire);
|
||||
[[deprecated("Use ThumbnailRendererTask(GameSave const &, Vec2<int>, bool, bool, bool)")]]
|
||||
ThumbnailRendererTask(GameSave *save, int width, int height, bool autoRescale = false, bool decorations = true, bool fire = true);
|
||||
virtual ~ThumbnailRendererTask();
|
||||
|
||||
|
@ -4,21 +4,20 @@
|
||||
|
||||
namespace http
|
||||
{
|
||||
ImageRequest::ImageRequest(ByteString url, int width, int height) :
|
||||
Request(url),
|
||||
Width(width),
|
||||
Height(height)
|
||||
{
|
||||
}
|
||||
ImageRequest::ImageRequest(ByteString url, Vec2<int> size):
|
||||
Request(std::move(url)),
|
||||
size(size)
|
||||
{}
|
||||
|
||||
ImageRequest::ImageRequest(ByteString url, int width, int height):
|
||||
ImageRequest(url, Vec2(width, height))
|
||||
{}
|
||||
|
||||
ImageRequest::~ImageRequest()
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
std::unique_ptr<VideoBuffer> ImageRequest::Finish()
|
||||
{
|
||||
int width = Width;
|
||||
int height = Height;
|
||||
auto [ status, data ] = Request::Finish();
|
||||
(void)status; // We don't use this for anything, not ideal >_>
|
||||
std::unique_ptr<VideoBuffer> vb;
|
||||
@ -28,14 +27,14 @@ namespace http
|
||||
std::vector<pixel> imageData;
|
||||
if (PngDataToPixels(imageData, imgw, imgh, data.data(), data.size(), true))
|
||||
{
|
||||
vb = std::unique_ptr<VideoBuffer>(new VideoBuffer(imageData.data(), imgw, imgh));
|
||||
vb = std::make_unique<VideoBuffer>(imageData.data(), Vec2(imgw, imgh));
|
||||
}
|
||||
else
|
||||
{
|
||||
vb = std::unique_ptr<VideoBuffer>(new VideoBuffer(32, 32));
|
||||
vb->SetCharacter(14, 14, 'x', 255, 255, 255, 255);
|
||||
vb = std::make_unique<VideoBuffer>(Vec2(32, 32));
|
||||
vb->BlendChar(Vec2(14, 14), 'x', 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||
}
|
||||
vb->Resize(width, height, true);
|
||||
vb->Resize(size, true);
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "Request.h"
|
||||
#include "common/String.h"
|
||||
#include "common/Vec2.h"
|
||||
#include "Request.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -10,9 +11,11 @@ namespace http
|
||||
{
|
||||
class ImageRequest : public Request
|
||||
{
|
||||
int Width, Height;
|
||||
|
||||
Vec2<int> size;
|
||||
|
||||
public:
|
||||
ImageRequest(ByteString url, Vec2<int> size);
|
||||
[[deprecated("Use ImageRequest(ByteString, Vec<int>)")]]
|
||||
ImageRequest(ByteString url, int width, int height);
|
||||
virtual ~ImageRequest();
|
||||
|
||||
|
@ -41,315 +41,133 @@ VideoBuffer::VideoBuffer(pixel const *buffer, int width, int height, int pitch):
|
||||
VideoBuffer(buffer, Vec2(width, height), pitch == 0 ? width : pitch)
|
||||
{}
|
||||
|
||||
void VideoBuffer::CopyData(pixel * buffer, int width, int height, int pitch)
|
||||
{
|
||||
for (auto y = 0; y < height; ++y)
|
||||
{
|
||||
std::copy(buffer + y * pitch, buffer + y * pitch + width, Buffer.data() + y * width);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBuffer::Crop(Rect<int> rect)
|
||||
{
|
||||
Crop(rect.Size().X, rect.Size().Y, rect.TopLeft.X, rect.TopLeft.Y);
|
||||
rect &= Size().OriginRect();
|
||||
if (rect == Size().OriginRect())
|
||||
return;
|
||||
|
||||
PlaneAdapter<std::vector<pixel> &> newVideo(rect.Size(), video.Base);
|
||||
for (auto y = 0; y < newVideo.Size().Y; y++)
|
||||
std::copy_n(
|
||||
video.RowIterator(rect.TopLeft + Vec2(0, y)),
|
||||
newVideo.Size().X,
|
||||
newVideo.RowIterator(Vec2(0, y))
|
||||
);
|
||||
newVideo.Base.resize(newVideo.Size().X * newVideo.Size().Y);
|
||||
newVideo.Base.shrink_to_fit();
|
||||
video.SetSize(newVideo.Size());
|
||||
}
|
||||
|
||||
void VideoBuffer::Crop(int width, int height, int x, int y)
|
||||
void VideoBuffer::Resize(Vec2<int> size, bool resample)
|
||||
{
|
||||
CopyData(Buffer.data() + y * Width + x, width, height, Width);
|
||||
video.SetSize(Vec2(width, height));
|
||||
if (size == Size())
|
||||
return;
|
||||
|
||||
if (resample)
|
||||
{
|
||||
std::array<std::unique_ptr<Resampler>, PIXELCHANNELS> resamplers;
|
||||
Resampler::Contrib_List *clist_x = NULL, *clist_y = NULL;
|
||||
for (auto &ptr : resamplers)
|
||||
{
|
||||
ptr = std::make_unique<Resampler>(
|
||||
Size().X, Size().Y, // source size
|
||||
size.X, size.Y, // destination size
|
||||
Resampler::BOUNDARY_CLAMP,
|
||||
0.0f, 255.0f, // upper and lower bounds for channel values
|
||||
"lanczos12",
|
||||
clist_x, clist_y,
|
||||
0.75f, 0.75f // X and Y filter scales, values < 1.0 cause aliasing, but create sharper looking mips.
|
||||
);
|
||||
clist_x = ptr->get_clist_x();
|
||||
clist_y = ptr->get_clist_y();
|
||||
}
|
||||
|
||||
std::array<std::unique_ptr<float []>, PIXELCHANNELS> samples;
|
||||
for (auto &ptr : samples)
|
||||
ptr = std::make_unique<float []>(Size().X);
|
||||
|
||||
PlaneAdapter<std::vector<pixel>> newVideo(size);
|
||||
|
||||
pixel const *inIter = video.data();
|
||||
std::array<pixel *, PIXELCHANNELS> outIter;
|
||||
for (pixel *&it : outIter)
|
||||
it = newVideo.data();
|
||||
|
||||
for (int sourceY = 0; sourceY < Size().Y; sourceY++)
|
||||
{
|
||||
for (int sourceX = 0; sourceX < Size().X; sourceX++)
|
||||
{
|
||||
pixel px = *inIter++;
|
||||
for (int c = 0; c < PIXELCHANNELS; c++)
|
||||
samples[c][sourceX] = uint8_t(px >> (8 * c));
|
||||
}
|
||||
|
||||
for (int c = 0; c < PIXELCHANNELS; c++)
|
||||
{
|
||||
if (!resamplers[c]->put_line(samples[c].get()))
|
||||
{
|
||||
fprintf(stderr, "Out of memory when resampling\n");
|
||||
Crop(size.OriginRect()); // Better than leaving the image at original size I guess
|
||||
return;
|
||||
}
|
||||
|
||||
while (float const *output = resamplers[c]->get_line())
|
||||
for (int destX = 0; destX < size.X; destX++)
|
||||
*outIter[c]++ |= pixel(uint8_t(output[destX])) << (8 * c);
|
||||
}
|
||||
}
|
||||
|
||||
video = std::move(newVideo);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaneAdapter<std::vector<pixel>> newVideo(size);
|
||||
for (auto pos : size.OriginRect())
|
||||
{
|
||||
auto oldPos = Vec2(pos.X * Size().X / size.X, pos.Y * Size().Y / size.Y);
|
||||
newVideo[pos] = video[oldPos];
|
||||
}
|
||||
video = std::move(newVideo);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBuffer::Resize(float factor, bool resample)
|
||||
{
|
||||
int newWidth = int(Width * factor);
|
||||
int newHeight = int(Height * factor);
|
||||
Resize(newWidth, newHeight, resample);
|
||||
Resize(Vec2<int>(Size() * factor), resample);
|
||||
}
|
||||
|
||||
void VideoBuffer::Resize(int width, int height, bool resample, bool fixedRatio)
|
||||
void VideoBuffer::ResizeToFit(Vec2<int> bound, bool resample)
|
||||
{
|
||||
int newWidth = width;
|
||||
int newHeight = height;
|
||||
pixel * newBuffer;
|
||||
if(newHeight == -1 && newWidth == -1)
|
||||
return;
|
||||
if(newHeight == -1 || newWidth == -1)
|
||||
Vec2<int> size = Size();
|
||||
if (size.X > bound.X || size.Y > bound.Y)
|
||||
{
|
||||
if(newHeight == -1)
|
||||
newHeight = int(float(Height) * newWidth / Width);
|
||||
if(newWidth == -1)
|
||||
newWidth = int(float(Width) * newHeight / Height);
|
||||
}
|
||||
else if(fixedRatio)
|
||||
{
|
||||
//Force proportions
|
||||
if(newWidth*Height > newHeight*Width) // same as nW/W > nH/H
|
||||
newWidth = (int)(Width * (newHeight/(float)Height));
|
||||
if (bound.X * size.Y < bound.Y * size.X)
|
||||
size = size * bound.X / size.X;
|
||||
else
|
||||
newHeight = (int)(Height * (newWidth/(float)Width));
|
||||
}
|
||||
if(resample)
|
||||
newBuffer = Graphics::resample_img(Buffer.data(), Width, Height, newWidth, newHeight);
|
||||
else
|
||||
newBuffer = Graphics::resample_img_nn(Buffer.data(), Width, Height, newWidth, newHeight);
|
||||
|
||||
if(newBuffer)
|
||||
{
|
||||
Buffer.assign(newBuffer, newBuffer + newWidth * newHeight);
|
||||
delete[] newBuffer;
|
||||
video.SetSize(Vec2(newWidth, newHeight));
|
||||
size = size * bound.Y / size.Y;
|
||||
}
|
||||
Resize(size, resample);
|
||||
}
|
||||
|
||||
int VideoBuffer::SetCharacter(int x, int y, String::value_type c, int r, int g, int b, int a)
|
||||
{
|
||||
FontReader reader(c);
|
||||
for (int j = -2; j < FONT_H - 2; j++)
|
||||
for (int i = 0; i < reader.GetWidth(); i++)
|
||||
SetPixel(x + i, y + j, r, g, b, reader.NextPixel() * a / 3);
|
||||
return x + reader.GetWidth();
|
||||
// Technically inaccurate but oh well
|
||||
BlendChar(Vec2(x, y), c, RGBA<uint8_t>(r, g, b, a));
|
||||
}
|
||||
|
||||
int VideoBuffer::BlendCharacter(int x, int y, String::value_type c, int r, int g, int b, int a)
|
||||
{
|
||||
FontReader reader(c);
|
||||
for (int j = -2; j < FONT_H - 2; j++)
|
||||
for (int i = 0; i < reader.GetWidth(); i++)
|
||||
BlendPixel(x + i, y + j, r, g, b, reader.NextPixel() * a / 3);
|
||||
return x + reader.GetWidth();
|
||||
BlendChar(Vec2(x, y), c, RGBA<uint8_t>(r, g, b, a));
|
||||
}
|
||||
|
||||
int VideoBuffer::AddCharacter(int x, int y, String::value_type c, int r, int g, int b, int a)
|
||||
{
|
||||
FontReader reader(c);
|
||||
for (int j = -2; j < FONT_H - 2; j++)
|
||||
for (int i = 0; i < reader.GetWidth(); i++)
|
||||
AddPixel(x + i, y + j, r, g, b, reader.NextPixel() * a / 3);
|
||||
return x + reader.GetWidth();
|
||||
AddChar(Vec2(x, y), c, RGBA<uint8_t>(r, g, b, a));
|
||||
}
|
||||
|
||||
template class RasterDrawMethods<VideoBuffer>;
|
||||
|
||||
pixel *Graphics::resample_img_nn(pixel * src, int sw, int sh, int rw, int rh)
|
||||
{
|
||||
int y, x;
|
||||
pixel *q = NULL;
|
||||
q = new pixel[rw*rh];
|
||||
for (y=0; y<rh; y++)
|
||||
for (x=0; x<rw; x++){
|
||||
q[rw*y+x] = src[sw*(y*sh/rh)+(x*sw/rw)];
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
pixel *Graphics::resample_img(pixel *src, int sw, int sh, int rw, int rh)
|
||||
{
|
||||
#ifdef HIGH_QUALITY_RESAMPLE
|
||||
|
||||
unsigned char * source = (unsigned char*)src;
|
||||
int sourceWidth = sw, sourceHeight = sh;
|
||||
int resultWidth = rw, resultHeight = rh;
|
||||
int sourcePitch = sourceWidth*PIXELSIZE, resultPitch = resultWidth*PIXELSIZE;
|
||||
// Filter scale - values < 1.0 cause aliasing, but create sharper looking mips.
|
||||
const float filter_scale = 0.75f;
|
||||
const char* pFilter = "lanczos12";
|
||||
|
||||
|
||||
Resampler * resamplers[PIXELCHANNELS];
|
||||
float * samples[PIXELCHANNELS];
|
||||
|
||||
//Resampler for each colour channel
|
||||
if (sourceWidth <= 0 || sourceHeight <= 0 || resultWidth <= 0 || resultHeight <= 0)
|
||||
return NULL;
|
||||
resamplers[0] = new Resampler(sourceWidth, sourceHeight, resultWidth, resultHeight, Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f, pFilter, NULL, NULL, filter_scale, filter_scale);
|
||||
samples[0] = new float[sourceWidth];
|
||||
for (int i = 1; i < PIXELCHANNELS; i++)
|
||||
{
|
||||
resamplers[i] = new Resampler(sourceWidth, sourceHeight, resultWidth, resultHeight, Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f, pFilter, resamplers[0]->get_clist_x(), resamplers[0]->get_clist_y(), filter_scale, filter_scale);
|
||||
samples[i] = new float[sourceWidth];
|
||||
}
|
||||
|
||||
unsigned char * resultImage = new unsigned char[resultHeight * resultPitch];
|
||||
std::fill(resultImage, resultImage + (resultHeight*resultPitch), 0);
|
||||
|
||||
//Resample time
|
||||
int resultY = 0;
|
||||
for (int sourceY = 0; sourceY < sourceHeight; sourceY++)
|
||||
{
|
||||
unsigned char * sourcePixel = &source[sourceY * sourcePitch];
|
||||
|
||||
//Move pixel components into channel samples
|
||||
for (int c = 0; c < PIXELCHANNELS; c++)
|
||||
{
|
||||
for (int x = 0; x < sourceWidth; x++)
|
||||
{
|
||||
samples[c][x] = sourcePixel[(x*PIXELSIZE)+c] * (1.0f/255.0f);
|
||||
}
|
||||
}
|
||||
|
||||
//Put channel sample data into resampler
|
||||
for (int c = 0; c < PIXELCHANNELS; c++)
|
||||
{
|
||||
if (!resamplers[c]->put_line(&samples[c][0]))
|
||||
{
|
||||
printf("Out of memory!\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//Perform resample and Copy components from resampler result samples to image buffer
|
||||
for ( ; ; )
|
||||
{
|
||||
int comp_index;
|
||||
for (comp_index = 0; comp_index < PIXELCHANNELS; comp_index++)
|
||||
{
|
||||
const float* resultSamples = resamplers[comp_index]->get_line();
|
||||
if (!resultSamples)
|
||||
break;
|
||||
|
||||
unsigned char * resultPixel = &resultImage[(resultY * resultPitch) + comp_index];
|
||||
|
||||
for (int x = 0; x < resultWidth; x++)
|
||||
{
|
||||
int c = (int)(255.0f * resultSamples[x] + .5f);
|
||||
if (c < 0) c = 0; else if (c > 255) c = 255;
|
||||
*resultPixel = (unsigned char)c;
|
||||
resultPixel += PIXELSIZE;
|
||||
}
|
||||
}
|
||||
if (comp_index < PIXELCHANNELS)
|
||||
break;
|
||||
|
||||
resultY++;
|
||||
}
|
||||
}
|
||||
|
||||
//Clean up
|
||||
for(int i = 0; i < PIXELCHANNELS; i++)
|
||||
{
|
||||
delete resamplers[i];
|
||||
delete[] samples[i];
|
||||
}
|
||||
|
||||
return (pixel*)resultImage;
|
||||
#else
|
||||
if constexpr (DEBUG)
|
||||
{
|
||||
std::cout << "Resampling " << sw << "x" << sh << " to " << rw << "x" << rh << std::endl;
|
||||
}
|
||||
bool stairstep = false;
|
||||
if(rw < sw || rh < sh)
|
||||
{
|
||||
float fx = (float)(((float)sw)/((float)rw));
|
||||
float fy = (float)(((float)sh)/((float)rh));
|
||||
|
||||
int fxint, fyint;
|
||||
double fxintp_t, fyintp_t;
|
||||
|
||||
float fxf = modf(fx, &fxintp_t), fyf = modf(fy, &fyintp_t);
|
||||
fxint = fxintp_t;
|
||||
fyint = fyintp_t;
|
||||
|
||||
if(((fxint & (fxint-1)) == 0 && fxf < 0.1f) || ((fyint & (fyint-1)) == 0 && fyf < 0.1f))
|
||||
stairstep = true;
|
||||
|
||||
if constexpr (DEBUG)
|
||||
{
|
||||
if(stairstep)
|
||||
std::cout << "Downsampling by " << fx << "x" << fy << " using stairstepping" << std::endl;
|
||||
else
|
||||
std::cout << "Downsampling by " << fx << "x" << fy << " without stairstepping" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int y, x, fxceil, fyceil;
|
||||
//int i,j,x,y,w,h,r,g,b,c;
|
||||
pixel *q = NULL;
|
||||
if(rw == sw && rh == sh){
|
||||
//Don't resample
|
||||
q = new pixel[rw*rh];
|
||||
std::copy(src, src+(rw*rh), q);
|
||||
} else if(!stairstep) {
|
||||
float fx, fy, fyc, fxc;
|
||||
double intp;
|
||||
pixel tr, tl, br, bl;
|
||||
q = new pixel[rw*rh];
|
||||
//Bilinear interpolation for upscaling
|
||||
for (y=0; y<rh; y++)
|
||||
for (x=0; x<rw; x++)
|
||||
{
|
||||
fx = ((float)x)*((float)sw)/((float)rw);
|
||||
fy = ((float)y)*((float)sh)/((float)rh);
|
||||
fxc = modf(fx, &intp);
|
||||
fyc = modf(fy, &intp);
|
||||
fxceil = (int)ceil(fx);
|
||||
fyceil = (int)ceil(fy);
|
||||
if (fxceil>=sw) fxceil = sw-1;
|
||||
if (fyceil>=sh) fyceil = sh-1;
|
||||
tr = src[sw*(int)floor(fy)+fxceil];
|
||||
tl = src[sw*(int)floor(fy)+(int)floor(fx)];
|
||||
br = src[sw*fyceil+fxceil];
|
||||
bl = src[sw*fyceil+(int)floor(fx)];
|
||||
q[rw*y+x] = PIXRGB(
|
||||
(int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)),
|
||||
(int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)),
|
||||
(int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
//Stairstepping
|
||||
float fx, fy, fyc, fxc;
|
||||
double intp;
|
||||
pixel tr, tl, br, bl;
|
||||
int rrw = rw, rrh = rh;
|
||||
pixel * oq;
|
||||
oq = new pixel[sw*sh];
|
||||
std::copy(src, src+(sw*sh), oq);
|
||||
rw = sw;
|
||||
rh = sh;
|
||||
while(rrw != rw && rrh != rh){
|
||||
if(rw > rrw)
|
||||
rw *= 0.7;
|
||||
if(rh > rrh)
|
||||
rh *= 0.7;
|
||||
if(rw <= rrw)
|
||||
rw = rrw;
|
||||
if(rh <= rrh)
|
||||
rh = rrh;
|
||||
q = new pixel[rw*rh];
|
||||
//Bilinear interpolation
|
||||
for (y=0; y<rh; y++)
|
||||
for (x=0; x<rw; x++)
|
||||
{
|
||||
fx = ((float)x)*((float)sw)/((float)rw);
|
||||
fy = ((float)y)*((float)sh)/((float)rh);
|
||||
fxc = modf(fx, &intp);
|
||||
fyc = modf(fy, &intp);
|
||||
fxceil = (int)ceil(fx);
|
||||
fyceil = (int)ceil(fy);
|
||||
if (fxceil>=sw) fxceil = sw-1;
|
||||
if (fyceil>=sh) fyceil = sh-1;
|
||||
tr = oq[sw*(int)floor(fy)+fxceil];
|
||||
tl = oq[sw*(int)floor(fy)+(int)floor(fx)];
|
||||
br = oq[sw*fyceil+fxceil];
|
||||
bl = oq[sw*fyceil+(int)floor(fx)];
|
||||
q[rw*y+x] = PIXRGB(
|
||||
(int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)),
|
||||
(int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)),
|
||||
(int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc))
|
||||
);
|
||||
}
|
||||
delete[] oq;
|
||||
oq = q;
|
||||
sw = rw;
|
||||
sh = rh;
|
||||
}
|
||||
}
|
||||
return q;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Graphics::textwidth(const String &str)
|
||||
{
|
||||
int x = 0;
|
||||
@ -737,29 +555,25 @@ void Graphics::draw_rgba_image(const pixel *data, int w, int h, int x, int y, fl
|
||||
|
||||
VideoBuffer Graphics::DumpFrame()
|
||||
{
|
||||
VideoBuffer newBuffer(WINDOWW, WINDOWH);
|
||||
std::copy(vid, vid+(WINDOWW*WINDOWH), newBuffer.Buffer.data());
|
||||
VideoBuffer newBuffer(WINDOW);
|
||||
std::copy_n(video.data(), WINDOW.X * WINDOW.Y, newBuffer.Data());
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
void Graphics::SwapClipRect(Rect<int> &rect)
|
||||
{
|
||||
std::swap(clipRect, rect);
|
||||
clipRect &= video.Size().OriginRect();
|
||||
}
|
||||
|
||||
void Graphics::SetClipRect(int &x, int &y, int &w, int &h)
|
||||
{
|
||||
int newX = x;
|
||||
int newY = y;
|
||||
int newW = w;
|
||||
int newH = h;
|
||||
if (newX < 0) newX = 0;
|
||||
if (newY < 0) newY = 0;
|
||||
if (newW > WINDOWW - newX) newW = WINDOWW - newX;
|
||||
if (newH > WINDOWH - newY) newH = WINDOWH - newY;
|
||||
x = clipx1;
|
||||
y = clipy1;
|
||||
w = clipx2 - clipx1;
|
||||
h = clipy2 - clipy1;
|
||||
clipx1 = newX;
|
||||
clipy1 = newY;
|
||||
clipx2 = newX + newW;
|
||||
clipy2 = newY + newH;
|
||||
Rect<int> rect = RectSized(Vec2(x, y), Vec2(w, h));
|
||||
SwapClipRect(rect);
|
||||
x = rect.TopLeft.X;
|
||||
y = rect.TopLeft.Y;
|
||||
w = rect.Size().X;
|
||||
h = rect.Size().Y;
|
||||
}
|
||||
|
||||
bool VideoBuffer::WritePNG(const ByteString &path) const
|
||||
|
@ -20,8 +20,6 @@ class VideoBuffer: public RasterDrawMethods<VideoBuffer>
|
||||
|
||||
friend struct RasterDrawMethods<VideoBuffer>;
|
||||
|
||||
void CopyData(pixel * buffer, int width, int height, int pitch);
|
||||
|
||||
public:
|
||||
[[deprecated("Use video")]]
|
||||
std::vector<pixel> &Buffer = video.Base;
|
||||
@ -40,6 +38,11 @@ public:
|
||||
return video.Size();
|
||||
}
|
||||
|
||||
pixel *Data()
|
||||
{
|
||||
return video.data();
|
||||
}
|
||||
|
||||
pixel const *Data() const
|
||||
{
|
||||
return video.data();
|
||||
@ -48,7 +51,9 @@ public:
|
||||
void Crop(Rect<int>);
|
||||
|
||||
void Resize(float factor, bool resample = false);
|
||||
void Resize(Vec2<int> size, bool resamble = false, bool fixedRatio = true);
|
||||
void Resize(Vec2<int> size, bool resample = false);
|
||||
// Automatically choose a size to fit within the given box, keeping aspect ratio
|
||||
void ResizeToFit(Vec2<int> bound, bool resample = false);
|
||||
|
||||
[[deprecated("Use VideoBuffer(VideoBuffer const &)")]]
|
||||
VideoBuffer(VideoBuffer * old);
|
||||
@ -56,54 +61,25 @@ public:
|
||||
VideoBuffer(pixel const *buffer, int width, int height, int pitch = 0);
|
||||
[[deprecated("Use VideoBuffer(Vec2<int>)")]]
|
||||
VideoBuffer(int width, int height);
|
||||
[[deprecated("Use Resize(Vec2<int>, bool, bool)")]]
|
||||
void Resize(int width, int height, bool resample = false, bool fixedRatio = true);
|
||||
[[deprecated("Use Crop(Rect<int>)")]]
|
||||
void Crop(int width, int height, int x, int y);
|
||||
|
||||
using RasterDrawMethods<VideoBuffer>::BlendPixel;
|
||||
[[deprecated("Use BlendPixel(Vec2<int>, RGBA<uint8_t>)")]]
|
||||
TPT_INLINE void BlendPixel(int x, int y, int r, int g, int b, int a)
|
||||
{
|
||||
pixel t;
|
||||
if (x<0 || y<0 || x>=Width || y>=Height)
|
||||
return;
|
||||
if (a!=255)
|
||||
{
|
||||
t = Buffer[y*(Width)+x];
|
||||
r = (a*r + (255-a)*PIXR(t)) >> 8;
|
||||
g = (a*g + (255-a)*PIXG(t)) >> 8;
|
||||
b = (a*b + (255-a)*PIXB(t)) >> 8;
|
||||
}
|
||||
Buffer[y*(Width)+x] = PIXRGB(r,g,b);
|
||||
BlendPixel(Vec2(x, y), RGBA<uint8_t>(r, g, b, a));
|
||||
}
|
||||
|
||||
[[deprecated("Use DrawPixel")]]
|
||||
TPT_INLINE void SetPixel(int x, int y, int r, int g, int b, int a)
|
||||
{
|
||||
if (x<0 || y<0 || x>=Width || y>=Height)
|
||||
return;
|
||||
Buffer[y*(Width)+x] = PIXRGB((r*a)>>8, (g*a)>>8, (b*a)>>8);
|
||||
DrawPixel(Vec2(x, y), 0x000000_rgb .Blend(RGBA<uint8_t>(r, g, b, a)));
|
||||
}
|
||||
|
||||
using RasterDrawMethods<VideoBuffer>::AddPixel;
|
||||
[[deprecated("Use AddPixel(Vec2<int>, RGBA<uint8_t>)")]]
|
||||
TPT_INLINE void AddPixel(int x, int y, int r, int g, int b, int a)
|
||||
{
|
||||
pixel t;
|
||||
if (x<0 || y<0 || x>=Width || y>=Height)
|
||||
return;
|
||||
t = Buffer[y*(Width)+x];
|
||||
r = (a*r + 255*PIXR(t)) >> 8;
|
||||
g = (a*g + 255*PIXG(t)) >> 8;
|
||||
b = (a*b + 255*PIXB(t)) >> 8;
|
||||
if (r>255)
|
||||
r = 255;
|
||||
if (g>255)
|
||||
g = 255;
|
||||
if (b>255)
|
||||
b = 255;
|
||||
Buffer[y*(Width)+x] = PIXRGB(r,g,b);
|
||||
AddPixel(Vec2(x, y), RGBA<uint8_t>(r, g, b, a));
|
||||
}
|
||||
[[deprecated("Use BlendChar")]]
|
||||
int SetCharacter(int x, int y, String::value_type c, int r, int g, int b, int a);
|
||||
@ -127,15 +103,6 @@ class Graphics: public RasterDrawMethods<Graphics>
|
||||
|
||||
friend struct RasterDrawMethods<Graphics>;
|
||||
|
||||
[[deprecated("Use clipRect")]]
|
||||
int &clipx1 = clipRect.TopLeft.X;
|
||||
[[deprecated("Use clipRect")]]
|
||||
int &clipy1 = clipRect.TopLeft.Y;
|
||||
[[deprecated("Use clipRect")]]
|
||||
int &clipx2 = clipRect.BottomRight.X;
|
||||
[[deprecated("Use clipRect")]]
|
||||
int &clipy2 = clipRect.BottomRight.Y;
|
||||
|
||||
public:
|
||||
pixel const *Data() const
|
||||
{
|
||||
@ -154,10 +121,6 @@ public:
|
||||
};
|
||||
static std::vector<pixel> Gradient(std::vector<GradientStop> stops, int resolution);
|
||||
|
||||
//PTIF methods
|
||||
static pixel *resample_img_nn(pixel *src, int sw, int sh, int rw, int rh);
|
||||
static pixel *resample_img(pixel *src, int sw, int sh, int rw, int rh);
|
||||
|
||||
//Font/text metrics
|
||||
static int CharWidth(String::value_type c);
|
||||
static int textwidthx(const String &s, int w);
|
||||
@ -168,7 +131,6 @@ public:
|
||||
|
||||
void draw_icon(int x, int y, Icon icon, unsigned char alpha = 255, bool invert = false);
|
||||
|
||||
void Clear();
|
||||
void Finalise();
|
||||
|
||||
void draw_rgba_image(const pixel *data, int w, int h, int x, int y, float alpha);
|
||||
@ -176,6 +138,8 @@ public:
|
||||
Graphics()
|
||||
{}
|
||||
|
||||
void SwapClipRect(Rect<int> &);
|
||||
[[deprecated("Use SwapClipRect")]]
|
||||
void SetClipRect(int &x, int &y, int &w, int &h);
|
||||
};
|
||||
|
||||
|
@ -4,11 +4,6 @@
|
||||
#include "SimulationConfig.h"
|
||||
#include "RasterDrawMethodsImpl.h"
|
||||
|
||||
void Graphics::Clear()
|
||||
{
|
||||
memset(vid, 0, PIXELSIZE * (WINDOWW * WINDOWH));
|
||||
}
|
||||
|
||||
void Graphics::Finalise()
|
||||
{
|
||||
|
||||
|
@ -52,8 +52,7 @@ void Renderer::SetSample(int x, int y)
|
||||
sampleColor = GetPixel(x, y);
|
||||
}
|
||||
|
||||
void Renderer::clearScreen()
|
||||
{
|
||||
void Renderer::clearScreen() {
|
||||
if(display_mode & DISPLAY_PERS)
|
||||
{
|
||||
std::copy(persistentVid, persistentVid+(VIDXRES*YRES), vid);
|
||||
|
@ -464,18 +464,8 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
|
||||
if(save->GetGameSave())
|
||||
{
|
||||
savePreview = SaveRenderer::Ref().Render(save->GetGameSave(), false, true);
|
||||
|
||||
if(savePreview && !(savePreview->Width == XRES/2 && savePreview->Height == YRES/2))
|
||||
{
|
||||
float factorX = ((float)XRES/2)/((float)savePreview->Width);
|
||||
float factorY = ((float)YRES/2)/((float)savePreview->Height);
|
||||
float scaleFactor = factorY < factorX ? factorY : factorX;
|
||||
pixel *data = Graphics::resample_img(savePreview->Buffer.data(), savePreview->Width, savePreview->Height, int(savePreview->Width*scaleFactor), int(savePreview->Height*scaleFactor));
|
||||
savePreview->Width = int(savePreview->Width * scaleFactor);
|
||||
savePreview->Height = int(savePreview->Height * scaleFactor);
|
||||
savePreview->Buffer.assign(data, data + savePreview->Width * savePreview->Height);
|
||||
delete[] data;
|
||||
}
|
||||
if (savePreview)
|
||||
savePreview->ResizeToFit(RES / 2, true);
|
||||
}
|
||||
else if (!sender->GetCanOpen())
|
||||
openButton->Enabled = false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user