Skip to content

Commit

Permalink
fix: merge cells background
Browse files Browse the repository at this point in the history
fix: #676

test: #677

fix: #700

fix: aux line background

fix: spreadsheet

fix: precise rect for background
  • Loading branch information
lumixraku committed May 21, 2024
1 parent c63ffb6 commit a6518ae
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,5 @@ export abstract class DocComponent extends RenderComponent<
return false;
}

protected abstract _draw(ctx: UniverRenderingContext, bounds?: IViewportBound): void;
protected abstract _draw(ctx: UniverRenderingContext, bounds?: IViewportInfo): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import type { IRange, IScale } from '@univerjs/core';

import { getColor, inViewRanges } from '../../../basics/tools';
import { fixLineWidthByScale, getColor, inViewRanges, mergeRangeIfIntersects } from '../../../basics/tools';
import type { UniverRenderingContext } from '../../../context';
import { SpreadsheetExtensionRegistry } from '../../extension';
import type { SpreadsheetSkeleton } from '../sheet-skeleton';
Expand All @@ -29,8 +29,8 @@ const UNIQUE_KEY = 'DefaultBackgroundExtension';
* in prev version background ext is higer than font ext. now turing back lower than font ext.
* font ext zindex is 30.
*/
const DOC_EXTENSION_Z_INDEX = 20;
const PRINTING_Z_INDEX = 20;
const DOC_EXTENSION_Z_INDEX = 21;
const PRINTING_Z_INDEX = 21;

