/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkEdgeBuilder.h"
#include "SkPath.h"
#include "SkEdge.h"
#include "SkEdgeClipper.h"
#include "SkLineClipper.h"
#include "SkGeometry.h"
SkEdgeBuilder::SkEdgeBuilder() : fAlloc(16*1024) {}
template <typename T> static T* typedAllocThrow(SkChunkAlloc& alloc) {
return static_cast<T*>(alloc.allocThrow(sizeof(T)));
}
///////////////////////////////////////////////////////////////////////////////
void SkEdgeBuilder::addLine(const SkPoint pts[]) {
SkEdge* edge = typedAllocThrow<SkEdge>(fAlloc);
if (edge->setLine(pts[0], pts[1], NULL, fShiftUp)) {
fList.push(edge);
} else {
// TODO: unallocate edge from storage...
}
}
void SkEdgeBuilder::addQuad(const SkPoint pts[]) {
SkQuadraticEdge* edge = typedAllocThrow<SkQuadraticEdge>(fAlloc);
if (edge->setQuadratic(pts, fShiftUp)) {
fList.push(edge);
} else {
// TODO: unallocate edge from storage...
}
}
void SkEdgeBuilder::addCubic(const SkPoint pts[]) {
SkCubicEdge* edge = typedAllocThrow<SkCubicEdge>(fAlloc);
if (edge->setCubic(pts, NULL, fShiftUp)) {
fList.push(edge);
} else {
// TODO: unallocate edge from storage...
}
}
void SkEdgeBuilder::addClipper(SkEdgeClipper* clipper) {
SkPoint pts[4];
SkPath::Verb verb;
while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kLine_Verb:
this->addLine(pts);
break;
case SkPath::kQuad_Verb:
this->addQuad(pts);
break;
case SkPath::kCubic_Verb:
this->addCubic(pts);
break;
default:
break;
}
}
}
///////////////////////////////////////////////////////////////////////////////
static void setShiftedClip(SkRect* dst, const SkIRect& src, int shift) {
dst->set(SkIntToScalar(src.fLeft >> shift),
SkIntToScalar(src.fTop >> shift),
SkIntToScalar(src.fRight >> shift),
SkIntToScalar(src.fBottom >> shift));
}
int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip,
int shiftUp) {
fAlloc.reset();
fList.reset();
fShiftUp = shiftUp;
SkPath::Iter iter(path, true);
SkPoint pts[4];
SkPath::Verb verb;
if (iclip) {
SkRect clip;
setShiftedClip(&clip, *iclip, shiftUp);
SkEdgeClipper clipper;
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
case SkPath::kClose_Verb:
// we ignore these, and just get the whole segment from
// the corresponding line/quad/cubic verbs
break;
case SkPath::kLine_Verb: {
SkPoint lines[SkLineClipper::kMaxPoints];
int lineCount = SkLineClipper::ClipLine(pts, clip, lines);
for (int i = 0; i < lineCount; i++) {
this->addLine(&lines[i]);
}
break;
}
case SkPath::kQuad_Verb:
if (clipper.clipQuad(pts, clip)) {
this->addClipper(&clipper);
}
break;
case SkPath::kCubic_Verb:
if (clipper.clipCubic(pts, clip)) {
this->addClipper(&clipper);
}
break;
default:
SkDEBUGFAIL("unexpected verb");
break;
}
}
} else {
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
case SkPath::kClose_Verb:
// we ignore these, and just get the whole segment from
// the corresponding line/quad/cubic verbs
break;
case SkPath::kLine_Verb:
this->addLine(pts);
break;
case SkPath::kQuad_Verb: {
SkPoint monoX[5];
int n = SkChopQuadAtYExtrema(pts, monoX);
for (int i = 0; i <= n; i++) {
this->addQuad(&monoX[i * 2]);
}
break;
}
case SkPath::kCubic_Verb: {
SkPoint monoY[10];
int n = SkChopCubicAtYExtrema(pts, monoY);
for (int i = 0; i <= n; i++) {
this->addCubic(&monoY[i * 3]);
}
break;
}
default:
SkDEBUGFAIL("unexpected verb");
break;
}
}
}
return fList.count();
}