• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libs/graphics/sgl/SkScan_Hairline.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
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
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
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.
16 */
17 
18 #include "SkScan.h"
19 #include "SkBlitter.h"
20 #include "SkRegion.h"
21 #include "SkFDot6.h"
22 
horiline(int x,int stopx,SkFixed fy,SkFixed dy,SkBlitter * blitter)23 static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter)
24 {
25     SkASSERT(x < stopx);
26 
27     do {
28         blitter->blitH(x, fy >> 16, 1);
29         fy += dy;
30     } while (++x < stopx);
31 }
32 
vertline(int y,int stopy,SkFixed fx,SkFixed dx,SkBlitter * blitter)33 static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter)
34 {
35     SkASSERT(y < stopy);
36 
37     do {
38         blitter->blitH(fx >> 16, y, 1);
39         fx += dx;
40     } while (++y < stopy);
41 }
42 
HairLine(const SkPoint & pt0,const SkPoint & pt1,const SkRegion * clip,SkBlitter * blitter)43 void SkScan::HairLine(const SkPoint& pt0, const SkPoint& pt1, const SkRegion* clip, SkBlitter* blitter)
44 {
45     SkBlitterClipper    clipper;
46 
47     SkFDot6 x0 = SkScalarToFDot6(pt0.fX);
48     SkFDot6 y0 = SkScalarToFDot6(pt0.fY);
49     SkFDot6 x1 = SkScalarToFDot6(pt1.fX);
50     SkFDot6 y1 = SkScalarToFDot6(pt1.fY);
51 
52     if (clip)
53     {
54         SkRect      r;
55         SkIRect     ir;
56         SkPoint     pts[2];
57 
58         pts[0] = pt0;
59         pts[1] = pt1;
60         r.set(pts, 2);
61         r.roundOut(&ir);
62 
63         // if we're given a horizontal or vertical line
64         // this rect could be empty (in area), in which case
65         // clip->quickReject() will always return true.
66         // hence we bloat the rect to avoid that case
67         if (ir.width() == 0)
68             ir.fRight += 1;
69         if (ir.height() == 0)
70             ir.fBottom += 1;
71 
72         if (clip->quickReject(ir))
73             return;
74         if (clip->quickContains(ir))
75             clip = NULL;
76         else
77         {
78             blitter = clipper.apply(blitter, clip);
79         }
80     }
81 
82     SkFDot6 dx = x1 - x0;
83     SkFDot6 dy = y1 - y0;
84 
85     if (SkAbs32(dx) > SkAbs32(dy))  // mostly horizontal
86     {
87         if (x0 > x1)    // we want to go left-to-right
88         {
89             SkTSwap<SkFDot6>(x0, x1);
90             SkTSwap<SkFDot6>(y0, y1);
91         }
92         int ix0 = SkFDot6Round(x0);
93         int ix1 = SkFDot6Round(x1);
94         if (ix0 == ix1) // too short to draw
95             return;
96 
97         SkFixed slope = SkFixedDiv(dy, dx);
98         SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
99 
100         horiline(ix0, ix1, startY, slope, blitter);
101     }
102     else                // mostly vertical
103     {
104         if (y0 > y1)    // we want to go top-to-bottom
105         {
106             SkTSwap<SkFDot6>(x0, x1);
107             SkTSwap<SkFDot6>(y0, y1);
108         }
109         int iy0 = SkFDot6Round(y0);
110         int iy1 = SkFDot6Round(y1);
111         if (iy0 == iy1) // too short to draw
112             return;
113 
114         SkFixed slope = SkFixedDiv(dx, dy);
115         SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
116 
117         vertline(iy0, iy1, startX, slope, blitter);
118     }
119 }
120 
121 // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
122 // and double-hit the top-left.
HairRect(const SkRect & rect,const SkRegion * clip,SkBlitter * blitter)123 void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter)
124 {
125     SkBlitterClipper    clipper;
126     SkIRect             r;
127 
128     r.set(SkScalarToFixed(rect.fLeft) >> 16,
129           SkScalarToFixed(rect.fTop) >> 16,
130           (SkScalarToFixed(rect.fRight) >> 16) + 1,
131           (SkScalarToFixed(rect.fBottom) >> 16) + 1);
132 
133     if (clip)
134     {
135         if (clip->quickReject(r))
136             return;
137         if (!clip->quickContains(r))
138             blitter = clipper.apply(blitter, clip);
139     }
140 
141     int width = r.width();
142     int height = r.height();
143 
144     if ((width | height) == 0)
145         return;
146     if (width <= 2 || height <= 2)
147     {
148         blitter->blitRect(r.fLeft, r.fTop, width, height);
149         return;
150     }
151     // if we get here, we know we have 4 segments to draw
152     blitter->blitH(r.fLeft, r.fTop, width);                     // top
153     blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2);      // left
154     blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
155     blitter->blitH(r.fLeft, r.fBottom - 1, width);              // bottom
156 }
157 
158 /////////////////////////////////////////////////////////////////////////////////////////////////
159 
160 #include "SkPath.h"
161 #include "SkGeometry.h"
162 
quad_too_curvy(const SkPoint pts[3])163 static bool quad_too_curvy(const SkPoint pts[3])
164 {
165     return true;
166 }
167 
compute_int_quad_dist(const SkPoint pts[3])168 static int compute_int_quad_dist(const SkPoint pts[3]) {
169     // compute the vector between the control point ([1]) and the middle of the
170     // line connecting the start and end ([0] and [2])
171     SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
172     SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
173     // we want everyone to be positive
174     dx = SkScalarAbs(dx);
175     dy = SkScalarAbs(dy);
176     // convert to whole pixel values (use ceiling to be conservative)
177     int idx = SkScalarCeil(dx);
178     int idy = SkScalarCeil(dy);
179     // use the cheap approx for distance
180     if (idx > idy) {
181         return idx + (idy >> 1);
182     } else {
183         return idy + (idx >> 1);
184     }
185 }
186 
hairquad(const SkPoint pts[3],const SkRegion * clip,SkBlitter * blitter,int level,void (* lineproc)(const SkPoint &,const SkPoint &,const SkRegion * clip,SkBlitter *))187 static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level,
188                      void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*))
189 {
190 #if 1
191     if (level > 0 && quad_too_curvy(pts))
192     {
193         SkPoint tmp[5];
194 
195         SkChopQuadAtHalf(pts, tmp);
196         hairquad(tmp, clip, blitter, level - 1, lineproc);
197         hairquad(&tmp[2], clip, blitter, level - 1, lineproc);
198     }
199     else
200         lineproc(pts[0], pts[2], clip, blitter);
201 #else
202     int count = 1 << level;
203     const SkScalar dt = SkFixedToScalar(SK_Fixed1 >> level);
204     SkScalar t = dt;
205     SkPoint prevPt = pts[0];
206     for (int i = 1; i < count; i++) {
207         SkPoint nextPt;
208         SkEvalQuadAt(pts, t, &nextPt);
209         lineproc(prevPt, nextPt, clip, blitter);
210         t += dt;
211         prevPt = nextPt;
212     }
213     // draw the last line explicitly to 1.0, in case t didn't match that exactly
214     lineproc(prevPt, pts[2], clip, blitter);
215 #endif
216 }
217 
cubic_too_curvy(const SkPoint pts[4])218 static bool cubic_too_curvy(const SkPoint pts[4])
219 {
220     return true;
221 }
222 
haircubic(const SkPoint pts[4],const SkRegion * clip,SkBlitter * blitter,int level,void (* lineproc)(const SkPoint &,const SkPoint &,const SkRegion *,SkBlitter *))223 static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level,
224                       void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
225 {
226     if (level > 0 && cubic_too_curvy(pts))
227     {
228         SkPoint tmp[7];
229 
230         SkChopCubicAt(pts, tmp, SK_Scalar1/2);
231         haircubic(tmp, clip, blitter, level - 1, lineproc);
232         haircubic(&tmp[3], clip, blitter, level - 1, lineproc);
233     }
234     else
235         lineproc(pts[0], pts[3], clip, blitter);
236 }
237 
238 #define kMaxCubicSubdivideLevel 6
239 #define kMaxQuadSubdivideLevel  5
240 
hair_path(const SkPath & path,const SkRegion * clip,SkBlitter * blitter,void (* lineproc)(const SkPoint &,const SkPoint &,const SkRegion *,SkBlitter *))241 static void hair_path(const SkPath& path, const SkRegion* clip, SkBlitter* blitter,
242                       void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
243 {
244     if (path.isEmpty())
245         return;
246 
247     const SkIRect* clipR = NULL;
248 
249     if (clip)
250     {
251         SkIRect ibounds;
252         path.getBounds().roundOut(&ibounds);
253         ibounds.inset(-1, -1);
254 
255         if (clip->quickReject(ibounds))
256             return;
257 
258         if (clip->quickContains(ibounds))
259             clip = NULL;
260         else
261             clipR = &clip->getBounds();
262     }
263 
264     SkPath::Iter    iter(path, false);
265     SkPoint         pts[4];
266     SkPath::Verb    verb;
267 
268     while ((verb = iter.next(pts)) != SkPath::kDone_Verb)
269     {
270         switch (verb) {
271         case SkPath::kLine_Verb:
272             lineproc(pts[0], pts[1], clip, blitter);
273             break;
274         case SkPath::kQuad_Verb: {
275             int d = compute_int_quad_dist(pts);
276             /*  quadratics approach the line connecting their start and end points
277              4x closer with each subdivision, so we compute the number of
278              subdivisions to be the minimum need to get that distance to be less
279              than a pixel.
280              */
281             int level = (33 - SkCLZ(d)) >> 1;
282 //          SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel);
283             // sanity check on level (from the previous version)
284             if (level > kMaxQuadSubdivideLevel) {
285                 level = kMaxQuadSubdivideLevel;
286             }
287             hairquad(pts, clip, blitter, level, lineproc);
288             break;
289         }
290         case SkPath::kCubic_Verb:
291             haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc);
292             break;
293         default:
294             break;
295         }
296     }
297 }
298 
HairPath(const SkPath & path,const SkRegion * clip,SkBlitter * blitter)299 void SkScan::HairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter)
300 {
301     hair_path(path, clip, blitter, SkScan::HairLine);
302 }
303 
AntiHairPath(const SkPath & path,const SkRegion * clip,SkBlitter * blitter)304 void SkScan::AntiHairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter)
305 {
306     hair_path(path, clip, blitter, SkScan::AntiHairLine);
307 }
308 
309 ////////////////////////////////////////////////////////////////////////////////
310 
FrameRect(const SkRect & r,SkScalar diameter,const SkRegion * clip,SkBlitter * blitter)311 void SkScan::FrameRect(const SkRect& r, SkScalar diameter, const SkRegion* clip, SkBlitter* blitter)
312 {
313     SkASSERT(diameter > 0);
314 
315     if (r.isEmpty())
316         return;
317 
318     SkScalar radius = diameter / 2;
319     SkRect   outer, tmp;
320 
321     outer.set(  r.fLeft - radius, r.fTop - radius,
322                 r.fRight + radius, r.fBottom + radius);
323 
324     if (r.width() <= diameter || r.height() <= diameter)
325     {
326         SkScan::FillRect(outer, clip, blitter);
327         return;
328     }
329 
330     tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + diameter);
331     SkScan::FillRect(tmp, clip, blitter);
332     tmp.fTop = outer.fBottom - diameter;
333     tmp.fBottom = outer.fBottom;
334     SkScan::FillRect(tmp, clip, blitter);
335 
336     tmp.set(outer.fLeft, outer.fTop + diameter, outer.fLeft + diameter, outer.fBottom - diameter);
337     SkScan::FillRect(tmp, clip, blitter);
338     tmp.fLeft = outer.fRight - diameter;
339     tmp.fRight = outer.fRight;
340     SkScan::FillRect(tmp, clip, blitter);
341 }
342 
343