subPixelOptimize.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /**
  2. * Sub-pixel optimize for canvas rendering, prevent from blur
  3. * when rendering a thin vertical/horizontal line.
  4. */
  5. var round = Math.round;
  6. /**
  7. * Sub pixel optimize line for canvas
  8. *
  9. * @param {Object} outputShape The modification will be performed on `outputShape`.
  10. * `outputShape` and `inputShape` can be the same object.
  11. * `outputShape` object can be used repeatly, because all of
  12. * the `x1`, `x2`, `y1`, `y2` will be assigned in this method.
  13. * @param {Object} [inputShape]
  14. * @param {number} [inputShape.x1]
  15. * @param {number} [inputShape.y1]
  16. * @param {number} [inputShape.x2]
  17. * @param {number} [inputShape.y2]
  18. * @param {Object} [style]
  19. * @param {number} [style.lineWidth] If `null`/`undefined`/`0`, do not optimize.
  20. */
  21. function subPixelOptimizeLine(outputShape, inputShape, style) {
  22. if (!inputShape) {
  23. return;
  24. }
  25. var x1 = inputShape.x1;
  26. var x2 = inputShape.x2;
  27. var y1 = inputShape.y1;
  28. var y2 = inputShape.y2;
  29. outputShape.x1 = x1;
  30. outputShape.x2 = x2;
  31. outputShape.y1 = y1;
  32. outputShape.y2 = y2;
  33. var lineWidth = style && style.lineWidth;
  34. if (!lineWidth) {
  35. return;
  36. }
  37. if (round(x1 * 2) === round(x2 * 2)) {
  38. outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true);
  39. }
  40. if (round(y1 * 2) === round(y2 * 2)) {
  41. outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true);
  42. }
  43. }
  44. /**
  45. * Sub pixel optimize rect for canvas
  46. *
  47. * @param {Object} outputShape The modification will be performed on `outputShape`.
  48. * `outputShape` and `inputShape` can be the same object.
  49. * `outputShape` object can be used repeatly, because all of
  50. * the `x`, `y`, `width`, `height` will be assigned in this method.
  51. * @param {Object} [inputShape]
  52. * @param {number} [inputShape.x]
  53. * @param {number} [inputShape.y]
  54. * @param {number} [inputShape.width]
  55. * @param {number} [inputShape.height]
  56. * @param {Object} [style]
  57. * @param {number} [style.lineWidth] If `null`/`undefined`/`0`, do not optimize.
  58. */
  59. function subPixelOptimizeRect(outputShape, inputShape, style) {
  60. if (!inputShape) {
  61. return;
  62. }
  63. var originX = inputShape.x;
  64. var originY = inputShape.y;
  65. var originWidth = inputShape.width;
  66. var originHeight = inputShape.height;
  67. outputShape.x = originX;
  68. outputShape.y = originY;
  69. outputShape.width = originWidth;
  70. outputShape.height = originHeight;
  71. var lineWidth = style && style.lineWidth;
  72. if (!lineWidth) {
  73. return;
  74. }
  75. outputShape.x = subPixelOptimize(originX, lineWidth, true);
  76. outputShape.y = subPixelOptimize(originY, lineWidth, true);
  77. outputShape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x, originWidth === 0 ? 0 : 1);
  78. outputShape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y, originHeight === 0 ? 0 : 1);
  79. }
  80. /**
  81. * Sub pixel optimize for canvas
  82. *
  83. * @param {number} position Coordinate, such as x, y
  84. * @param {number} lineWidth If `null`/`undefined`/`0`, do not optimize.
  85. * @param {boolean=} positiveOrNegative Default false (negative).
  86. * @return {number} Optimized position.
  87. */
  88. function subPixelOptimize(position, lineWidth, positiveOrNegative) {
  89. if (!lineWidth) {
  90. return position;
  91. } // Assure that (position + lineWidth / 2) is near integer edge,
  92. // otherwise line will be fuzzy in canvas.
  93. var doubledPosition = round(position * 2);
  94. return (doubledPosition + round(lineWidth)) % 2 === 0 ? doubledPosition / 2 : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
  95. }
  96. exports.subPixelOptimizeLine = subPixelOptimizeLine;
  97. exports.subPixelOptimizeRect = subPixelOptimizeRect;
  98. exports.subPixelOptimize = subPixelOptimize;