UNPKG

zrender

Version:

A lightweight graphic library providing 2d draw for Apache ECharts

135 lines (118 loc) 3.86 kB
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; }