#include "config.h"
#include "Gradient.h"
#include "CSSParser.h"
#include "GraphicsContext.h"
#include "SkColorShader.h"
#include "SkGradientShader.h"
#include "SkiaUtils.h"
namespace WebCore {
void Gradient::platformDestroy()
{
SkSafeUnref(m_gradient);
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 < 1 || 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];
}
}
SkShader* Gradient::platformGradient()
{
if (m_gradient)
return m_gradient;
sortStopsIfNecessary();
ASSERT(m_stopsSorted);
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) {
if (m_p0 == m_p1 && m_r0 <= 0.0f) {
m_gradient = SkGradientShader::CreateRadial(m_p1, m_r1, colors, pos, static_cast<int>(countUsed), tile);
} else {
SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0;
SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0;
m_gradient = SkGradientShader::CreateTwoPointRadial(m_p0, radius0, m_p1, radius1, colors, pos, static_cast<int>(countUsed), tile);
}
if (aspectRatio() != 1) {
m_gradientSpaceTransformation.translate(m_p0.x(), m_p0.y());
m_gradientSpaceTransformation.scale(1, 1 / aspectRatio());
m_gradientSpaceTransformation.translate(-m_p0.x(), -m_p0.y());
ASSERT(m_p0 == m_p1);
}
} else {
SkPoint pts[2] = { m_p0, m_p1 };
m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, static_cast<int>(countUsed), tile);
}
if (!m_gradient)
m_gradient = new SkColorShader(colors[countUsed - 1]);
else
m_gradient->setLocalMatrix(m_gradientSpaceTransformation);
return m_gradient;
}
void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
{
context->setFillGradient(this);
context->fillRect(rect);
}
void Gradient::setPlatformGradientSpaceTransform(const AffineTransform& matrix)
{
if (m_gradient)
m_gradient->setLocalMatrix(m_gradientSpaceTransformation);
}
}