Files
Webcam/include/framework/picture/pict_blit.cpp

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;
}
//------------------------------------------------------------------------------