1
/* libs/graphics/sgl/SkRegion_path.cpp
3
** Copyright 2006, The Android Open Source Project
5
** Licensed under the Apache License, Version 2.0 (the "License");
6
** you may not use this file except in compliance with the License.
7
** You may obtain a copy of the License at
9
** http://www.apache.org/licenses/LICENSE-2.0
11
** Unless required by applicable law or agreed to in writing, software
12
** distributed under the License is distributed on an "AS IS" BASIS,
13
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
** See the License for the specific language governing permissions and
15
** limitations under the License.
18
#include "SkRegionPriv.h"
19
#include "SkBlitter.h"
21
#include "SkTDArray.h"
24
class SkRgnBuilder : public SkBlitter {
26
virtual ~SkRgnBuilder();
28
// returns true if it could allocate the working storage needed
29
bool init(int maxHeight, int maxTransitions);
32
if (fCurrScanline != NULL) {
33
fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
34
if (!this->collapsWithPrev()) { // flush the last line
35
fCurrScanline = fCurrScanline->nextScanline();
40
int computeRunCount() const;
41
void copyToRect(SkIRect*) const;
42
void copyToRgn(SkRegion::RunType runs[]) const;
44
virtual void blitH(int x, int y, int width);
48
SkDebugf("SkRgnBuilder: Top = %d\n", fTop);
49
const Scanline* line = (Scanline*)fStorage;
50
while (line < fCurrScanline) {
51
SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount);
52
for (int i = 0; i < line->fXCount; i++) {
53
SkDebugf(" %d", line->firstX()[i]);
57
line = line->nextScanline();
63
SkRegion::RunType fLastY;
64
SkRegion::RunType fXCount;
66
SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); }
67
Scanline* nextScanline() const {
68
return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount);
71
SkRegion::RunType* fStorage;
72
Scanline* fCurrScanline;
73
Scanline* fPrevScanline;
74
// points at next avialable x[] in fCurrScanline
75
SkRegion::RunType* fCurrXPtr;
76
SkRegion::RunType fTop; // first Y value
80
bool collapsWithPrev() {
81
if (fPrevScanline != NULL &&
82
fPrevScanline->fLastY + 1 == fCurrScanline->fLastY &&
83
fPrevScanline->fXCount == fCurrScanline->fXCount &&
84
!memcmp(fPrevScanline->firstX(),
85
fCurrScanline->firstX(),
86
fCurrScanline->fXCount * sizeof(SkRegion::RunType)))
88
// update the height of fPrevScanline
89
fPrevScanline->fLastY = fCurrScanline->fLastY;
96
SkRgnBuilder::~SkRgnBuilder() {
100
bool SkRgnBuilder::init(int maxHeight, int maxTransitions) {
101
if ((maxHeight | maxTransitions) < 0) {
107
// compute the count with +1 and +3 slop for the working buffer
108
count.setMul(maxHeight + 1, 3 + maxTransitions);
109
if (!count.is32() || count.isNeg()) {
112
fStorageCount = count.get32();
114
size.setMul(fStorageCount, sizeof(SkRegion::RunType));
115
if (!size.is32() || size.isNeg()) {
119
fStorage = (SkRegion::RunType*)sk_malloc_flags(size.get32(), 0);
120
if (NULL == fStorage) {
124
fCurrScanline = NULL; // signal empty collection
125
fPrevScanline = NULL; // signal first scanline
129
void SkRgnBuilder::blitH(int x, int y, int width) {
130
if (fCurrScanline == NULL) { // first time
131
fTop = (SkRegion::RunType)(y);
132
fCurrScanline = (Scanline*)fStorage;
133
fCurrScanline->fLastY = (SkRegion::RunType)(y);
134
fCurrXPtr = fCurrScanline->firstX();
136
SkASSERT(y >= fCurrScanline->fLastY);
138
if (y > fCurrScanline->fLastY) {
139
// if we get here, we're done with fCurrScanline
140
fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
142
int prevLastY = fCurrScanline->fLastY;
143
if (!this->collapsWithPrev()) {
144
fPrevScanline = fCurrScanline;
145
fCurrScanline = fCurrScanline->nextScanline();
148
if (y - 1 > prevLastY) { // insert empty run
149
fCurrScanline->fLastY = (SkRegion::RunType)(y - 1);
150
fCurrScanline->fXCount = 0;
151
fCurrScanline = fCurrScanline->nextScanline();
153
// setup for the new curr line
154
fCurrScanline->fLastY = (SkRegion::RunType)(y);
155
fCurrXPtr = fCurrScanline->firstX();
158
// check if we should extend the current run, or add a new one
159
if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) {
160
fCurrXPtr[-1] = (SkRegion::RunType)(x + width);
162
fCurrXPtr[0] = (SkRegion::RunType)(x);
163
fCurrXPtr[1] = (SkRegion::RunType)(x + width);
166
SkASSERT(fCurrXPtr - fStorage < fStorageCount);
169
int SkRgnBuilder::computeRunCount() const {
170
if (fCurrScanline == NULL) {
174
const SkRegion::RunType* line = fStorage;
175
const SkRegion::RunType* stop = (const SkRegion::RunType*)fCurrScanline;
177
return 2 + (int)(stop - line);
180
void SkRgnBuilder::copyToRect(SkIRect* r) const {
181
SkASSERT(fCurrScanline != NULL);
182
SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 4);
184
const Scanline* line = (const Scanline*)fStorage;
185
SkASSERT(line->fXCount == 2);
187
r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1);
190
void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const {
191
SkASSERT(fCurrScanline != NULL);
192
SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4);
194
const Scanline* line = (const Scanline*)fStorage;
195
const Scanline* stop = fCurrScanline;
199
*runs++ = (SkRegion::RunType)(line->fLastY + 1);
200
int count = line->fXCount;
202
memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType));
205
*runs++ = SkRegion::kRunTypeSentinel;
206
line = line->nextScanline();
207
} while (line < stop);
208
SkASSERT(line == stop);
209
*runs = SkRegion::kRunTypeSentinel;
212
static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
213
static const uint8_t gPathVerbToInitialLastIndex[] = {
222
static const uint8_t gPathVerbToMaxEdges[] = {
231
SkPath::Iter iter(path, true);
236
SkScalar top = SkIntToScalar(SK_MaxS16);
237
SkScalar bot = SkIntToScalar(SK_MinS16);
239
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
240
maxEdges += gPathVerbToMaxEdges[verb];
242
int lastIndex = gPathVerbToInitialLastIndex[verb];
244
for (int i = 1; i <= lastIndex; i++) {
245
if (top > pts[i].fY) {
247
} else if (bot < pts[i].fY) {
251
} else if (SkPath::kMove_Verb == verb) {
252
if (top > pts[0].fY) {
254
} else if (bot < pts[0].fY) {
259
SkASSERT(top <= bot);
261
*itop = SkScalarRound(top);
262
*ibot = SkScalarRound(bot);
266
bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) {
267
SkDEBUGCODE(this->validate();)
269
if (clip.isEmpty()) {
270
return this->setEmpty();
273
if (path.isEmpty()) {
274
if (path.isInverseFillType()) {
275
return this->set(clip);
277
return this->setEmpty();
281
// compute worst-case rgn-size for the path
282
int pathTop, pathBot;
283
int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
284
int clipTop, clipBot;
287
clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
289
int top = SkMax32(pathTop, clipTop);
290
int bot = SkMin32(pathBot, clipBot);
293
return this->setEmpty();
295
SkRgnBuilder builder;
297
if (!builder.init(bot - top, SkMax32(pathTransitions, clipTransitions))) {
298
// can't allocate working space, so return false
299
return this->setEmpty();
302
SkScan::FillPath(path, clip, &builder);
305
int count = builder.computeRunCount();
307
return this->setEmpty();
308
} else if (count == kRectRegionRuns) {
309
builder.copyToRect(&fBounds);
310
this->setRect(fBounds);
314
tmp.fRunHead = RunHead::Alloc(count);
315
builder.copyToRgn(tmp.fRunHead->writable_runs());
316
ComputeRunBounds(tmp.fRunHead->readonly_runs(), count, &tmp.fBounds);
319
SkDEBUGCODE(this->validate();)
323
/////////////////////////////////////////////////////////////////////////////////////////////////
324
/////////////////////////////////////////////////////////////////////////////////////////////////
331
kCompleteLink = (kY0Link | kY1Link)
334
SkRegion::RunType fX;
335
SkRegion::RunType fY0, fY1;
339
void set(int x, int y0, int y1) {
342
fX = (SkRegion::RunType)(x);
343
fY0 = (SkRegion::RunType)(y0);
344
fY1 = (SkRegion::RunType)(y1);
346
SkDEBUGCODE(fNext = NULL;)
350
return SkFastMin32(fY0, fY1);
354
static void find_link(Edge* base, Edge* stop) {
355
SkASSERT(base < stop);
357
if (base->fFlags == Edge::kCompleteLink) {
358
SkASSERT(base->fNext);
362
SkASSERT(base + 1 < stop);
368
if ((base->fFlags & Edge::kY0Link) == 0) {
371
if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) {
372
SkASSERT(NULL == e->fNext);
374
e->fFlags = SkToU8(e->fFlags | Edge::kY1Link);
381
if ((base->fFlags & Edge::kY1Link) == 0) {
384
if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) {
385
SkASSERT(NULL == base->fNext);
387
e->fFlags = SkToU8(e->fFlags | Edge::kY0Link);
393
base->fFlags = Edge::kCompleteLink;
396
static int extract_path(Edge* edge, Edge* stop, SkPath* path) {
397
while (0 == edge->fFlags) {
398
edge++; // skip over "used" edges
401
SkASSERT(edge < stop);
406
SkASSERT(edge != base);
409
path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0));
412
if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear
413
path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
414
path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H
420
} while (edge != base);
421
path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
426
#include "SkTSearch.h"
428
static int EdgeProc(const Edge* a, const Edge* b) {
429
return (a->fX == b->fX) ? a->top() - b->top() : a->fX - b->fX;
432
bool SkRegion::getBoundaryPath(SkPath* path) const {
433
if (this->isEmpty()) {
437
const SkIRect& bounds = this->getBounds();
439
if (this->isRect()) {
441
r.set(bounds); // this converts the ints to scalars
446
SkRegion::Iterator iter(*this);
447
SkTDArray<Edge> edges;
449
for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) {
450
Edge* edge = edges.append(2);
451
edge[0].set(r.fLeft, r.fBottom, r.fTop);
452
edge[1].set(r.fRight, r.fTop, r.fBottom);
454
SkQSort(edges.begin(), edges.count(), sizeof(Edge), (SkQSortCompareProc)EdgeProc);
456
int count = edges.count();
457
Edge* start = edges.begin();
458
Edge* stop = start + count;
461
for (e = start; e != stop; e++) {
466
for (e = start; e != stop; e++) {
467
SkASSERT(e->fNext != NULL);
468
SkASSERT(e->fFlags == Edge::kCompleteLink);
472
path->incReserve(count << 1);
475
count -= extract_path(start, stop, path);