/* ----------------------------------------------------------------------------- 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 *clip_rect) { if (!gradient.GetControlPointCount()) return; Rect 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; } //------------------------------------------------------------------------------