#include "config.h"
#include "Gradient.h"
#include "CSSParser.h"
#include "GraphicsContext.h"
#include "SkGradientShader.h"
#include "SkiaUtils.h"
namespace WebCore {
void Gradient::platformDestroy()
{
if (m_gradient)
m_gradient->safeUnref();
m_gradient = 0;
}
static inline U8CPU F2B(float x)
{
return static_cast<int>(x * 255);
}
static SkColor makeSkColor(float a, float r, float g, float b)
{
return SkColorSetARGB(F2B(a), F2B(r), F2B(g), F2B(b));
}
static size_t totalStopsNeeded(const Gradient::ColorStop* stopData, size_t count)
{
const Gradient::ColorStop* stop = stopData;
size_t countUsed = count;
if (count < 1 || stop->stop > 0.0)
countUsed++;
stop += count - 1;
if (count < 2 || stop->stop < 1.0)
countUsed++;
return countUsed;
}
static void fillStops(const Gradient::ColorStop* stopData,
size_t count, SkScalar* pos, SkColor* colors)
{
const Gradient::ColorStop* stop = stopData;
size_t start = 0;
if (count < 1) {
pos[0] = WebCoreFloatToSkScalar(0.0);
colors[0] = makeSkColor(0.0, 0.0, 0.0, 0.0);
start = 1;
} else if (stop->stop > 0.0) {
pos[0] = WebCoreFloatToSkScalar(0.0);
colors[0] = makeSkColor(stop->alpha, stop->red, stop->green, stop->blue);
start = 1;
}
for (size_t i = start; i < start + count; i++) {
pos[i] = WebCoreFloatToSkScalar(stop->stop);
colors[i] = makeSkColor(stop->alpha, stop->red, stop->green, stop->blue);
++stop;
}
if (count < 1 || (--stop)->stop < 1.0) {
pos[start + count] = WebCoreFloatToSkScalar(1.0);
colors[start + count] = colors[start + count - 1];
}
}
static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b)
{
return a.stop < b.stop;
}
SkShader* Gradient::platformGradient()
{
if (m_gradient)
return m_gradient;
if (!m_stopsSorted) {
if (m_stops.size())
std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
m_stopsSorted = true;
}
size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size());
ASSERT(countUsed >= 2);
ASSERT(countUsed >= m_stops.size());
SkAutoMalloc storage(countUsed * (sizeof(SkColor) + sizeof(SkScalar)));
SkColor* colors = (SkColor*)storage.get();
SkScalar* pos = (SkScalar*)(colors + countUsed);
fillStops(m_stops.data(), m_stops.size(), pos, colors);
SkShader::TileMode tile = SkShader::kClamp_TileMode;
switch (m_spreadMethod) {
case SpreadMethodReflect:
tile = SkShader::kMirror_TileMode;
break;
case SpreadMethodRepeat:
tile = SkShader::kRepeat_TileMode;
break;
case SpreadMethodPad:
tile = SkShader::kClamp_TileMode;
break;
}
if (m_radial) {
m_gradient = SkGradientShader::CreateRadial(m_p1,
WebCoreFloatToSkScalar(m_r1), colors, pos,
static_cast<int>(countUsed), tile);
} else {
SkPoint pts[2] = { m_p0, m_p1 };
m_gradient = SkGradientShader::CreateLinear(pts, colors, pos,
static_cast<int>(countUsed), tile);
}
SkMatrix matrix = m_gradientSpaceTransformation;
m_gradient->setLocalMatrix(matrix);
return m_gradient;
}
void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
{
context->setFillGradient(this);
context->fillRect(rect);
}
}