This source file includes following definitions.
- m_spreadMethod
- m_spreadMethod
- compareStops
- addColorStop
- sortStopsIfNecessary
- hasAlpha
- setSpreadMethod
- setDrawsInPMColorSpace
- setGradientSpaceTransform
- totalStopsNeeded
- makeSkColor
- fillStops
- shader
#include "config.h"
#include "platform/graphics/Gradient.h"
#include "platform/geometry/FloatRect.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/skia/SkiaUtils.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkColorShader.h"
#include "third_party/skia/include/core/SkShader.h"
#include "third_party/skia/include/effects/SkGradientShader.h"
typedef Vector<SkScalar, 8> ColorStopOffsetVector;
typedef Vector<SkColor, 8> ColorStopColorVector;
namespace WebCore {
Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1)
: m_p0(p0)
, m_p1(p1)
, m_r0(0)
, m_r1(0)
, m_aspectRatio(1)
, m_radial(false)
, m_stopsSorted(false)
, m_drawInPMColorSpace(false)
, m_spreadMethod(SpreadMethodPad)
{
}
Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio)
: m_p0(p0)
, m_p1(p1)
, m_r0(r0)
, m_r1(r1)
, m_aspectRatio(aspectRatio)
, m_radial(true)
, m_stopsSorted(false)
, m_drawInPMColorSpace(false)
, m_spreadMethod(SpreadMethodPad)
{
}
Gradient::~Gradient()
{
}
static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b)
{
return a.stop < b.stop;
}
void Gradient::addColorStop(const Gradient::ColorStop& stop)
{
if (m_stops.isEmpty()) {
m_stopsSorted = true;
} else {
m_stopsSorted = m_stopsSorted && compareStops(m_stops.last(), stop);
}
m_stops.append(stop);
m_gradient.clear();
}
void Gradient::sortStopsIfNecessary()
{
if (m_stopsSorted)
return;
m_stopsSorted = true;
if (!m_stops.size())
return;
std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
}
bool Gradient::hasAlpha() const
{
for (size_t i = 0; i < m_stops.size(); i++) {
if (m_stops[i].color.hasAlpha())
return true;
}
return false;
}
void Gradient::setSpreadMethod(GradientSpreadMethod spreadMethod)
{
ASSERT(!m_gradient);
if (m_spreadMethod == spreadMethod)
return;
m_spreadMethod = spreadMethod;
}
void Gradient::setDrawsInPMColorSpace(bool drawInPMColorSpace)
{
if (drawInPMColorSpace == m_drawInPMColorSpace)
return;
m_drawInPMColorSpace = drawInPMColorSpace;
m_gradient.clear();
}
void Gradient::setGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation)
{
if (m_gradientSpaceTransformation == gradientSpaceTransformation)
return;
m_gradientSpaceTransformation = gradientSpaceTransformation;
if (m_gradient)
m_gradient->setLocalMatrix(affineTransformToSkMatrix(m_gradientSpaceTransformation));
}
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 inline SkColor makeSkColor(const Color& c)
{
return SkColorSetARGB(c.alpha(), c.red(), c.green(), c.blue());
}
static void fillStops(const Gradient::ColorStop* stopData,
size_t count, ColorStopOffsetVector& pos, ColorStopColorVector& colors)
{
const Gradient::ColorStop* stop = stopData;
size_t start = 0;
if (count < 1) {
pos[0] = WebCoreFloatToSkScalar(0.0);
colors[0] = SK_ColorTRANSPARENT;
start = 1;
} else if (stop->stop > 0.0) {
pos[0] = WebCoreFloatToSkScalar(0.0);
colors[0] = makeSkColor(stop->color);
start = 1;
}
for (size_t i = start; i < start + count; i++) {
pos[i] = WebCoreFloatToSkScalar(stop->stop);
colors[i] = makeSkColor(stop->color);
++stop;
}
if (count < 1 || (--stop)->stop < 1.0) {
pos[start + count] = WebCoreFloatToSkScalar(1.0);
colors[start + count] = colors[start + count - 1];
}
}
SkShader* Gradient::shader()
{
if (m_gradient)
return m_gradient.get();
sortStopsIfNecessary();
ASSERT(m_stopsSorted);
size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size());
ASSERT(countUsed >= 2);
ASSERT(countUsed >= m_stops.size());
ColorStopOffsetVector pos(countUsed);
ColorStopColorVector 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;
}
uint32_t shouldDrawInPMColorSpace = m_drawInPMColorSpace ? SkGradientShader::kInterpolateColorsInPremul_Flag : 0;
if (m_radial) {
if (m_p0 == m_p1 && m_r0 <= 0.0f) {
m_gradient = adoptRef(SkGradientShader::CreateRadial(m_p1, m_r1, colors.data(), pos.data(), static_cast<int>(countUsed), tile, 0, shouldDrawInPMColorSpace));
} else {
SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0;
SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0;
m_gradient = adoptRef(SkGradientShader::CreateTwoPointConical(m_p0, radius0, m_p1, radius1, colors.data(), pos.data(), static_cast<int>(countUsed), tile, 0, shouldDrawInPMColorSpace));
}
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 = adoptRef(SkGradientShader::CreateLinear(pts, colors.data(), pos.data(), static_cast<int>(countUsed), tile, 0, shouldDrawInPMColorSpace));
}
if (!m_gradient) {
m_gradient = adoptRef(new SkColorShader(colors[countUsed - 1]));
} else {
m_gradient->setLocalMatrix(affineTransformToSkMatrix(m_gradientSpaceTransformation));
}
return m_gradient.get();
}
}