Skip to content

Commit

Permalink
Basic merging working
Browse files Browse the repository at this point in the history
Part of xtermjs#4243
  • Loading branch information
Tyriar committed Nov 1, 2022
1 parent 02fa38c commit 54ee698
Showing 1 changed file with 115 additions and 3 deletions.
118 changes: 115 additions & 3 deletions src/browser/renderer/shared/TextureAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,112 @@ export class TextureAtlas implements ITextureAtlas {
this._didWarmUp = false;
}

private _hasMerged = false;

private _createNewPage(): AtlasPage {
if (this._pages.length === 4 || this._pages.length === 7) {
this._increaseTextureSize();
// if (this._pages.length === 4 || this._pages.length === 7) {
// this._increaseTextureSize();
// }

if (!this._hasMerged && this._pages.length === 6) {
this._hasMerged = true;
console.log('try merge');
console.time('merge');

// TODO: Track the most filled pages (pixels used of total) and use them?

// Migrate over 1 page at a time due to the time it takes to iterate over glyphs

// Get the 4 most used pages
// TODO: This is slow, need to sort after slice so _pages doesn't get sorted
const mergingPages = this._pages.slice().sort((a, b) => a.percentageUsed < b.percentageUsed ? 1 : -1).slice(0, 4);
const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort();
// TODO: Pull texture page index in a nicer way
const mergedPageIndex = sortedMergingPagesIndexes[0];
const mergedPage = this._mergePages(mergingPages, mergedPageIndex);

console.timeEnd('merge');

(console as any).image(mergedPage.canvas);

mergedPage.hasCanvasChanged = true;
// this._pages[0] = mergedPage;
// Replace an old merging page with the merged
this._pages[mergedPageIndex] = mergedPage;

console.log('before adjust', this._pages);

console.log({ mergedPageIndex });

// TODO: Splice other 3 pages, shifting all other texture page props
for (let i = sortedMergingPagesIndexes.length - 1; i >= 1; i--) {
// TODO: Optimize, this is slow
const mergingPageIndex = sortedMergingPagesIndexes[i];

console.log('splice', mergingPageIndex);
this._pages.splice(mergingPageIndex, 1);
for (let j = mergingPageIndex; j < this._pages.length; j++) {
const adjustingPage = this._pages[j];
console.log('adjust', j);
// if (mergingPages.includes(adjustingPage)) {
// continue;
// }
for (const g of adjustingPage.glyphs) {
g.texturePage--;
}
adjustingPage.hasCanvasChanged = true;
}
}

this._onAddTextureAtlasCanvas.fire(mergedPage.canvas);

// TODO: Force a refresh because atlas pages changed?

// Continue with creating the new page as a glyph may be getting drawn

// mergedPage.ctx.drawImage(this._pages[1].canvas, this._textureSize, 0);
// mergedPage.ctx.drawImage(this._pages[2].canvas, 0, this._textureSize);
// mergedPage.ctx.drawImage(this._pages[3].canvas, this._textureSize, this._textureSize);
}

// TODO: Ensure pages aren't created beyond the maximum supported
const newPage = new AtlasPage(this._document, this._textureSize);
this._pages.push(newPage);
console.log('pages', this._pages);
this._activePages.push(newPage);
this._onAddTextureAtlasCanvas.fire(newPage.canvas);
return newPage;
}

private _mergePages(mergingPages: AtlasPage[], mergedPageIndex: number): AtlasPage {
const mergedSize = this._textureSize * 2;
const mergedPage = new AtlasPage(this._document, mergedSize, mergingPages);
for (const [i, p] of mergingPages.entries()) {
const xOffset = i * p.canvas.width % mergedSize;
const yOffset = Math.floor(i / 2) * p.canvas.height;
mergedPage.ctx.drawImage(p.canvas, xOffset, yOffset);
for (const g of p.glyphs) {
g.texturePage = mergedPageIndex;
g.sizeClipSpace.x = g.size.x / mergedSize;
g.sizeClipSpace.y = g.size.y / mergedSize;
g.texturePosition.x += xOffset;
g.texturePosition.y += yOffset;
g.texturePositionClipSpace.x = g.texturePosition.x / mergedSize;
g.texturePositionClipSpace.y = g.texturePosition.y / mergedSize;
}

p.ctx.clearRect(0, 0, p.canvas.width, p.canvas.height);
p.ctx.fillText('merged', 100, 100);

// Remove the merging page from active pages if it was there
const index = this._activePages.indexOf(p);
if (index !== -1) {
this._activePages.splice(index, 1);
}
}
return mergedPage;
}

/**
* Doubles the texture size of new atlas pages if allowed.
*/
Expand Down Expand Up @@ -730,6 +824,7 @@ export class TextureAtlas implements ITextureAtlas {
rasterizedGlyph.size.x,
rasterizedGlyph.size.y
);
activePage.addGlyph(rasterizedGlyph);
activePage.hasCanvasChanged = true;

return rasterizedGlyph;
Expand Down Expand Up @@ -829,6 +924,16 @@ class AtlasPage {
public readonly canvas: HTMLCanvasElement;
public readonly ctx: CanvasRenderingContext2D;

private _usedPixels: number = 0;
public get percentageUsed(): number { return this._usedPixels / (this.canvas.width * this.canvas.height); }

private readonly _glyphs: IRasterizedGlyph[] = [];
public get glyphs(): ReadonlyArray<IRasterizedGlyph> { return this._glyphs; }
public addGlyph(glyph: IRasterizedGlyph): void {
this._glyphs.push(glyph);
this._usedPixels += glyph.size.x * glyph.size.y;
}

/**
* Whether the canvas of the atlas page has changed, this is only set to true by the atlas, the
* user of the boolean is required to reset its value to false.
Expand All @@ -854,8 +959,15 @@ class AtlasPage {

constructor(
document: Document,
size: number
size: number,
sourcePages?: AtlasPage[]
) {
if (sourcePages) {
for (const p of sourcePages) {
this._glyphs.push(...p.glyphs);
this._usedPixels += p._usedPixels;
}
}
this.canvas = createCanvas(document, size, size);
// The canvas needs alpha because we use clearColor to convert the background color to alpha.
// It might also contain some characters with transparent backgrounds if allowTransparency is
Expand Down

0 comments on commit 54ee698

Please sign in to comment.