PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/core/SkEdge.cpp

https://github.com/mkedwards/external__skia
C++ | 473 lines | 324 code | 75 blank | 74 comment | 44 complexity | e1b8e9a796bf8f9529f0b91ce0d1382c MD5 | raw file
  1. /* libs/graphics/sgl/SkEdge.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. #include "SkEdge.h"
  18. #include "SkFDot6.h"
  19. /*
  20. In setLine, setQuadratic, setCubic, the first thing we do is to convert
  21. the points into FDot6. This is modulated by the shift parameter, which
  22. will either be 0, or something like 2 for antialiasing.
  23. In the float case, we want to turn the float into .6 by saying pt * 64,
  24. or pt * 256 for antialiasing. This is implemented as 1 << (shift + 6).
  25. In the fixed case, we want to turn the fixed into .6 by saying pt >> 10,
  26. or pt >> 8 for antialiasing. This is implemented as pt >> (10 - shift).
  27. */
  28. /////////////////////////////////////////////////////////////////////////
  29. int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip,
  30. int shift) {
  31. SkFDot6 x0, y0, x1, y1;
  32. {
  33. #ifdef SK_SCALAR_IS_FLOAT
  34. float scale = float(1 << (shift + 6));
  35. x0 = int(p0.fX * scale);
  36. y0 = int(p0.fY * scale);
  37. x1 = int(p1.fX * scale);
  38. y1 = int(p1.fY * scale);
  39. #else
  40. shift = 10 - shift;
  41. x0 = p0.fX >> shift;
  42. y0 = p0.fY >> shift;
  43. x1 = p1.fX >> shift;
  44. y1 = p1.fY >> shift;
  45. #endif
  46. }
  47. int winding = 1;
  48. if (y0 > y1) {
  49. SkTSwap(x0, x1);
  50. SkTSwap(y0, y1);
  51. winding = -1;
  52. }
  53. int top = SkFDot6Round(y0);
  54. int bot = SkFDot6Round(y1);
  55. // are we a zero-height line?
  56. if (top == bot) {
  57. return 0;
  58. }
  59. // are we completely above or below the clip?
  60. if (NULL != clip && (top >= clip->fBottom || bot <= clip->fTop)) {
  61. return 0;
  62. }
  63. SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
  64. fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2
  65. fDX = slope;
  66. fFirstY = top;
  67. fLastY = bot - 1;
  68. fCurveCount = 0;
  69. fWinding = SkToS8(winding);
  70. fCurveShift = 0;
  71. if (clip) {
  72. this->chopLineWithClip(*clip);
  73. }
  74. return 1;
  75. }
  76. // called from a curve subclass
  77. int SkEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1)
  78. {
  79. SkASSERT(fWinding == 1 || fWinding == -1);
  80. SkASSERT(fCurveCount != 0);
  81. // SkASSERT(fCurveShift != 0);
  82. y0 >>= 10;
  83. y1 >>= 10;
  84. SkASSERT(y0 <= y1);
  85. int top = SkFDot6Round(y0);
  86. int bot = SkFDot6Round(y1);
  87. // SkASSERT(top >= fFirstY);
  88. // are we a zero-height line?
  89. if (top == bot)
  90. return 0;
  91. x0 >>= 10;
  92. x1 >>= 10;
  93. SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
  94. fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2
  95. fDX = slope;
  96. fFirstY = top;
  97. fLastY = bot - 1;
  98. return 1;
  99. }
  100. void SkEdge::chopLineWithClip(const SkIRect& clip)
  101. {
  102. int top = fFirstY;
  103. SkASSERT(top < clip.fBottom);
  104. // clip the line to the top
  105. if (top < clip.fTop)
  106. {
  107. SkASSERT(fLastY >= clip.fTop);
  108. fX += fDX * (clip.fTop - top);
  109. fFirstY = clip.fTop;
  110. }
  111. }
  112. ///////////////////////////////////////////////////////////////////////////////
  113. /* We store 1<<shift in a (signed) byte, so its maximum value is 1<<6 == 64.
  114. Note that this limits the number of lines we use to approximate a curve.
  115. If we need to increase this, we need to store fCurveCount in something
  116. larger than int8_t.
  117. */
  118. #define MAX_COEFF_SHIFT 6
  119. static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy)
  120. {
  121. dx = SkAbs32(dx);
  122. dy = SkAbs32(dy);
  123. // return max + min/2
  124. if (dx > dy)
  125. dx += dy >> 1;
  126. else
  127. dx = dy + (dx >> 1);
  128. return dx;
  129. }
  130. static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy)
  131. {
  132. // cheap calc of distance from center of p0-p2 to the center of the curve
  133. SkFDot6 dist = cheap_distance(dx, dy);
  134. // shift down dist (it is currently in dot6)
  135. // down by 5 should give us 1/2 pixel accuracy (assuming our dist is accurate...)
  136. // this is chosen by heuristic: make it as big as possible (to minimize segments)
  137. // ... but small enough so that our curves still look smooth
  138. dist = (dist + (1 << 4)) >> 5;
  139. // each subdivision (shift value) cuts this dist (error) by 1/4
  140. return (32 - SkCLZ(dist)) >> 1;
  141. }
  142. int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], int shift)
  143. {
  144. SkFDot6 x0, y0, x1, y1, x2, y2;
  145. {
  146. #ifdef SK_SCALAR_IS_FLOAT
  147. float scale = float(1 << (shift + 6));
  148. x0 = int(pts[0].fX * scale);
  149. y0 = int(pts[0].fY * scale);
  150. x1 = int(pts[1].fX * scale);
  151. y1 = int(pts[1].fY * scale);
  152. x2 = int(pts[2].fX * scale);
  153. y2 = int(pts[2].fY * scale);
  154. #else
  155. shift = 10 - shift;
  156. x0 = pts[0].fX >> shift;
  157. y0 = pts[0].fY >> shift;
  158. x1 = pts[1].fX >> shift;
  159. y1 = pts[1].fY >> shift;
  160. x2 = pts[2].fX >> shift;
  161. y2 = pts[2].fY >> shift;
  162. #endif
  163. }
  164. int winding = 1;
  165. if (y0 > y2)
  166. {
  167. SkTSwap(x0, x2);
  168. SkTSwap(y0, y2);
  169. winding = -1;
  170. }
  171. SkASSERT(y0 <= y1 && y1 <= y2);
  172. int top = SkFDot6Round(y0);
  173. int bot = SkFDot6Round(y2);
  174. // are we a zero-height quad (line)?
  175. if (top == bot)
  176. return 0;
  177. // compute number of steps needed (1 << shift)
  178. {
  179. SkFDot6 dx = ((x1 << 1) - x0 - x2) >> 2;
  180. SkFDot6 dy = ((y1 << 1) - y0 - y2) >> 2;
  181. shift = diff_to_shift(dx, dy);
  182. SkASSERT(shift >= 0);
  183. }
  184. // need at least 1 subdivision for our bias trick
  185. if (shift == 0) {
  186. shift = 1;
  187. } else if (shift > MAX_COEFF_SHIFT) {
  188. shift = MAX_COEFF_SHIFT;
  189. }
  190. fWinding = SkToS8(winding);
  191. fCurveShift = SkToU8(shift);
  192. //fCubicDShift only set for cubics
  193. fCurveCount = SkToS8(1 << shift);
  194. SkFixed A = SkFDot6ToFixed(x0 - x1 - x1 + x2);
  195. SkFixed B = SkFDot6ToFixed(x1 - x0 + x1 - x0);
  196. fQx = SkFDot6ToFixed(x0);
  197. fQDx = B + (A >> shift); // biased by shift
  198. fQDDx = A >> (shift - 1); // biased by shift
  199. A = SkFDot6ToFixed(y0 - y1 - y1 + y2);
  200. B = SkFDot6ToFixed(y1 - y0 + y1 - y0);
  201. fQy = SkFDot6ToFixed(y0);
  202. fQDy = B + (A >> shift); // biased by shift
  203. fQDDy = A >> (shift - 1); // biased by shift
  204. fQLastX = SkFDot6ToFixed(x2);
  205. fQLastY = SkFDot6ToFixed(y2);
  206. return this->updateQuadratic();
  207. }
  208. int SkQuadraticEdge::updateQuadratic()
  209. {
  210. int success;
  211. int count = fCurveCount;
  212. SkFixed oldx = fQx;
  213. SkFixed oldy = fQy;
  214. SkFixed dx = fQDx;
  215. SkFixed dy = fQDy;
  216. SkFixed newx, newy;
  217. int shift = fCurveShift;
  218. SkASSERT(count > 0);
  219. do {
  220. if (--count > 0)
  221. {
  222. newx = oldx + (dx >> shift);
  223. dx += fQDDx;
  224. newy = oldy + (dy >> shift);
  225. dy += fQDDy;
  226. }
  227. else // last segment
  228. {
  229. newx = fQLastX;
  230. newy = fQLastY;
  231. }
  232. success = this->updateLine(oldx, oldy, newx, newy);
  233. oldx = newx;
  234. oldy = newy;
  235. } while (count > 0 && !success);
  236. fQx = newx;
  237. fQy = newy;
  238. fQDx = dx;
  239. fQDy = dy;
  240. fCurveCount = SkToS8(count);
  241. return success;
  242. }
  243. /////////////////////////////////////////////////////////////////////////
  244. static inline int SkFDot6UpShift(SkFDot6 x, int upShift) {
  245. SkASSERT((x << upShift >> upShift) == x);
  246. return x << upShift;
  247. }
  248. /* f(1/3) = (8a + 12b + 6c + d) / 27
  249. f(2/3) = (a + 6b + 12c + 8d) / 27
  250. f(1/3)-b = (8a - 15b + 6c + d) / 27
  251. f(2/3)-c = (a + 6b - 15c + 8d) / 27
  252. use 16/512 to approximate 1/27
  253. */
  254. static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d)
  255. {
  256. SkFDot6 oneThird = ((a << 3) - ((b << 4) - b) + 6*c + d) * 19 >> 9;
  257. SkFDot6 twoThird = (a + 6*b - ((c << 4) - c) + (d << 3)) * 19 >> 9;
  258. return SkMax32(SkAbs32(oneThird), SkAbs32(twoThird));
  259. }
  260. int SkCubicEdge::setCubic(const SkPoint pts[4], const SkIRect* clip, int shift)
  261. {
  262. SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3;
  263. {
  264. #ifdef SK_SCALAR_IS_FLOAT
  265. float scale = float(1 << (shift + 6));
  266. x0 = int(pts[0].fX * scale);
  267. y0 = int(pts[0].fY * scale);
  268. x1 = int(pts[1].fX * scale);
  269. y1 = int(pts[1].fY * scale);
  270. x2 = int(pts[2].fX * scale);
  271. y2 = int(pts[2].fY * scale);
  272. x3 = int(pts[3].fX * scale);
  273. y3 = int(pts[3].fY * scale);
  274. #else
  275. shift = 10 - shift;
  276. x0 = pts[0].fX >> shift;
  277. y0 = pts[0].fY >> shift;
  278. x1 = pts[1].fX >> shift;
  279. y1 = pts[1].fY >> shift;
  280. x2 = pts[2].fX >> shift;
  281. y2 = pts[2].fY >> shift;
  282. x3 = pts[3].fX >> shift;
  283. y3 = pts[3].fY >> shift;
  284. #endif
  285. }
  286. int winding = 1;
  287. if (y0 > y3)
  288. {
  289. SkTSwap(x0, x3);
  290. SkTSwap(x1, x2);
  291. SkTSwap(y0, y3);
  292. SkTSwap(y1, y2);
  293. winding = -1;
  294. }
  295. int top = SkFDot6Round(y0);
  296. int bot = SkFDot6Round(y3);
  297. // are we a zero-height cubic (line)?
  298. if (top == bot)
  299. return 0;
  300. // are we completely above or below the clip?
  301. if (clip && (top >= clip->fBottom || bot <= clip->fTop))
  302. return 0;
  303. // compute number of steps needed (1 << shift)
  304. {
  305. // Can't use (center of curve - center of baseline), since center-of-curve
  306. // need not be the max delta from the baseline (it could even be coincident)
  307. // so we try just looking at the two off-curve points
  308. SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3);
  309. SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3);
  310. // add 1 (by observation)
  311. shift = diff_to_shift(dx, dy) + 1;
  312. }
  313. // need at least 1 subdivision for our bias trick
  314. SkASSERT(shift > 0);
  315. if (shift > MAX_COEFF_SHIFT) {
  316. shift = MAX_COEFF_SHIFT;
  317. }
  318. /* Since our in coming data is initially shifted down by 10 (or 8 in
  319. antialias). That means the most we can shift up is 8. However, we
  320. compute coefficients with a 3*, so the safest upshift is really 6
  321. */
  322. int upShift = 6; // largest safe value
  323. int downShift = shift + upShift - 10;
  324. if (downShift < 0) {
  325. downShift = 0;
  326. upShift = 10 - shift;
  327. }
  328. fWinding = SkToS8(winding);
  329. fCurveCount = SkToS8(-1 << shift);
  330. fCurveShift = SkToU8(shift);
  331. fCubicDShift = SkToU8(downShift);
  332. SkFixed B = SkFDot6UpShift(3 * (x1 - x0), upShift);
  333. SkFixed C = SkFDot6UpShift(3 * (x0 - x1 - x1 + x2), upShift);
  334. SkFixed D = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift);
  335. fCx = SkFDot6ToFixed(x0);
  336. fCDx = B + (C >> shift) + (D >> 2*shift); // biased by shift
  337. fCDDx = 2*C + (3*D >> (shift - 1)); // biased by 2*shift
  338. fCDDDx = 3*D >> (shift - 1); // biased by 2*shift
  339. B = SkFDot6UpShift(3 * (y1 - y0), upShift);
  340. C = SkFDot6UpShift(3 * (y0 - y1 - y1 + y2), upShift);
  341. D = SkFDot6UpShift(y3 + 3 * (y1 - y2) - y0, upShift);
  342. fCy = SkFDot6ToFixed(y0);
  343. fCDy = B + (C >> shift) + (D >> 2*shift); // biased by shift
  344. fCDDy = 2*C + (3*D >> (shift - 1)); // biased by 2*shift
  345. fCDDDy = 3*D >> (shift - 1); // biased by 2*shift
  346. fCLastX = SkFDot6ToFixed(x3);
  347. fCLastY = SkFDot6ToFixed(y3);
  348. if (clip)
  349. {
  350. do {
  351. if (!this->updateCubic()) {
  352. return 0;
  353. }
  354. } while (!this->intersectsClip(*clip));
  355. this->chopLineWithClip(*clip);
  356. return 1;
  357. }
  358. return this->updateCubic();
  359. }
  360. int SkCubicEdge::updateCubic()
  361. {
  362. int success;
  363. int count = fCurveCount;
  364. SkFixed oldx = fCx;
  365. SkFixed oldy = fCy;
  366. SkFixed newx, newy;
  367. const int ddshift = fCurveShift;
  368. const int dshift = fCubicDShift;
  369. SkASSERT(count < 0);
  370. do {
  371. if (++count < 0)
  372. {
  373. newx = oldx + (fCDx >> dshift);
  374. fCDx += fCDDx >> ddshift;
  375. fCDDx += fCDDDx;
  376. newy = oldy + (fCDy >> dshift);
  377. fCDy += fCDDy >> ddshift;
  378. fCDDy += fCDDDy;
  379. }
  380. else // last segment
  381. {
  382. // SkDebugf("LastX err=%d, LastY err=%d\n", (oldx + (fCDx >> shift) - fLastX), (oldy + (fCDy >> shift) - fLastY));
  383. newx = fCLastX;
  384. newy = fCLastY;
  385. }
  386. success = this->updateLine(oldx, oldy, newx, newy);
  387. oldx = newx;
  388. oldy = newy;
  389. } while (count < 0 && !success);
  390. fCx = newx;
  391. fCy = newy;
  392. fCurveCount = SkToS8(count);
  393. return success;
  394. }