1437 lines
29 KiB
C++
1437 lines
29 KiB
C++
// Copyright (C) 2002-2008 Nikolaus Gebhardt / Thomas Alten
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "CImage.h"
|
|
#include "irrString.h"
|
|
#include "SoftwareDriver2_helper.h"
|
|
#include "CColorConverter.h"
|
|
|
|
namespace irr
|
|
{
|
|
|
|
struct SBlitJob
|
|
{
|
|
AbsRectangle Dest;
|
|
AbsRectangle Source;
|
|
|
|
u32 argb;
|
|
|
|
void * src;
|
|
void * dst;
|
|
|
|
s32 width;
|
|
s32 height;
|
|
|
|
u32 srcPitch;
|
|
u32 dstPitch;
|
|
|
|
u32 srcPixelMul;
|
|
u32 dstPixelMul;
|
|
};
|
|
|
|
// Blitter Operation
|
|
enum eBlitter
|
|
{
|
|
BLITTER_INVALID = 0,
|
|
BLITTER_COLOR,
|
|
BLITTER_COLOR_ALPHA,
|
|
BLITTER_TEXTURE,
|
|
BLITTER_TEXTURE_ALPHA_BLEND,
|
|
BLITTER_TEXTURE_ALPHA_COLOR_BLEND
|
|
};
|
|
|
|
typedef void (*tExecuteBlit) ( const SBlitJob * job );
|
|
|
|
|
|
// Bitfields Cohen Sutherland
|
|
enum eClipCode
|
|
{
|
|
CLIPCODE_EMPTY = 0,
|
|
CLIPCODE_BOTTOM = 1,
|
|
CLIPCODE_TOP = 2,
|
|
CLIPCODE_LEFT = 4,
|
|
CLIPCODE_RIGHT = 8
|
|
};
|
|
|
|
inline u32 GetClipCode ( const AbsRectangle &r, const core::position2d<s32> &p )
|
|
{
|
|
u32 code = CLIPCODE_EMPTY;
|
|
|
|
if ( p.X < r.x0 )
|
|
code = CLIPCODE_LEFT;
|
|
else
|
|
if ( p.X > r.x1 )
|
|
code = CLIPCODE_RIGHT;
|
|
|
|
if ( p.Y < r.y0 )
|
|
code |= CLIPCODE_TOP;
|
|
else
|
|
if ( p.Y > r.y1 )
|
|
code |= CLIPCODE_BOTTOM;
|
|
|
|
return code;
|
|
}
|
|
|
|
|
|
/*!
|
|
Cohen Sutherland clipping
|
|
@return: 1 if valid
|
|
*/
|
|
|
|
static int ClipLine (const AbsRectangle &clipping,
|
|
core::position2d<s32> &p0,
|
|
core::position2d<s32> &p1,
|
|
const core::position2d<s32>& p0_in,
|
|
const core::position2d<s32>& p1_in)
|
|
{
|
|
u32 code0;
|
|
u32 code1;
|
|
u32 code;
|
|
|
|
p0 = p0_in;
|
|
p1 = p1_in;
|
|
|
|
code0 = GetClipCode ( clipping, p0 );
|
|
code1 = GetClipCode ( clipping, p1 );
|
|
|
|
// trivial accepted
|
|
while ( code0 | code1 )
|
|
{
|
|
s32 x=0;
|
|
s32 y=0;
|
|
|
|
// trivial reject
|
|
if ( code0 & code1 )
|
|
return 0;
|
|
|
|
if ( code0 )
|
|
{
|
|
// clip first point
|
|
code = code0;
|
|
}
|
|
else
|
|
{
|
|
// clip last point
|
|
code = code1;
|
|
}
|
|
|
|
if ( (code & CLIPCODE_BOTTOM) == CLIPCODE_BOTTOM )
|
|
{
|
|
// clip bottom viewport
|
|
y = clipping.y1;
|
|
x = p0.X + ( p1.X - p0.X ) * ( y - p0.Y ) / ( p1.Y - p0.Y );
|
|
}
|
|
else
|
|
if ( (code & CLIPCODE_TOP) == CLIPCODE_TOP )
|
|
{
|
|
// clip to viewport
|
|
y = clipping.y0;
|
|
x = p0.X + ( p1.X - p0.X ) * ( y - p0.Y ) / ( p1.Y - p0.Y );
|
|
}
|
|
else
|
|
if ( (code & CLIPCODE_RIGHT) == CLIPCODE_RIGHT )
|
|
{
|
|
// clip right viewport
|
|
x = clipping.x1;
|
|
y = p0.Y + ( p1.Y - p0.Y ) * ( x - p0.X ) / ( p1.X - p0.X );
|
|
}
|
|
else
|
|
if ( (code & CLIPCODE_LEFT) == CLIPCODE_LEFT )
|
|
{
|
|
// clip left viewport
|
|
x = clipping.x0;
|
|
y = p0.Y + ( p1.Y - p0.Y ) * ( x - p0.X ) / ( p1.X - p0.X );
|
|
}
|
|
|
|
if ( code == code0 )
|
|
{
|
|
// modify first point
|
|
p0.X = x;
|
|
p0.Y = y;
|
|
code0 = GetClipCode ( clipping, p0 );
|
|
}
|
|
else
|
|
{
|
|
// modify second point
|
|
p1.X = x;
|
|
p1.Y = y;
|
|
code1 = GetClipCode ( clipping, p1 );
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
inline void GetClip(AbsRectangle &clipping, video::IImage * t)
|
|
{
|
|
clipping.x0 = 0;
|
|
clipping.y0 = 0;
|
|
clipping.x1 = t->getDimension().Width - 1;
|
|
clipping.y1 = t->getDimension().Height - 1;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
static void RenderLine32_Decal(video::IImage *t,
|
|
const core::position2d<s32> &p0,
|
|
const core::position2d<s32> &p1,
|
|
u32 argb )
|
|
{
|
|
s32 dx = p1.X - p0.X;
|
|
s32 dy = p1.Y - p0.Y;
|
|
|
|
s32 c;
|
|
s32 m;
|
|
s32 d = 0;
|
|
s32 run;
|
|
|
|
s32 xInc = 4;
|
|
s32 yInc = (s32) t->getPitch();
|
|
|
|
if ( dx < 0 )
|
|
{
|
|
xInc = -xInc;
|
|
dx = -dx;
|
|
}
|
|
|
|
if ( dy < 0 )
|
|
{
|
|
yInc = -yInc;
|
|
dy = -dy;
|
|
}
|
|
|
|
u32 *dst;
|
|
dst = (u32*) ( (u8*) t->lock() + ( p0.Y * t->getPitch() ) + ( p0.X << 2 ) );
|
|
|
|
if ( dy > dx )
|
|
{
|
|
s32 tmp;
|
|
tmp = dx;
|
|
dx = dy;
|
|
dy = tmp;
|
|
tmp = xInc;
|
|
xInc = yInc;
|
|
yInc = tmp;
|
|
}
|
|
|
|
c = dx << 1;
|
|
m = dy << 1;
|
|
|
|
run = dx;
|
|
while ( run )
|
|
{
|
|
*dst = argb;
|
|
|
|
dst = (u32*) ( (u8*) dst + xInc ); // x += xInc
|
|
d += m;
|
|
if ( d > dx )
|
|
{
|
|
dst = (u32*) ( (u8*) dst + yInc ); // y += yInc
|
|
d -= c;
|
|
}
|
|
run -= 1;
|
|
}
|
|
|
|
t->unlock();
|
|
}
|
|
|
|
|
|
/*
|
|
*/
|
|
static void RenderLine32_Blend(video::IImage *t,
|
|
const core::position2d<s32> &p0,
|
|
const core::position2d<s32> &p1,
|
|
u32 argb, u32 alpha)
|
|
{
|
|
s32 dx = p1.X - p0.X;
|
|
s32 dy = p1.Y - p0.Y;
|
|
|
|
s32 c;
|
|
s32 m;
|
|
s32 d = 0;
|
|
s32 run;
|
|
|
|
s32 xInc = 4;
|
|
s32 yInc = (s32) t->getPitch();
|
|
|
|
if ( dx < 0 )
|
|
{
|
|
xInc = -xInc;
|
|
dx = -dx;
|
|
}
|
|
|
|
if ( dy < 0 )
|
|
{
|
|
yInc = -yInc;
|
|
dy = -dy;
|
|
}
|
|
|
|
u32 *dst;
|
|
dst = (u32*) ( (u8*) t->lock() + ( p0.Y * t->getPitch() ) + ( p0.X << 2 ) );
|
|
|
|
if ( dy > dx )
|
|
{
|
|
s32 tmp;
|
|
tmp = dx;
|
|
dx = dy;
|
|
dy = tmp;
|
|
tmp = xInc;
|
|
xInc = yInc;
|
|
yInc = tmp;
|
|
}
|
|
|
|
c = dx << 1;
|
|
m = dy << 1;
|
|
|
|
run = dx;
|
|
while ( run )
|
|
{
|
|
*dst = PixelBlend32 ( *dst, argb, alpha );
|
|
|
|
dst = (u32*) ( (u8*) dst + xInc ); // x += xInc
|
|
d += m;
|
|
if ( d > dx )
|
|
{
|
|
dst = (u32*) ( (u8*) dst + yInc ); // y += yInc
|
|
d -= c;
|
|
}
|
|
run -= 1;
|
|
}
|
|
|
|
t->unlock();
|
|
}
|
|
|
|
/*
|
|
*/
|
|
static void RenderLine16_Decal (video::IImage *t,
|
|
const core::position2d<s32> &p0,
|
|
const core::position2d<s32> &p1,
|
|
u32 argb )
|
|
{
|
|
s32 dx = p1.X - p0.X;
|
|
s32 dy = p1.Y - p0.Y;
|
|
|
|
s32 c;
|
|
s32 m;
|
|
s32 d = 0;
|
|
s32 run;
|
|
|
|
s32 xInc = 2;
|
|
s32 yInc = (s32) t->getPitch();
|
|
|
|
if ( dx < 0 )
|
|
{
|
|
xInc = -xInc;
|
|
dx = -dx;
|
|
}
|
|
|
|
if ( dy < 0 )
|
|
{
|
|
yInc = -yInc;
|
|
dy = -dy;
|
|
}
|
|
|
|
u16 *dst;
|
|
dst = (u16*) ( (u8*) t->lock() + ( p0.Y * t->getPitch() ) + ( p0.X << 1 ) );
|
|
|
|
if ( dy > dx )
|
|
{
|
|
s32 tmp;
|
|
tmp = dx;
|
|
dx = dy;
|
|
dy = tmp;
|
|
tmp = xInc;
|
|
xInc = yInc;
|
|
yInc = tmp;
|
|
}
|
|
|
|
c = dx << 1;
|
|
m = dy << 1;
|
|
|
|
run = dx;
|
|
while ( run )
|
|
{
|
|
*dst = argb;
|
|
|
|
dst = (u16*) ( (u8*) dst + xInc ); // x += xInc
|
|
d += m;
|
|
if ( d > dx )
|
|
{
|
|
dst = (u16*) ( (u8*) dst + yInc ); // y += yInc
|
|
d -= c;
|
|
}
|
|
run -= 1;
|
|
}
|
|
|
|
t->unlock();
|
|
}
|
|
|
|
/*
|
|
*/
|
|
static void RenderLine16_Blend (video::IImage *t,
|
|
const core::position2d<s32> &p0,
|
|
const core::position2d<s32> &p1,
|
|
u32 argb,
|
|
u32 alpha)
|
|
{
|
|
s32 dx = p1.X - p0.X;
|
|
s32 dy = p1.Y - p0.Y;
|
|
|
|
s32 c;
|
|
s32 m;
|
|
s32 d = 0;
|
|
s32 run;
|
|
|
|
s32 xInc = 2;
|
|
s32 yInc = (s32) t->getPitch();
|
|
|
|
if ( dx < 0 )
|
|
{
|
|
xInc = -xInc;
|
|
dx = -dx;
|
|
}
|
|
|
|
if ( dy < 0 )
|
|
{
|
|
yInc = -yInc;
|
|
dy = -dy;
|
|
}
|
|
|
|
u16 *dst;
|
|
dst = (u16*) ( (u8*) t->lock() + ( p0.Y * t->getPitch() ) + ( p0.X << 1 ) );
|
|
|
|
if ( dy > dx )
|
|
{
|
|
s32 tmp;
|
|
tmp = dx;
|
|
dx = dy;
|
|
dy = tmp;
|
|
tmp = xInc;
|
|
xInc = yInc;
|
|
yInc = tmp;
|
|
}
|
|
|
|
c = dx << 1;
|
|
m = dy << 1;
|
|
|
|
run = dx;
|
|
while ( run )
|
|
{
|
|
*dst = PixelBlend16 ( *dst, argb, alpha );
|
|
|
|
dst = (u16*) ( (u8*) dst + xInc ); // x += xInc
|
|
d += m;
|
|
if ( d > dx )
|
|
{
|
|
dst = (u16*) ( (u8*) dst + yInc ); // y += yInc
|
|
d -= c;
|
|
}
|
|
run -= 1;
|
|
}
|
|
|
|
t->unlock();
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureCopy_x_to_x ( const SBlitJob * job )
|
|
{
|
|
const void *src = (void*) job->src;
|
|
void *dst = (void*) job->dst;
|
|
|
|
const u32 widthPitch = job->width * job->dstPixelMul;
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
memcpy ( dst, src, widthPitch );
|
|
|
|
src = (void*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (void*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureCopy_32_to_16 ( const SBlitJob * job )
|
|
{
|
|
const u32 *src = static_cast<const u32*>(job->src);
|
|
u16 *dst = static_cast<u16*>(job->dst);
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
//16 bit Blitter depends on pre-multiplied color
|
|
const u32 s = PixelLerp32 ( src[dx] | 0xFF000000, extractAlpha ( src[dx] ) );
|
|
dst[dx] = video::A8R8G8B8toA1R5G5B5 ( s );
|
|
}
|
|
|
|
src = (u32*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u16*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureCopy_24_to_16 ( const SBlitJob * job )
|
|
{
|
|
const void *src = (void*) job->src;
|
|
u16 *dst = (u16*) job->dst;
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
u8 * s = (u8*) src;
|
|
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
dst[dx] = video::RGB16(s[0], s[1], s[2]);
|
|
s += 3;
|
|
}
|
|
|
|
src = (void*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u16*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureCopy_16_to_32 ( const SBlitJob * job )
|
|
{
|
|
const u16 *src = (u16*) job->src;
|
|
u32 *dst = (u32*) job->dst;
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
dst[dx] = video::A1R5G5B5toA8R8G8B8 ( src[dx] );
|
|
}
|
|
|
|
src = (u16*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureCopy_24_to_32 ( const SBlitJob * job )
|
|
{
|
|
void *src = (void*) job->src;
|
|
u32 *dst = (u32*) job->dst;
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
u8 * s = (u8*) src;
|
|
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
dst[dx] = 0xFF000000 | s[0] << 16 | s[1] << 8 | s[2];
|
|
s += 3;
|
|
}
|
|
|
|
src = (void*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureBlend_16_to_16 ( const SBlitJob * job )
|
|
{
|
|
u32 dx;
|
|
s32 dy;
|
|
|
|
u32 *src = (u32*) job->src;
|
|
u32 *dst = (u32*) job->dst;
|
|
|
|
|
|
const u32 rdx = job->width >> 1;
|
|
const u32 off = core::if_c_a_else_b ( job->width & 1 ,job->width - 1, 0 );
|
|
|
|
|
|
if ( 0 == off )
|
|
{
|
|
for ( dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( dx = 0; dx != rdx; ++dx )
|
|
{
|
|
dst[dx] = PixelBlend16_simd ( dst[dx], src[dx] );
|
|
}
|
|
|
|
src = (u32*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
for ( dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( dx = 0; dx != rdx; ++dx )
|
|
{
|
|
dst[dx] = PixelBlend16_simd ( dst[dx], src[dx] );
|
|
}
|
|
|
|
((u16*) dst)[off] = PixelBlend16 ( ((u16*) dst)[off], ((u16*) src)[off] );
|
|
|
|
src = (u32*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureBlend_32_to_32 ( const SBlitJob * job )
|
|
{
|
|
u32 *src = (u32*) job->src;
|
|
u32 *dst = (u32*) job->dst;
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
dst[dx] = PixelBlend32 ( dst[dx], src[dx] );
|
|
}
|
|
src = (u32*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureBlendColor_16_to_16 ( const SBlitJob * job )
|
|
{
|
|
u16 *src = (u16*) job->src;
|
|
u16 *dst = (u16*) job->dst;
|
|
|
|
u16 blend = video::A8R8G8B8toA1R5G5B5 ( job->argb );
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
dst[dx] = PixelBlend16 ( dst[dx], PixelMul16_2 ( src[dx], blend ) );
|
|
}
|
|
src = (u16*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u16*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_TextureBlendColor_32_to_32 ( const SBlitJob * job )
|
|
{
|
|
u32 *src = (u32*) job->src;
|
|
u32 *dst = (u32*) job->dst;
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
dst[dx] = PixelBlend32 ( dst[dx], PixelMul32_2 ( src[dx], job->argb ) );
|
|
}
|
|
src = (u32*) ( (u8*) (src) + job->srcPitch );
|
|
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_Color_16_to_16 ( const SBlitJob * job )
|
|
{
|
|
u16 *dst = (u16*) job->dst;
|
|
|
|
u16 c0 = video::A8R8G8B8toA1R5G5B5 ( job->argb );
|
|
u32 c = c0 | c0 << 16;
|
|
|
|
if ( 0 == (job->srcPitch & 3 ) )
|
|
{
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
memset32 ( dst, c, job->srcPitch );
|
|
dst = (u16*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s32 dx = job->width - 1;
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
memset32 ( dst, c, job->srcPitch );
|
|
dst[dx] = c0;
|
|
dst = (u16*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_Color_32_to_32 ( const SBlitJob * job )
|
|
{
|
|
u32 *dst = (u32*) job->dst;
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
memset32 ( dst, job->argb, job->srcPitch );
|
|
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_ColorAlpha_16_to_16 ( const SBlitJob * job )
|
|
{
|
|
u16 *dst = (u16*) job->dst;
|
|
|
|
const u32 alpha = extractAlpha ( job->argb ) >> 3;
|
|
const u32 src = video::A8R8G8B8toA1R5G5B5 ( job->argb );
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
dst[dx] = PixelBlend16 ( dst[dx], src, alpha );
|
|
}
|
|
dst = (u16*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void executeBlit_ColorAlpha_32_to_32 ( const SBlitJob * job )
|
|
{
|
|
u32 *dst = (u32*) job->dst;
|
|
|
|
const u32 alpha = extractAlpha ( job->argb );
|
|
const u32 src = job->argb;
|
|
|
|
for ( s32 dy = 0; dy != job->height; ++dy )
|
|
{
|
|
for ( s32 dx = 0; dx != job->width; ++dx )
|
|
{
|
|
dst[dx] = PixelBlend32 ( dst[dx], src, alpha );
|
|
}
|
|
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static tExecuteBlit getBlitter ( eBlitter operation,const video::IImage * dest,const video::IImage * source )
|
|
{
|
|
video::ECOLOR_FORMAT sourceFormat = (video::ECOLOR_FORMAT) -1;
|
|
video::ECOLOR_FORMAT destFormat = (video::ECOLOR_FORMAT) -1;
|
|
|
|
if ( source )
|
|
sourceFormat = source->getColorFormat ();
|
|
|
|
if ( dest )
|
|
destFormat = dest->getColorFormat ();
|
|
|
|
switch ( operation )
|
|
{
|
|
case BLITTER_TEXTURE:
|
|
{
|
|
if ( sourceFormat == destFormat )
|
|
return executeBlit_TextureCopy_x_to_x;
|
|
|
|
if ( destFormat == video::ECF_A1R5G5B5 && sourceFormat == video::ECF_A8R8G8B8 )
|
|
return executeBlit_TextureCopy_32_to_16;
|
|
|
|
if ( destFormat == video::ECF_A1R5G5B5 && sourceFormat == video::ECF_R8G8B8 )
|
|
return executeBlit_TextureCopy_24_to_16;
|
|
|
|
if ( destFormat == video::ECF_A8R8G8B8 && sourceFormat == video::ECF_A1R5G5B5 )
|
|
return executeBlit_TextureCopy_16_to_32;
|
|
|
|
if ( destFormat == video::ECF_A8R8G8B8 && sourceFormat == video::ECF_R8G8B8 )
|
|
return executeBlit_TextureCopy_24_to_32;
|
|
|
|
} break;
|
|
|
|
case BLITTER_TEXTURE_ALPHA_BLEND:
|
|
{
|
|
if ( destFormat == video::ECF_A1R5G5B5 && sourceFormat == video::ECF_A1R5G5B5 )
|
|
return executeBlit_TextureBlend_16_to_16;
|
|
|
|
if ( destFormat == video::ECF_A8R8G8B8 && sourceFormat == video::ECF_A8R8G8B8 )
|
|
return executeBlit_TextureBlend_32_to_32;
|
|
|
|
} break;
|
|
|
|
case BLITTER_TEXTURE_ALPHA_COLOR_BLEND:
|
|
{
|
|
if ( destFormat == video::ECF_A1R5G5B5 && sourceFormat == video::ECF_A1R5G5B5 )
|
|
return executeBlit_TextureBlendColor_16_to_16;
|
|
|
|
if ( destFormat == video::ECF_A8R8G8B8 && sourceFormat == video::ECF_A8R8G8B8 )
|
|
return executeBlit_TextureBlendColor_32_to_32;
|
|
} break;
|
|
|
|
case BLITTER_COLOR:
|
|
{
|
|
if ( destFormat == video::ECF_A1R5G5B5 )
|
|
return executeBlit_Color_16_to_16;
|
|
|
|
if ( destFormat == video::ECF_A8R8G8B8 )
|
|
return executeBlit_Color_32_to_32;
|
|
} break;
|
|
|
|
case BLITTER_COLOR_ALPHA:
|
|
{
|
|
if ( destFormat == video::ECF_A1R5G5B5 )
|
|
return executeBlit_ColorAlpha_16_to_16;
|
|
|
|
if ( destFormat == video::ECF_A8R8G8B8 )
|
|
return executeBlit_ColorAlpha_32_to_32;
|
|
|
|
} break;
|
|
|
|
case BLITTER_INVALID:
|
|
break;
|
|
}
|
|
/*
|
|
char buf[64];
|
|
sprintf ( buf, "Blit: %d %d->%d unsupported",operation,sourceFormat,destFormat );
|
|
os::Printer::log (buf );
|
|
*/
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
a generic 2D Blitter
|
|
*/
|
|
static s32 Blit ( eBlitter operation,
|
|
video::IImage * dest,
|
|
const core::rect<s32> *destClipping,
|
|
const core::position2d<s32> *destPos,
|
|
video::IImage * const source,
|
|
const core::rect<s32> *sourceClipping,
|
|
u32 argb
|
|
)
|
|
{
|
|
tExecuteBlit blitter = getBlitter ( operation, dest, source );
|
|
if ( 0 == blitter )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Clipping
|
|
AbsRectangle sourceClip;
|
|
AbsRectangle destClip;
|
|
AbsRectangle v;
|
|
|
|
SBlitJob job;
|
|
|
|
if ( sourceClipping )
|
|
{
|
|
sourceClip.x0 = sourceClipping->UpperLeftCorner.X;
|
|
sourceClip.y0 = sourceClipping->UpperLeftCorner.Y;
|
|
sourceClip.x1 = sourceClipping->LowerRightCorner.X;
|
|
sourceClip.y1 = sourceClipping->LowerRightCorner.Y;
|
|
}
|
|
else
|
|
{
|
|
sourceClip.x0 = 0;
|
|
sourceClip.y0 = 0;
|
|
sourceClip.x1 = source ? source->getDimension().Width : 0;
|
|
sourceClip.y1 = source ? source->getDimension().Height : 0;
|
|
}
|
|
|
|
if ( destClipping )
|
|
{
|
|
destClip.x0 = destClipping->UpperLeftCorner.X;
|
|
destClip.y0 = destClipping->UpperLeftCorner.Y;
|
|
destClip.x1 = destClipping->LowerRightCorner.X;
|
|
destClip.y1 = destClipping->LowerRightCorner.Y;
|
|
}
|
|
else
|
|
{
|
|
destClip.x0 = 0;
|
|
destClip.y0 = 0;
|
|
destClip.x1 = dest ? dest->getDimension().Width : 0;
|
|
destClip.y1 = dest ? dest->getDimension().Height : 0;
|
|
}
|
|
|
|
v.x0 = destPos ? destPos->X : 0;
|
|
v.y0 = destPos ? destPos->Y : 0;
|
|
v.x1 = v.x0 + ( sourceClip.x1 - sourceClip.x0 );
|
|
v.y1 = v.y0 + ( sourceClip.y1 - sourceClip.y0 );
|
|
|
|
intersect ( job.Dest, destClip, v );
|
|
if ( !isValid ( job.Dest ) )
|
|
return 0;
|
|
|
|
job.width = job.Dest.x1 - job.Dest.x0;
|
|
job.height = job.Dest.y1 - job.Dest.y0;
|
|
|
|
|
|
job.Source.x0 = sourceClip.x0 + ( job.Dest.x0 - v.x0 );
|
|
job.Source.x1 = job.Source.x0 + job.width;
|
|
|
|
job.Source.y0 = sourceClip.y0 + ( job.Dest.y0 - v.y0 );
|
|
job.Source.y1 = job.Source.y0 + job.height;
|
|
|
|
job.argb = argb;
|
|
|
|
if ( source )
|
|
{
|
|
job.srcPitch = source->getPitch();
|
|
job.srcPixelMul = source->getBytesPerPixel();
|
|
job.src = (void*) ( (u8*) source->lock() + ( job.Source.y0 * job.srcPitch ) + ( job.Source.x0 * job.srcPixelMul ) );
|
|
}
|
|
else
|
|
{
|
|
// use srcPitch for color operation on dest
|
|
job.srcPitch = job.width * dest->getBytesPerPixel();
|
|
}
|
|
|
|
job.dstPitch = dest->getPitch();
|
|
job.dstPixelMul = dest->getBytesPerPixel();
|
|
job.dst = (void*) ( (u8*) dest->lock() + ( job.Dest.y0 * job.dstPitch ) + ( job.Dest.x0 * job.dstPixelMul ) );
|
|
|
|
blitter ( &job );
|
|
|
|
if ( source )
|
|
source->unlock ();
|
|
|
|
if ( dest )
|
|
dest->unlock();
|
|
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
|
|
//! constructor
|
|
CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<s32>& size)
|
|
:Data(0), Size(size), Format(format), DeleteMemory(true)
|
|
{
|
|
initData();
|
|
}
|
|
|
|
|
|
//! constructor
|
|
CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<s32>& size, void* data,
|
|
bool ownForeignMemory, bool deleteForeignMemory)
|
|
: Data(0), Size(size), Format(format), DeleteMemory(deleteForeignMemory)
|
|
{
|
|
if (ownForeignMemory)
|
|
{
|
|
Data = (void*)0xbadf00d;
|
|
initData();
|
|
Data = data;
|
|
}
|
|
else
|
|
{
|
|
Data = 0;
|
|
initData();
|
|
memcpy(Data, data, Size.Height * Pitch);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//! constructor
|
|
CImage::CImage(ECOLOR_FORMAT format, IImage* imageToCopy)
|
|
: Data(0), Format(format), DeleteMemory(true)
|
|
{
|
|
if (!imageToCopy)
|
|
return;
|
|
|
|
Size = imageToCopy->getDimension();
|
|
initData();
|
|
|
|
// now copy data from other image
|
|
|
|
Blit ( BLITTER_TEXTURE, this, 0, 0, imageToCopy, 0,0 );
|
|
}
|
|
|
|
|
|
|
|
//! constructor
|
|
CImage::CImage(IImage* imageToCopy, const core::position2d<s32>& pos,
|
|
const core::dimension2d<s32>& size)
|
|
: Data(0), Size(0,0), DeleteMemory(true)
|
|
{
|
|
if (!imageToCopy)
|
|
return;
|
|
|
|
Format = imageToCopy->getColorFormat();
|
|
Size = size;
|
|
|
|
initData();
|
|
|
|
core::rect<s32> sClip( pos.X, pos.Y, pos.X + size.Width,pos.Y + size.Height );
|
|
Blit ( BLITTER_TEXTURE, this, 0, 0, imageToCopy, &sClip,0 );
|
|
}
|
|
|
|
|
|
|
|
//! assumes format and size has been set and creates the rest
|
|
void CImage::initData()
|
|
{
|
|
setBitMasks();
|
|
BitsPerPixel = getBitsPerPixelFromFormat(Format);
|
|
BytesPerPixel = BitsPerPixel / 8;
|
|
|
|
// Pitch should be aligned...
|
|
Pitch = BytesPerPixel * Size.Width;
|
|
|
|
if (!Data)
|
|
Data = new s8[Size.Height * Pitch];
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CImage::~CImage()
|
|
{
|
|
if ( DeleteMemory )
|
|
delete [] (s8*)Data;
|
|
}
|
|
|
|
|
|
//! Returns width and height of image data.
|
|
const core::dimension2d<s32>& CImage::getDimension() const
|
|
{
|
|
return Size;
|
|
}
|
|
|
|
|
|
|
|
//! Returns bits per pixel.
|
|
u32 CImage::getBitsPerPixel() const
|
|
{
|
|
return BitsPerPixel;
|
|
}
|
|
|
|
|
|
//! Returns bytes per pixel
|
|
u32 CImage::getBytesPerPixel() const
|
|
{
|
|
return BytesPerPixel;
|
|
}
|
|
|
|
|
|
|
|
//! Returns image data size in bytes
|
|
u32 CImage::getImageDataSizeInBytes() const
|
|
{
|
|
return Pitch * Size.Height;
|
|
}
|
|
|
|
|
|
|
|
//! Returns image data size in pixels
|
|
u32 CImage::getImageDataSizeInPixels() const
|
|
{
|
|
return Size.Width * Size.Height;
|
|
}
|
|
|
|
|
|
|
|
//! returns mask for red value of a pixel
|
|
u32 CImage::getRedMask() const
|
|
{
|
|
return RedMask;
|
|
}
|
|
|
|
|
|
|
|
//! returns mask for green value of a pixel
|
|
u32 CImage::getGreenMask() const
|
|
{
|
|
return GreenMask;
|
|
}
|
|
|
|
|
|
|
|
//! returns mask for blue value of a pixel
|
|
u32 CImage::getBlueMask() const
|
|
{
|
|
return BlueMask;
|
|
}
|
|
|
|
|
|
|
|
//! returns mask for alpha value of a pixel
|
|
u32 CImage::getAlphaMask() const
|
|
{
|
|
return AlphaMask;
|
|
}
|
|
|
|
|
|
void CImage::setBitMasks()
|
|
{
|
|
switch(Format)
|
|
{
|
|
case ECF_A1R5G5B5:
|
|
AlphaMask = 0x1<<15;
|
|
RedMask = 0x1F<<10;
|
|
GreenMask = 0x1F<<5;
|
|
BlueMask = 0x1F;
|
|
break;
|
|
case ECF_R5G6B5:
|
|
AlphaMask = 0x0;
|
|
RedMask = 0x1F<<11;
|
|
GreenMask = 0x3F<<5;
|
|
BlueMask = 0x1F;
|
|
break;
|
|
case ECF_R8G8B8:
|
|
AlphaMask = 0x0;
|
|
RedMask = 0x00FF0000;
|
|
GreenMask = 0x0000FF00;
|
|
BlueMask = 0x000000FF;
|
|
break;
|
|
case ECF_A8R8G8B8:
|
|
AlphaMask = 0xFF000000;
|
|
RedMask = 0x00FF0000;
|
|
GreenMask = 0x0000FF00;
|
|
BlueMask = 0x000000FF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
u32 CImage::getBitsPerPixelFromFormat(ECOLOR_FORMAT format)
|
|
{
|
|
switch(format)
|
|
{
|
|
case ECF_A1R5G5B5:
|
|
return 16;
|
|
case ECF_R5G6B5:
|
|
return 16;
|
|
case ECF_R8G8B8:
|
|
return 24;
|
|
case ECF_A8R8G8B8:
|
|
return 32;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//! sets a pixel
|
|
void CImage::setPixel(u32 x, u32 y, const SColor &color )
|
|
{
|
|
if (x >= (u32)Size.Width || y >= (u32)Size.Height)
|
|
return;
|
|
|
|
switch(Format)
|
|
{
|
|
case ECF_A1R5G5B5:
|
|
{
|
|
u16 * dest = (u16*) ((u8*) Data + ( y * Pitch ) + ( x << 1 ));
|
|
*dest = video::A8R8G8B8toA1R5G5B5 ( color.color );
|
|
} break;
|
|
|
|
case ECF_R5G6B5:
|
|
{
|
|
u16 * dest = (u16*) ((u8*) Data + ( y * Pitch ) + ( x << 1 ));
|
|
*dest = video::A8R8G8B8toR5G6B5 ( color.color );
|
|
} break;
|
|
|
|
case ECF_R8G8B8:
|
|
{
|
|
u8* dest = (u8*) Data + ( y * Pitch ) + ( x * 3 );
|
|
dest[0] = color.getRed();
|
|
dest[1] = color.getGreen();
|
|
dest[2] = color.getBlue();
|
|
} break;
|
|
|
|
case ECF_A8R8G8B8:
|
|
{
|
|
u32 * dest = (u32*) ((u8*) Data + ( y * Pitch ) + ( x << 2 ));
|
|
*dest = color.color;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
|
|
//! returns a pixel
|
|
SColor CImage::getPixel(u32 x, u32 y) const
|
|
{
|
|
if (x >= (u32)Size.Width || y >= (u32)Size.Height)
|
|
return SColor(0);
|
|
|
|
switch(Format)
|
|
{
|
|
case ECF_A1R5G5B5:
|
|
return A1R5G5B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]);
|
|
case ECF_R5G6B5:
|
|
return R5G6B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]);
|
|
case ECF_A8R8G8B8:
|
|
return ((u32*)Data)[y*Size.Width + x];
|
|
case ECF_R8G8B8:
|
|
{
|
|
u8* p = &((u8*)Data)[(y*3)*Size.Width + (x*3)];
|
|
return SColor(255,p[0],p[1],p[2]);
|
|
}
|
|
}
|
|
|
|
return SColor(0);
|
|
}
|
|
|
|
|
|
//! returns the color format
|
|
ECOLOR_FORMAT CImage::getColorFormat() const
|
|
{
|
|
return Format;
|
|
}
|
|
|
|
//! draws a rectangle
|
|
void CImage::drawRectangle(const core::rect<s32>& rect, const SColor &color)
|
|
{
|
|
Blit ( color.getAlpha() == 0xFF ? BLITTER_COLOR : BLITTER_COLOR_ALPHA,
|
|
this, 0, &rect.UpperLeftCorner, 0, &rect, color.color );
|
|
}
|
|
|
|
|
|
//! copies this surface into another
|
|
void CImage::copyTo(IImage* target, const core::position2d<s32>& pos)
|
|
{
|
|
Blit ( BLITTER_TEXTURE, target, 0, &pos, this, 0, 0 );
|
|
}
|
|
|
|
|
|
//! copies this surface into another
|
|
void CImage::copyTo(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect)
|
|
{
|
|
Blit ( BLITTER_TEXTURE, target, clipRect, &pos, this, &sourceRect, 0 );
|
|
}
|
|
|
|
|
|
|
|
//! copies this surface into another, using the alpha mask, an cliprect and a color to add with
|
|
void CImage::copyToWithAlpha(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const SColor &color, const core::rect<s32>* clipRect)
|
|
{
|
|
// color blend only necessary on not full spectrum aka. color.color != 0xFFFFFFFF
|
|
Blit ( color.color == 0xFFFFFFFF ? BLITTER_TEXTURE_ALPHA_BLEND: BLITTER_TEXTURE_ALPHA_COLOR_BLEND,
|
|
target, clipRect, &pos, this, &sourceRect, color.color );
|
|
}
|
|
|
|
|
|
|
|
//! draws a line from to with color
|
|
void CImage::drawLine(const core::position2d<s32>& from, const core::position2d<s32>& to, const SColor &color)
|
|
{
|
|
AbsRectangle clip;
|
|
GetClip ( clip, this );
|
|
|
|
core::position2d<s32> p[2];
|
|
|
|
if ( ClipLine ( clip, p[0], p[1], from, to ) )
|
|
{
|
|
u32 alpha = extractAlpha ( color.color );
|
|
|
|
switch ( Format )
|
|
{
|
|
case ECF_A1R5G5B5:
|
|
if ( alpha == 256 )
|
|
{
|
|
RenderLine16_Decal ( this, p[0], p[1], video::A8R8G8B8toA1R5G5B5 ( color.color ) );
|
|
}
|
|
else
|
|
{
|
|
RenderLine16_Blend ( this, p[0], p[1], video::A8R8G8B8toA1R5G5B5 ( color.color ), alpha >> 3 );
|
|
}
|
|
break;
|
|
case ECF_A8R8G8B8:
|
|
if ( alpha == 256 )
|
|
{
|
|
RenderLine32_Decal ( this, p[0], p[1], color.color );
|
|
}
|
|
else
|
|
{
|
|
RenderLine32_Blend ( this, p[0], p[1], color.color, alpha );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//! copies this surface into another, scaling it to the target image size
|
|
// note: this is very very slow. (i didn't want to write a fast version.
|
|
// but hopefully, nobody wants to scale surfaces every frame.
|
|
void CImage::copyToScaling(void* target, s32 width, s32 height, ECOLOR_FORMAT format, u32 pitch)
|
|
{
|
|
if (!target || !width || !height)
|
|
return;
|
|
|
|
const u32 bpp=getBitsPerPixelFromFormat(format)/8;
|
|
if (0==pitch)
|
|
pitch = width*bpp;
|
|
|
|
if (Format==format && Size.Width==width && Size.Height==height)
|
|
{
|
|
if (pitch==Pitch)
|
|
{
|
|
memcpy(target, Data, height*pitch);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
u8* tgtpos = (u8*) target;
|
|
u8* dstpos = (u8*) Data;
|
|
const u32 bwidth = width*bpp;
|
|
for (s32 y=0; y<height; ++y)
|
|
{
|
|
memcpy(target, Data, height*pitch);
|
|
memset(tgtpos+width, 0, pitch-bwidth);
|
|
tgtpos += pitch;
|
|
dstpos += Pitch;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
const f32 sourceXStep = (f32)Size.Width / (f32)width;
|
|
const f32 sourceYStep = (f32)Size.Height / (f32)height;
|
|
s32 yval=0, syval=0;
|
|
f32 sy = 0.0f;
|
|
for (s32 y=0; y<height; ++y)
|
|
{
|
|
f32 sx = 0.0f;
|
|
for (s32 x=0; x<width; ++x)
|
|
{
|
|
CColorConverter::convert_viaFormat(((u8*)Data)+ syval + ((s32)sx)*BytesPerPixel, Format, 1, ((u8*)target)+ yval + (x*bpp), format);
|
|
sx+=sourceXStep;
|
|
}
|
|
sy+=sourceYStep;
|
|
syval=((s32)sy)*Pitch;
|
|
yval+=pitch;
|
|
}
|
|
}
|
|
|
|
//! copies this surface into another, scaling it to the target image size
|
|
// note: this is very very slow. (i didn't want to write a fast version.
|
|
// but hopefully, nobody wants to scale surfaces every frame.
|
|
void CImage::copyToScaling(IImage* target)
|
|
{
|
|
if (!target)
|
|
return;
|
|
|
|
const core::dimension2d<s32>& targetSize = target->getDimension();
|
|
|
|
if (targetSize==Size)
|
|
{
|
|
copyTo(target);
|
|
return;
|
|
}
|
|
|
|
copyToScaling(target->lock(), targetSize.Width, targetSize.Height, target->getColorFormat());
|
|
target->unlock();
|
|
}
|
|
|
|
//! copies this surface into another, scaling it to fit it.
|
|
void CImage::copyToScalingBoxFilter(IImage* target, s32 bias)
|
|
{
|
|
const core::dimension2d<s32> destSize = target->getDimension();
|
|
|
|
const f32 sourceXStep = (f32) Size.Width / (f32) destSize.Width;
|
|
const f32 sourceYStep = (f32) Size.Height / (f32) destSize.Height;
|
|
|
|
target->lock();
|
|
|
|
s32 fx = core::ceil32 ( sourceXStep );
|
|
s32 fy = core::ceil32 ( sourceYStep );
|
|
f32 sx;
|
|
f32 sy;
|
|
|
|
sy = 0.f;
|
|
for ( s32 y = 0; y != destSize.Height; ++y )
|
|
{
|
|
sx = 0.f;
|
|
for ( s32 x = 0; x != destSize.Width; ++x )
|
|
{
|
|
target->setPixel ( x, y, getPixelBox( core::floor32 ( sx ), core::floor32 ( sy ), fx, fy, bias ) );
|
|
sx += sourceXStep;
|
|
}
|
|
sy += sourceYStep;
|
|
}
|
|
|
|
target->unlock ();
|
|
}
|
|
|
|
|
|
//! fills the surface with given color
|
|
void CImage::fill(const SColor &color)
|
|
{
|
|
u32 c;
|
|
|
|
switch ( Format )
|
|
{
|
|
case ECF_A1R5G5B5:
|
|
c = video::A8R8G8B8toA1R5G5B5 ( color.color );
|
|
c |= c << 16;
|
|
break;
|
|
case ECF_R5G6B5:
|
|
c = video::A8R8G8B8toR5G6B5 ( color.color );
|
|
c |= c << 16;
|
|
break;
|
|
case ECF_A8R8G8B8:
|
|
c = color.color;
|
|
break;
|
|
default:
|
|
// os::Printer::log("CImage::Format not supported", ELL_ERROR);
|
|
return;
|
|
}
|
|
|
|
memset32 ( Data, c, getImageDataSizeInBytes () );
|
|
}
|
|
|
|
|
|
//! get a filtered pixel
|
|
inline SColor CImage::getPixelBox ( s32 x, s32 y, s32 fx, s32 fy, s32 bias ) const
|
|
{
|
|
SColor c;
|
|
s32 a = 0, r = 0, g = 0, b = 0;
|
|
|
|
for ( s32 dx = 0; dx != fx; ++dx )
|
|
{
|
|
for ( s32 dy = 0; dy != fy; ++dy )
|
|
{
|
|
c = getPixel ( x + dx , y + dy );
|
|
|
|
a += c.getAlpha ();
|
|
r += c.getRed();
|
|
g += c.getGreen();
|
|
b += c.getBlue();
|
|
}
|
|
}
|
|
|
|
s32 sdiv = s32_log2_s32(fx * fy);
|
|
|
|
a = core::s32_clamp ( ( a >> sdiv ) + bias, 0, 255 );
|
|
r = core::s32_clamp ( ( r >> sdiv ) + bias, 0, 255 );
|
|
g = core::s32_clamp ( ( g >> sdiv ) + bias, 0, 255 );
|
|
b = core::s32_clamp ( ( b >> sdiv ) + bias, 0, 255 );
|
|
|
|
c.set ( a, r, g, b );
|
|
return c;
|
|
}
|
|
|
|
|
|
} // end namespace video
|
|
} // end namespace irr
|
|
|