421 lines
11 KiB
C++
421 lines
11 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
------------------------------------------------------------------------------*/
|
|
|
|
|
|
#include <cstring>
|
|
#include "picture/pict.h"
|
|
#include "color/color.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Picture::Reframe(int offset_sx, int offset_sy, int offset_ex, int offset_ey, const Color *fill)
|
|
{
|
|
if (!GetData() || (GetPixelFormat().GetBpp() != 32))
|
|
return false;
|
|
|
|
int _width = GetWidth() - offset_sx + offset_ex,
|
|
_height = GetHeight() - offset_sy + offset_ey;
|
|
|
|
uint *new_data = (uint *)AllocMemory(sizeof(uint) * _width * _height), *_d = new_data;
|
|
|
|
if (!new_data)
|
|
__ERR__(__LOG_E__ << "Failed to allocate destination buffer.\n", false)
|
|
|
|
// Fill color.
|
|
uint _fill = fill ? GetPixelFormat().Format(fill->x, fill->y, fill->z, fill->w) : GetPixelFormat().Format(0, 0, 0);
|
|
|
|
// Top framing.
|
|
for (int y = offset_sy; y < 0; ++y)
|
|
for (int x = 0; x < _width; ++x)
|
|
*_d++ = _fill;
|
|
|
|
// Blit + left/right framing.
|
|
int blit_ex = offset_ex > 0 ? GetWidth() : GetWidth() + offset_ex,
|
|
blit_ey = offset_ey > 0 ? GetHeight() : GetHeight() + offset_ey;
|
|
|
|
uint *s = (uint *)GetData();
|
|
if (offset_sy > 0)
|
|
s += GetWidth() * offset_sy;
|
|
|
|
for (int y = offset_sy > 0 ? offset_sy : 0; y < blit_ey; ++y)
|
|
{
|
|
uint *_s = s;
|
|
|
|
for (int x = offset_sx; x < 0; ++x)
|
|
*_d++ = _fill; // Left framing
|
|
for (int x = offset_sx > 0 ? offset_sx : 0; x < blit_ex; ++x)
|
|
*_d++ = _s[x]; // Blit
|
|
for (int x = 0; x < offset_ex; ++x)
|
|
*_d++ = _fill; // Right framing
|
|
|
|
s += GetWidth();
|
|
}
|
|
|
|
// Bottom framing.
|
|
for (int y = 0; y < offset_ey; ++y)
|
|
for (int x = 0; x < _width; ++x)
|
|
*_d++ = _fill;
|
|
|
|
SetData(new_data, _width, _height, GetPixelFormat().GetDesc(), true);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Picture::Flip(bool flip_h, bool flip_v)
|
|
{
|
|
if (!GetData() || (GetPixelFormat().GetBpp() != 32))
|
|
return false;
|
|
|
|
uint *s = (uint *)GetData(),
|
|
*d = s, t;
|
|
|
|
if (flip_h)
|
|
{
|
|
if (flip_v)
|
|
{
|
|
d += GetWidth() * GetHeight() - 1;
|
|
|
|
while (d > s)
|
|
{
|
|
t = *s;
|
|
*s++ = *d;
|
|
*d-- = t;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (uint n = 0; n < GetHeight(); ++n)
|
|
{
|
|
uint *_s = s, *_d = d + GetWidth() - 1;
|
|
|
|
while (_d > _s)
|
|
{
|
|
t = *_s;
|
|
*_s++ = *_d;
|
|
*_d-- = t;
|
|
}
|
|
s += GetWidth();
|
|
d += GetWidth();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (flip_v)
|
|
{
|
|
d += GetWidth() * (GetHeight() - 1);
|
|
while (d > s)
|
|
{
|
|
for (uint n = 0; n < GetWidth(); ++n)
|
|
{
|
|
t = s[n];
|
|
s[n] = d[n];
|
|
d[n] = t;
|
|
}
|
|
s += GetWidth();
|
|
d -= GetWidth();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void WindowClip(const Rect <int> *a, Rect <int> *fa, const Rect <int> *b, Rect <int> *fb)
|
|
{
|
|
Rect <int> _b(*b);
|
|
_b.ex = _b.sx + a->GetWidth();
|
|
_b.ey = _b.sy + a->GetHeight();
|
|
|
|
// Clip source rectangle and correct destination rectangle.
|
|
*fa = fa->Intersection(*a);
|
|
_b.sx += fa->sx - a->sx;
|
|
_b.sy += fa->sy - a->sy;
|
|
_b.ex += fa->ex - a->ex;
|
|
_b.ey += fa->ey - a->ey;
|
|
|
|
// Clip destination rectangle and correct source rectangle.
|
|
*fb = fb->Intersection(_b);
|
|
fa->sx += fb->sx - _b.sx;
|
|
fa->sy += fb->sy - _b.sy;
|
|
fa->ex += fb->ex - _b.ex;
|
|
fa->ey += fb->ey - _b.ey;
|
|
}
|
|
static void WindowStretch(Rect <float> *a, Rect <float> *fa, Rect <float> *b, Rect <float> *fb)
|
|
{
|
|
float ku = (float)b->GetWidth() / (float)a->GetWidth(),
|
|
kv = (float)b->GetHeight() / (float)a->GetHeight();
|
|
|
|
// Clip source rectangle and correct destination rectangle.
|
|
fa[0] = fa->Intersection(a[0]);
|
|
b->sx += (fa->sx - a->sx) * ku;
|
|
b->sy += (fa->sy - a->sy) * kv;
|
|
b->ex += (fa->ex - a->ex) * ku;
|
|
b->ey += (fa->ey - a->ey) * kv;
|
|
|
|
// Clip destination rectangle and correct source rectangle.
|
|
fb[0] = fb->Intersection(b[0]);
|
|
ku = 1 / ku;
|
|
kv = 1 / kv;
|
|
fa->sx += (fb->sx - b->sx) * ku;
|
|
fa->sy += (fb->sy - b->sy) * kv;
|
|
fa->ex += (fb->ex - b->ex) * ku;
|
|
fa->ey += (fb->ey - b->ey) * kv;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Picture::BlitMask(const Picture &src, Picture &dst, Picture &msk, const Rect <int> *src_rect, const Rect <int> *dst_rect)
|
|
{
|
|
if (!src.GetData() || !dst.GetData())
|
|
return false;
|
|
|
|
// Default blit rectangles.
|
|
Rect <int> default_a(src.GetRect()), default_b(dst.GetRect());
|
|
if (!src_rect)
|
|
src_rect = &default_a;
|
|
if (!dst_rect)
|
|
dst_rect = &default_b;
|
|
|
|
// Compute blit window.
|
|
Rect <int> frame_a(src.GetRect()), frame_b(dst.GetRect());
|
|
WindowClip(src_rect, &frame_a, dst_rect, &frame_b);
|
|
|
|
// Limit to mask dimensions.
|
|
if (frame_b.GetWidth() > (int)msk.GetWidth())
|
|
frame_b.SetWidth(msk.GetWidth());
|
|
if (frame_b.GetHeight() > (int)msk.GetHeight())
|
|
frame_b.SetHeight(msk.GetHeight());
|
|
|
|
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
|
|
return false;
|
|
|
|
PixelFormatDescription initial = dst.GetPixelFormat().GetDesc();
|
|
dst.Convert(src.GetPixelFormat().GetDesc());
|
|
|
|
// Perform blit.
|
|
uchar *psrc = src.GetDataOffset(frame_a.sx, frame_a.sy),
|
|
*pdst = dst.GetDataOffset(frame_b.sx, frame_b.sy),
|
|
*pmsk = msk.GetData();
|
|
|
|
for (int n = 0; n < frame_b.GetHeight(); n++)
|
|
{
|
|
uchar *_pdst = pdst, *_psrc = psrc, *_pmsk = pmsk;
|
|
|
|
for (int x = frame_b.GetWidth(); x--; )
|
|
{
|
|
uchar alpha = _pmsk[3];
|
|
|
|
uchar a_blend = AlphaCompositeAlpha(_pdst[3], alpha);
|
|
_pdst[0] = AlphaCompositeColor(_pdst[0], _psrc[0], _pdst[3], alpha, a_blend);
|
|
_pdst[1] = AlphaCompositeColor(_pdst[1], _psrc[1], _pdst[3], alpha, a_blend);
|
|
_pdst[2] = AlphaCompositeColor(_pdst[2], _psrc[2], _pdst[3], alpha, a_blend);
|
|
_pdst[3] = a_blend;
|
|
|
|
_psrc += 4;
|
|
_pdst += 4;
|
|
_pmsk += 4;
|
|
}
|
|
|
|
psrc += src.GetPitch();
|
|
pdst += dst.GetPitch();
|
|
pmsk += msk.GetPitch();
|
|
}
|
|
dst.Convert(initial);
|
|
return true;
|
|
}
|
|
bool Picture::Blit(const Picture &src, Picture &dst, const Rect <int> *src_rect, const Rect <int> *dst_rect, BlendMode mode)
|
|
{
|
|
if (!src.GetData() || !dst.GetData())
|
|
return false;
|
|
|
|
// Default blit rectangles.
|
|
Rect <int> default_a(src.GetRect()), default_b(dst.GetRect());
|
|
if (!src_rect)
|
|
src_rect = &default_a;
|
|
if (!dst_rect)
|
|
dst_rect = &default_b;
|
|
|
|
// Compute blit window.
|
|
Rect <int> frame_a(src.GetRect()), frame_b(dst.GetRect());
|
|
WindowClip(src_rect, &frame_a, dst_rect, &frame_b);
|
|
|
|
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
|
|
return false;
|
|
|
|
PixelFormatDescription initial = dst.GetPixelFormat().GetDesc();
|
|
dst.Convert(src.GetPixelFormat().GetDesc());
|
|
|
|
// Perform blit.
|
|
uchar *psrc = src.GetDataOffset(frame_a.sx, frame_a.sy),
|
|
*pdst = dst.GetDataOffset(frame_b.sx, frame_b.sy);
|
|
|
|
for (int n = 0; n < frame_b.GetHeight(); n++)
|
|
{
|
|
uchar *_pdst = pdst, *_psrc = psrc;
|
|
|
|
switch (mode)
|
|
{
|
|
case RgbToAlpha:
|
|
for (int x = frame_b.GetWidth(); x--; )
|
|
{
|
|
_pdst[3] = (_psrc[0] + _psrc[1] + _psrc[2]) / 3;
|
|
_psrc += 4;
|
|
_pdst += 4;
|
|
}
|
|
break;
|
|
|
|
case BlendReplace:
|
|
memmove(pdst, psrc, frame_b.GetWidth() * dst.GetBpp() / 8);
|
|
break;
|
|
|
|
case BlendComposeFast:
|
|
for (int x = frame_b.GetWidth(); x--; )
|
|
{
|
|
int a = _psrc[3], ia = 255 - a;
|
|
_pdst[0] = uchar((_psrc[0] * a + _pdst[0] * ia) >> 8);
|
|
_pdst[1] = uchar((_psrc[1] * a + _pdst[1] * ia) >> 8);
|
|
_pdst[2] = uchar((_psrc[2] * a + _pdst[2] * ia) >> 8);
|
|
_pdst[3] = uchar(Types::Max <int> (_pdst[3], a));
|
|
|
|
_psrc += 4;
|
|
_pdst += 4;
|
|
}
|
|
break;
|
|
|
|
case BlendCompose:
|
|
for (int x = frame_b.GetWidth(); x--; )
|
|
{
|
|
uchar a_blend = AlphaCompositeAlpha(_pdst[3], _psrc[3]);
|
|
_pdst[0] = AlphaCompositeColor(_pdst[0], _psrc[0], _pdst[3], _psrc[3], a_blend);
|
|
_pdst[1] = AlphaCompositeColor(_pdst[1], _psrc[1], _pdst[3], _psrc[3], a_blend);
|
|
_pdst[2] = AlphaCompositeColor(_pdst[2], _psrc[2], _pdst[3], _psrc[3], a_blend);
|
|
_pdst[3] = a_blend;
|
|
|
|
_psrc += 4;
|
|
_pdst += 4;
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
psrc += src.GetPitch();
|
|
pdst += dst.GetPitch();
|
|
}
|
|
dst.Convert(initial);
|
|
return true;
|
|
}
|
|
bool Picture::ScaleBlit(Picture &src, Picture &dst, Rect <float> *src_rect, Rect <float> *dst_rect)
|
|
{
|
|
if (!src.GetData() || !dst.GetData())
|
|
return false;
|
|
|
|
// Default blitting rectangles.
|
|
Rect <float> default_a(src.GetRect().AsFloat()), default_b(dst.GetRect().AsFloat());
|
|
if (!src_rect)
|
|
src_rect = &default_a;
|
|
if (!dst_rect)
|
|
dst_rect = &default_b;
|
|
|
|
Rect <float> frame_a(src.GetRect().AsFloat()), frame_b(dst.GetRect().AsFloat());
|
|
WindowStretch(src_rect, &frame_a, dst_rect, &frame_b);
|
|
|
|
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
|
|
return false;
|
|
|
|
// Apply sub-pixel/sub-texel correction.
|
|
float ku = frame_a.GetWidth() / frame_b.GetWidth(),
|
|
kv = frame_a.GetHeight() / frame_b.GetHeight();
|
|
|
|
float fb_isx = Math::Floor(frame_b.sx),
|
|
fb_isy = Math::Floor(frame_b.sy),
|
|
fb_iex = Math::Floor(frame_b.ex),
|
|
fb_iey = Math::Floor(frame_b.ey);
|
|
|
|
float dt_su = (frame_b.sx - fb_isx),
|
|
dt_sv = (frame_b.sy - fb_isy),
|
|
dt_eu = (frame_b.ex - fb_iex),
|
|
dt_ev = (frame_b.ey - fb_iey);
|
|
|
|
frame_a.sx -= dt_su * ku;
|
|
frame_a.sy -= dt_sv * kv;
|
|
frame_a.ex -= dt_eu * ku;
|
|
frame_a.ey -= dt_ev * kv;
|
|
|
|
frame_b.sx = fb_isx;
|
|
frame_b.sy = fb_isy;
|
|
frame_b.ex = fb_iex;
|
|
frame_b.ey = fb_iey;
|
|
|
|
// Convert sub pixel deltas to blending coefficients.
|
|
dt_su = 1 - dt_su;
|
|
dt_sv = 1 - dt_sv;
|
|
|
|
uint *pdst = (uint *)dst.GetDataOffset((uint)frame_b.sx, (uint)frame_b.sy);
|
|
|
|
int src_width = (int)frame_b.GetWidth(),
|
|
src_height = (int)frame_b.GetHeight();
|
|
|
|
float v = frame_a.sy;
|
|
for (int y = 0; y < src_height; y++)
|
|
{
|
|
float u = frame_a.sx;
|
|
|
|
// Blended or opaque scanline.
|
|
if ((!y) || (y == (src_height - 1)))
|
|
{
|
|
float kfrst, kscan, klast;
|
|
|
|
// Set blend coefficients.
|
|
if (!y)
|
|
{
|
|
kfrst = dt_su * dt_sv; // top-left
|
|
kscan = dt_sv; // top
|
|
klast = dt_eu * dt_sv; // top-right
|
|
}
|
|
else
|
|
{
|
|
kfrst = dt_su * dt_ev; // bottom-left
|
|
kscan = dt_ev; // bottom
|
|
klast = dt_eu * dt_ev; // bottom-right
|
|
}
|
|
|
|
// 1st pixel, scanline, last pixel.
|
|
pdst[0] = ColorBlend(pdst[0], src.SampleInteger(u, v), kfrst);
|
|
u += ku;
|
|
|
|
int x;
|
|
for (x = 1; x < (src_width - 1); x++)
|
|
{
|
|
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), kscan);
|
|
u += ku;
|
|
}
|
|
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), klast);
|
|
}
|
|
else
|
|
{
|
|
pdst[0] = ColorBlend(pdst[0], src.SampleInteger(u, v), dt_su);
|
|
u += ku;
|
|
|
|
int x;
|
|
for (x = 1; x < (src_width - 1); x++)
|
|
{
|
|
pdst[x] = src.SampleInteger(u, v);
|
|
u += ku;
|
|
}
|
|
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), dt_eu);
|
|
}
|
|
|
|
pdst += dst.GetWidth();
|
|
v += kv;
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|