| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- import { PathStyleProps } from '../Path';
- /**
- * Sub-pixel optimize for canvas rendering, prevent from blur
- * when rendering a thin vertical/horizontal line.
- */
- const round = Math.round;
- type LineShape = {
- x1: number
- y1: number
- x2: number
- y2: number
- }
- type RectShape = {
- x: number
- y: number
- width: number
- height: number
- r?: number | number[]
- }
- /**
- * Sub pixel optimize line for canvas
- *
- * @param outputShape The modification will be performed on `outputShape`.
- * `outputShape` and `inputShape` can be the same object.
- * `outputShape` object can be used repeatly, because all of
- * the `x1`, `x2`, `y1`, `y2` will be assigned in this method.
- */
- export function subPixelOptimizeLine(
- outputShape: Partial<LineShape>,
- inputShape: LineShape,
- style: Pick<PathStyleProps, 'lineWidth'> // DO not optimize when lineWidth is 0
- ): LineShape {
- if (!inputShape) {
- return;
- }
- const x1 = inputShape.x1;
- const x2 = inputShape.x2;
- const y1 = inputShape.y1;
- const y2 = inputShape.y2;
- outputShape.x1 = x1;
- outputShape.x2 = x2;
- outputShape.y1 = y1;
- outputShape.y2 = y2;
- const lineWidth = style && style.lineWidth;
- if (!lineWidth) {
- return outputShape as LineShape;
- }
- if (round(x1 * 2) === round(x2 * 2)) {
- outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true);
- }
- if (round(y1 * 2) === round(y2 * 2)) {
- outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true);
- }
- return outputShape as LineShape;
- }
- /**
- * Sub pixel optimize rect for canvas
- *
- * @param outputShape The modification will be performed on `outputShape`.
- * `outputShape` and `inputShape` can be the same object.
- * `outputShape` object can be used repeatly, because all of
- * the `x`, `y`, `width`, `height` will be assigned in this method.
- */
- export function subPixelOptimizeRect(
- outputShape: Partial<RectShape>,
- inputShape: RectShape,
- style: Pick<PathStyleProps, 'lineWidth'> // DO not optimize when lineWidth is 0
- ): RectShape {
- if (!inputShape) {
- return;
- }
- const originX = inputShape.x;
- const originY = inputShape.y;
- const originWidth = inputShape.width;
- const originHeight = inputShape.height;
- outputShape.x = originX;
- outputShape.y = originY;
- outputShape.width = originWidth;
- outputShape.height = originHeight;
- const lineWidth = style && style.lineWidth;
- if (!lineWidth) {
- return outputShape as RectShape;
- }
- outputShape.x = subPixelOptimize(originX, lineWidth, true);
- outputShape.y = subPixelOptimize(originY, lineWidth, true);
- outputShape.width = Math.max(
- subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x,
- originWidth === 0 ? 0 : 1
- );
- outputShape.height = Math.max(
- subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y,
- originHeight === 0 ? 0 : 1
- );
- return outputShape as RectShape;
- }
- /**
- * Sub pixel optimize for canvas
- *
- * @param position Coordinate, such as x, y
- * @param lineWidth If `null`/`undefined`/`0`, do not optimize.
- * @param positiveOrNegative Default false (negative).
- * @return Optimized position.
- */
- export function subPixelOptimize(
- position: number,
- lineWidth?: number,
- positiveOrNegative?: boolean
- ) {
- if (!lineWidth) {
- return position;
- }
- // Assure that (position + lineWidth / 2) is near integer edge,
- // otherwise line will be fuzzy in canvas.
- const doubledPosition = round(position * 2);
- return (doubledPosition + round(lineWidth)) % 2 === 0
- ? doubledPosition / 2
- : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
- }
|