185 lines
5.4 KiB
C++
185 lines
5.4 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "picture/pict_gradient.h"
|
|
#include "picture/pict.h"
|
|
#include "metafile/nml.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
static uchar ClampChannel(int v)
|
|
{
|
|
if (v < 0)
|
|
return 0;
|
|
if (v > 255)
|
|
return 255;
|
|
return (uchar)v;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Picture::DrawGradient(const Gradient &gradient, const Rect <int> *clip_rect)
|
|
{
|
|
if (!gradient.GetControlPointCount())
|
|
return;
|
|
|
|
Rect <int> rect = GetRect();
|
|
if (!clip_rect)
|
|
clip_rect = ▭
|
|
|
|
uint current_control_point = 0,
|
|
next_control_point = 1;
|
|
if (next_control_point == gradient.GetControlPointCount())
|
|
next_control_point = current_control_point;
|
|
uchar *pdata = GetDataOffset(clip_rect->sx, clip_rect->sy);
|
|
|
|
for (int y = 0; y < clip_rect->GetHeight(); ++y)
|
|
{
|
|
float c_k = (float)y / clip_rect->GetHeight();
|
|
|
|
// Check gradient interval change.
|
|
if ((c_k > gradient.k[next_control_point]) && (current_control_point + 1 < gradient.GetControlPointCount()))
|
|
{
|
|
current_control_point++;
|
|
next_control_point++;
|
|
if (next_control_point == gradient.GetControlPointCount())
|
|
next_control_point = current_control_point;
|
|
}
|
|
|
|
// Blend.
|
|
Color blend_color = gradient.color[current_control_point];
|
|
|
|
if (current_control_point != next_control_point)
|
|
blend_color = (gradient.color[next_control_point] - gradient.color[current_control_point]) *
|
|
(c_k - gradient.k[current_control_point]) / (gradient.k[next_control_point] - gradient.k[current_control_point]) +
|
|
gradient.color[current_control_point];
|
|
|
|
uchar *pscan = pdata,
|
|
r = (uchar)(blend_color.x * 255),
|
|
g = (uchar)(blend_color.y * 255),
|
|
b = (uchar)(blend_color.z * 255),
|
|
a = (uchar)(blend_color.w * 255);
|
|
|
|
switch (gradient.GetOperator())
|
|
{
|
|
case Picture::BlendReplace:
|
|
for (int x = 0; x < rect.GetWidth(); ++x)
|
|
{
|
|
pscan[0] = r;
|
|
pscan[1] = g;
|
|
pscan[2] = b;
|
|
pscan[3] = a;
|
|
pscan += 4;
|
|
}
|
|
break;
|
|
|
|
case Picture::BlendCompose:
|
|
for (int x = 0; x < rect.GetWidth(); ++x)
|
|
{
|
|
uchar a_blend = AlphaCompositeAlpha(pscan[3], a);
|
|
pscan[0] = AlphaCompositeColor(pscan[0], r, pscan[3], a, a_blend);
|
|
pscan[1] = AlphaCompositeColor(pscan[1], g, pscan[3], a, a_blend);
|
|
pscan[2] = AlphaCompositeColor(pscan[2], b, pscan[3], a, a_blend);
|
|
pscan[3] = a_blend;
|
|
pscan += 4;
|
|
}
|
|
break;
|
|
|
|
case Picture::BlendMultiply:
|
|
for (int x = 0; x < rect.GetWidth(); ++x)
|
|
{
|
|
pscan[0] = (pscan[0] * r) >> 8;
|
|
pscan[1] = (pscan[1] * g) >> 8;
|
|
pscan[2] = (pscan[2] * b) >> 8;
|
|
pscan[3] = (pscan[3] * a) >> 8;
|
|
pscan += 4;
|
|
}
|
|
break;
|
|
|
|
case Picture::BlendMultiply2x:
|
|
for (int x = 0; x < rect.GetWidth(); ++x)
|
|
{
|
|
pscan[0] = ClampChannel((pscan[0] * r) >> 7);
|
|
pscan[1] = ClampChannel((pscan[1] * g) >> 7);
|
|
pscan[2] = ClampChannel((pscan[2] * b) >> 7);
|
|
pscan[3] = (pscan[3] * a) >> 8;
|
|
pscan += 4;
|
|
}
|
|
break;
|
|
|
|
case Picture::BlendAdd:
|
|
for (int x = 0; x < rect.GetWidth(); ++x)
|
|
{
|
|
pscan[0] = ClampChannel(pscan[0] + r);
|
|
pscan[1] = ClampChannel(pscan[1] + g);
|
|
pscan[2] = ClampChannel(pscan[2] + b);
|
|
pscan[3] = (pscan[3] * a) >> 8;
|
|
pscan += 4;
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
pdata += GetPitch();
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Gradient::FromMetaTag(NML::Tag *tag)
|
|
{
|
|
control_point_count = 0;
|
|
op = Picture::BlendMultiply;
|
|
|
|
NMLTagForeach(ctag, *tag)
|
|
{
|
|
if (ctag->name == "Operator")
|
|
{
|
|
String op_string(ctag->GetString());
|
|
|
|
if (op_string == "Replace")
|
|
op = Picture::BlendReplace;
|
|
else if (op_string == "Multiply")
|
|
op = Picture::BlendMultiply;
|
|
else if (op_string == "Multiply2x")
|
|
op = Picture::BlendMultiply2x;
|
|
else if (op_string == "Add")
|
|
op = Picture::BlendAdd;
|
|
else if (op_string == "Compose")
|
|
op = Picture::BlendCompose;
|
|
else __LOG_W__ << "Unknown gradient operator.\n";
|
|
}
|
|
else if (ctag->name == "Control")
|
|
{
|
|
// Control point count safety.
|
|
if (control_point_count == 8)
|
|
__ERR__(__LOG_E__ << "Too many control points in gradient definition.\n", false);
|
|
|
|
// Coordinate.
|
|
NML::Tag *attr_tag = ctag->GetTypedTag("K", Variant::VariantFloat);
|
|
if (!attr_tag)
|
|
__ERR__(__LOG_E__ << "Missing tag (control point coordinate) in gradient definition.\n", false);
|
|
k[control_point_count] = attr_tag->GetReal();
|
|
|
|
// Color.
|
|
attr_tag = ctag->GetTypedTag("Color", Variant::VariantNone);
|
|
if (!attr_tag)
|
|
__ERR__(__LOG_E__ << "Missing tag (control point color) in gradient definition.\n", false);
|
|
color[control_point_count].Set();
|
|
if (!color[control_point_count].FromMetaTag(*attr_tag))
|
|
__ERR__(__LOG_E__ << "Erroneous control point color in gradient definition.\n", false);
|
|
|
|
control_point_count++;
|
|
}
|
|
else __ERR__(__LOG_E__ << "Unexpected tag in gradient definition.\n", false);
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|