export class Background extends SheetExtension {
override uKey = UNIQUE_KEY;
Expand All @@ -48,7 +48,7 @@ export class Background extends SheetExtension {
parentScale: IScale,
spreadsheetSkeleton: SpreadsheetSkeleton,
diffRanges: IRange[],
{ viewRanges }: { viewRanges?: IRange[]; diffRanges?: IRange[]; checkOutOfViewBound?: boolean }
{ viewRanges, checkOutOfViewBound }: { viewRanges: IRange[]; checkOutOfViewBound: boolean; viewPortKey: string }
) {
const { stylesCache } = spreadsheetSkeleton;
const { background, backgroundPositions } = stylesCache;
Expand All @@ -69,6 +69,7 @@ export class Background extends SheetExtension {
}
ctx.save();
// ctx.setGlobalCompositeOperation('destination-over');
const { scaleX, scaleY } = ctx.getScale();
background &&
Object.keys(background).forEach((rgb: string) => {
const backgroundCache = background[rgb];
Expand All @@ -77,34 +78,42 @@ export class Background extends SheetExtension {

const backgroundPaths = new Path2D(); // 使用 Path 对象代替原有的 ctx.moveTo ctx.lineTo, Path 性能更好
backgroundCache.forValue((rowIndex, columnIndex) => {
// 当前单元格不在视野范围内, 提前退出
// 和 font 不同的是, 不需要考虑合并单元格且单元格横跨 viewport 的情况.
// 因为即使合并后, 也会进入 forValue 迭代, 此刻单元格状态是 isMerged, 也能从 cellInfo 中获取颜色信息
// 同时, 后续绘制使用了 Path2D, 而不是 ctx.lineTo moveTo 这样的方式绘制, 效率上高很多, 不再有必要判断是否合并
if (!inViewRanges(viewRanges!, rowIndex, columnIndex)) {
if (!checkOutOfViewBound && !inViewRanges(viewRanges, rowIndex, columnIndex)) {
return true;
}
const cellInfo = backgroundPositions?.getValue(rowIndex, columnIndex);

// 合并单元格可能从视野外很远的位置开始, 因此需要全局遍历
const cellInfo = backgroundPositions?.getValue(rowIndex, columnIndex);
if (cellInfo == null) {
return true;
}
const { startY, endY, startX, endX } = cellInfo;
// const { isMerged, isMergedMainCell, mergeInfo } = cellInfo;

// // 合并后的单元格, 非左上角单元格(因为在)
// if (isMerged) {
// return true;
// }
// // 合并单元格, 但是区域是合并的左上角
// if (isMergedMainCell) {
// startY = mergeInfo.startY;
// endY = mergeInfo.endY;
// startX = mergeInfo.startX;
// endX = mergeInfo.endX;
// }

backgroundPaths.rect(startX, startY, endX - startX, endY - startY);
let { startY, endY, startX, endX } = cellInfo;
const { isMerged, isMergedMainCell, mergeInfo } = cellInfo;
const mergeTo = diffRanges && diffRanges.length > 0 ? diffRanges : viewRanges;
const combineWithMergeRanges = mergeRangeIfIntersects(mergeTo, [mergeInfo]);

// 不在范围(视野 + 合并单元格)内, 提前退出
if (!inViewRanges(combineWithMergeRanges!, rowIndex, columnIndex)) {
return true;
}

// 合并后的单元格 && 非左上角单元格 // 此刻需要使用左上角单元格的背景色
if (isMerged) {
return true;
}
// 合并单元格, 但是区域是合并的左上角
if (isMergedMainCell) {
startY = mergeInfo.startY;
endY = mergeInfo.endY;
startX = mergeInfo.startX;
endX = mergeInfo.endX;
}
// precise is a workaround for windows, macOS does not have this issue.
const startXPrecise = fixLineWidthByScale(startX, scaleX);
const startYPrecise = fixLineWidthByScale(startY, scaleY);
const endXPrecise = fixLineWidthByScale(endX, scaleX);
const endYPrecise = fixLineWidthByScale(endY, scaleY);
backgroundPaths.rect(startXPrecise, startYPrecise, endXPrecise - startXPrecise, endYPrecise - startYPrecise);
});
ctx.fill(backgroundPaths);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class Border extends SheetExtension {

override Z_INDEX = BORDER_Z_INDEX;

// eslint-disable-next-line max-lines-per-function
override draw(
ctx: UniverRenderingContext,
parentScale: IScale,
Expand Down Expand Up @@ -66,6 +67,7 @@ export class Border extends SheetExtension {

const precisionScale = this._getScale(ctx.getScale());

// eslint-disable-next-line max-lines-per-function
border?.forValue((rowIndex, columnIndex, borderCaches) => {
if (!borderCaches) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class Font extends SheetExtension {
diffRanges: IRange[],
moreBoundsInfo: { viewRanges: IRange[]; checkOutOfViewBound?: boolean; viewPortKey: string }
) {
const { viewRanges = [], checkOutOfViewBound, viewPortKey } = moreBoundsInfo;
const { viewRanges = [], checkOutOfViewBound } = moreBoundsInfo;
const { stylesCache, dataMergeCache, overflowCache, worksheet } = spreadsheetSkeleton;
const { font: fontList } = stylesCache;
if (!spreadsheetSkeleton || !worksheet) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export abstract class SheetComponent extends RenderComponent<SpreadsheetSkeleton
endColumn: number
): Nullable<{ startRow: number; startColumn: number; endRow: number; endColumn: number }>;

protected abstract _draw(ctx: UniverRenderingContext, bounds?: IViewportBound): void;
protected abstract _draw(ctx: UniverRenderingContext, bounds?: IViewportInfo): void;

/**
* TODO: DR-Univer, fix as unknown as
Expand All @@ -94,7 +94,7 @@ export abstract class SheetComponent extends RenderComponent<SpreadsheetSkeleton
}

export abstract class SpreadsheetHeader extends SheetComponent {
protected override _draw(ctx: UniverRenderingContext, bounds?: IViewportBound): void {
protected override _draw(ctx: UniverRenderingContext, bounds?: IViewportInfo): void {
this.draw(ctx, bounds);
}
}
64 changes: 30 additions & 34 deletions packages/engine-render/src/components/sheets/spreadsheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ export class Spreadsheet extends SheetComponent {
* diffRange 根据 diffCacheBounds 得到
* @param ctx
* @param viewportInfo
* @returns
*/
override draw(ctx: UniverRenderingContext, viewportInfo: IViewportInfo) {
// const { parent = { scaleX: 1, scaleY: 1 } } = this;
Expand All @@ -128,23 +127,22 @@ export class Spreadsheet extends SheetComponent {
if (!spreadsheetSkeleton) {
return;
}

this._drawAuxiliary(ctx);
const parentScale = this.getParentScale();

const diffRanges = this._refreshIncrementalState && viewportInfo?.diffCacheBounds
? viewportInfo?.diffBounds?.map((bound) => spreadsheetSkeleton.getRowColumnSegmentByViewBound(bound))
: undefined;
const viewRanges = [spreadsheetSkeleton.getRowColumnSegmentByViewBound(viewportInfo?.cacheBound)];
const extensions = this.getExtensionsByOrder();

for (const extension of extensions) {
const m = ctx.getTransform();
ctx.setTransform(m.a, m.b, m.c, m.d, Math.ceil(m.e), Math.ceil(m.f));
// const timeKey = `extension ${viewportInfo.viewPortKey}:${extension.constructor.name}`;
// console.time(timeKey);
extension.draw(ctx, parentScale, spreadsheetSkeleton, diffRanges, {
viewRanges,
diffRanges,
checkOutOfViewBound: true,
});
// console.timeEnd(timeKey);
}
}

Expand Down Expand Up @@ -216,6 +214,7 @@ export class Spreadsheet extends SheetComponent {
* @param state
*/
makeForceDirty(state = true) {
this.makeDirty(state);
this._forceDirty = state;
}

Expand All @@ -231,8 +230,8 @@ export class Spreadsheet extends SheetComponent {
* @param state
*/
override makeDirty(state: boolean = true) {
(this.getParent() as Scene)?.getViewports().forEach((vp) => vp.markDirty(state));
super.makeDirty(state);
(this.getParent() as Scene)?.getViewports().forEach((vp) => vp.markDirty());
if (state === false) {
this._dirtyBounds = [];
}
Expand All @@ -243,34 +242,34 @@ export class Spreadsheet extends SheetComponent {
this._dirtyBounds = dirtyBounds;
}

renderByViewport(mainCtx: UniverRenderingContext, viewportBoundsInfo: IViewportInfo, spreadsheetSkeleton: SpreadsheetSkeleton) {
const { diffBounds, diffX, diffY, viewPortPosition, isDirty, isForceDirty, cacheCanvas, leftOrigin, topOrigin, bufferEdgeX, bufferEdgeY } = viewportBoundsInfo as Required<IViewportInfo>;
renderByViewport(mainCtx: UniverRenderingContext, viewportInfo: IViewportInfo, spreadsheetSkeleton: SpreadsheetSkeleton) {
const { diffBounds, diffX, diffY, viewPortPosition, cacheCanvas, leftOrigin, topOrigin, bufferEdgeX, bufferEdgeY, isDirty: isViewportDirty, isForceDirty: isViewportForceDirty } = viewportInfo as Required<IViewportInfo>;
const { rowHeaderWidth, columnHeaderHeight } = spreadsheetSkeleton;
const { a: scaleX = 1, d: scaleY = 1 } = mainCtx.getTransform();
const bufferEdgeSizeX = bufferEdgeX * scaleX / window.devicePixelRatio;
const bufferEdgeSizeY = bufferEdgeY * scaleY / window.devicePixelRatio;

const cacheCtx = cacheCanvas.getContext();
cacheCtx.save();

const { left, top, right, bottom } = viewPortPosition;
const dw = right - left + rowHeaderWidth;
const dh = bottom - top + columnHeaderHeight;

const isForceDirty = isViewportForceDirty || this.isForceDirty();
const isDirty = isViewportDirty || this.isDirty();
if (diffBounds.length === 0 || (diffX === 0 && diffY === 0) || isForceDirty || isDirty) {
if (isDirty || isForceDirty) {
this.refreshCacheCanvas(viewportBoundsInfo, { cacheCanvas, cacheCtx, mainCtx, topOrigin, leftOrigin, bufferEdgeX, bufferEdgeY });
this.refreshCacheCanvas(viewportInfo, { cacheCanvas, cacheCtx, mainCtx, topOrigin, leftOrigin, bufferEdgeX, bufferEdgeY });
}
} else if (diffBounds.length !== 0 || diffX !== 0 || diffY === 0) {
} else if (diffBounds.length !== 0 || diffX !== 0 || diffY !== 0) {
// scrolling && no dirty
this.paintNewAreaOfCacheCanvas(viewportBoundsInfo, {
this.paintNewAreaOfCacheCanvas(viewportInfo, {
cacheCanvas, cacheCtx, mainCtx, topOrigin, leftOrigin, bufferEdgeX, bufferEdgeY, scaleX, scaleY, columnHeaderHeight, rowHeaderWidth,
});
}
// support for browser native zoom
// support for browser native zoom (only windows has this problem)
const sourceLeft = bufferEdgeSizeX * Math.min(1, window.devicePixelRatio);
const sourceTop = bufferEdgeSizeY * Math.min(1, window.devicePixelRatio);
this._applyCache(mainCtx, cacheCanvas, sourceLeft, sourceTop, dw, dh, left, top, dw, dh);
this._applyCache(cacheCanvas, mainCtx, sourceLeft, sourceTop, dw, dh, left, top, dw, dh);
cacheCtx.restore();
}

Expand All @@ -287,7 +286,6 @@ export class Spreadsheet extends SheetComponent {
}) {
const { cacheCanvas, cacheCtx, mainCtx, topOrigin, leftOrigin, bufferEdgeX, bufferEdgeY, scaleX, scaleY, columnHeaderHeight, rowHeaderWidth } = param;
const { shouldCacheUpdate, diffCacheBounds, diffX, diffY } = viewportBoundsInfo;

cacheCtx.save();
cacheCtx.setTransform(1, 0, 0, 1, 0, 0);
cacheCtx.globalCompositeOperation = 'copy';
Expand Down Expand Up @@ -344,7 +342,7 @@ export class Spreadsheet extends SheetComponent {
/**
* 整个 viewport 重绘
*/
refreshCacheCanvas(viewportBoundsInfo: IViewportInfo, param: {
refreshCacheCanvas(viewportInfo: IViewportInfo, param: {
cacheCanvas: Canvas; cacheCtx: UniverRenderingContext; mainCtx: UniverRenderingContext;
topOrigin: number;
leftOrigin: number;
Expand All @@ -368,7 +366,7 @@ export class Spreadsheet extends SheetComponent {
cacheCtx.translateWithPrecision(m.e / m.a - leftOrigin + bufferEdgeX, m.f / m.d - topOrigin + bufferEdgeY);

// extension 绘制时按照内容的左上角计算, 不考虑 rowHeaderWidth
this.draw(cacheCtx, viewportBoundsInfo);
this.draw(cacheCtx, viewportInfo);
cacheCtx.restore();
}

Expand All @@ -383,7 +381,6 @@ export class Spreadsheet extends SheetComponent {
if (!spreadsheetSkeleton) {
return;
}

spreadsheetSkeleton.calculateWithoutClearingCache(viewportInfo);

const segment = spreadsheetSkeleton.rowColumnSegment;
Expand All @@ -394,14 +391,12 @@ export class Spreadsheet extends SheetComponent {
) {
return;
}

mainCtx.save();


const { rowHeaderWidth, columnHeaderHeight } = spreadsheetSkeleton;
mainCtx.translateWithPrecision(rowHeaderWidth, columnHeaderHeight);

this._drawAuxiliary(mainCtx, viewportInfo);

const { viewPortKey } = viewportInfo;
// scene --> layer, getObjects --> viewport.render(object) --> spreadsheet
Expand Down Expand Up @@ -434,11 +429,10 @@ export class Spreadsheet extends SheetComponent {
* @param dy
* @param dw
* @param dh
* @returns
*/
protected _applyCache(
ctx: UniverRenderingContext,
cacheCanvas: Canvas,
ctx: UniverRenderingContext,
sx: number = 0,
sy: number = 0,
sw: number = 0,
Expand All @@ -453,21 +447,25 @@ export class Spreadsheet extends SheetComponent {
}

const pixelRatio = cacheCanvas.getPixelRatio();

const cacheCtx = cacheCanvas.getContext();
cacheCtx.save();
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
cacheCtx.setTransform(1, 0, 0, 1, 0, 0);
const fn = (num: number, scale: number) => {
return Math.round(num * scale);
};
ctx.imageSmoothingEnabled = false;
// ctx.imageSmoothingEnabled = true;
// ctx.imageSmoothingQuality = 'high';
ctx.drawImage(
cacheCanvas.getCanvasEle(),
sx * pixelRatio,
sy * pixelRatio,
fn(sx, pixelRatio),
fn(sy, pixelRatio),
sw * pixelRatio,
sh * pixelRatio,
dx * pixelRatio,
dy * pixelRatio,
fn(dx, pixelRatio),
fn(dy, pixelRatio),
dw * pixelRatio,
dh * pixelRatio
);
Expand Down Expand Up @@ -526,17 +524,15 @@ export class Spreadsheet extends SheetComponent {
* draw gridlines
* @param ctx
* @param bounds
* @returns
*/
// eslint-disable-next-line max-lines-per-function
private _drawAuxiliary(ctx: UniverRenderingContext, bounds?: IViewportInfo) {
private _drawAuxiliary(ctx: UniverRenderingContext) {
const spreadsheetSkeleton = this.getSkeleton();
if (spreadsheetSkeleton == null) {
return;
}

const { rowColumnSegment, dataMergeCache, overflowCache, stylesCache, showGridlines } = spreadsheetSkeleton;
const { border, backgroundPositions } = stylesCache;
const { rowColumnSegment, dataMergeCache, overflowCache, showGridlines } = spreadsheetSkeleton;
const { startRow, endRow, startColumn, endColumn } = rowColumnSegment;
if (!spreadsheetSkeleton || showGridlines === BooleanNumber.FALSE || this._forceDisableGridlines) {
return;
Expand Down Expand Up @@ -651,7 +647,7 @@ export class Spreadsheet extends SheetComponent {
// overflow cell
this._clearRectangle(ctx, rowHeightAccumulation, columnWidthAccumulation, overflowCache.toNativeArray());

this._clearBackground(ctx, backgroundPositions);
// this._clearBackground(ctx, backgroundPositions);

ctx.restore();
}
Expand Down
Loading

0 comments on commit a6518ae

Please sign in to comment.