Skip to content

Commit

Permalink
Fix canvas context leaks (#10)
Browse files Browse the repository at this point in the history
* Update packages
* Update API version
* get canvas context once
  • Loading branch information
MrMeison authored and ignatvilesov committed Jun 21, 2017
1 parent 599911a commit 0739310
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 133 deletions.
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
"fileMatch": [
"/pbiviz.json"
],
"url": "./.api/v1.5.0/schema.pbiviz.json"
"url": "./.api/v1.7.0/schema.pbiviz.json"
},
{
"fileMatch": [
"/capabilities.json"
],
"url": "./.api/v1.5.0/schema.capabilities.json"
"url": "./.api/v1.7.0/schema.capabilities.json"
}
]
}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.2.12
* FIX. memory leak: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': Out of memory at ImageData creation
* UPDATE. Updated package dependencies

## 1.2.11
* FIX. Stop word doesn't work if Word-breaking turned off
* FIX. Visual always was removing special characters. Added "Special characters" boolean property to "General" tab which will be control removing spesial characters
32 changes: 16 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "powerbi-visuals-wordcloud",
"version": "1.2.11",
"version": "1.2.12",
"description": "Word Cloud is a visual representation of word frequency and value. Use it to get instant insight into the most important terms in a set.",
"repository": {
"type": "git",
Expand All @@ -10,7 +10,7 @@
"powerbi-visuals"
],
"scripts": {
"postinstall": "pbiviz update 1.5.0",
"postinstall": "pbiviz update 1.7.0",
"pbiviz": "pbiviz",
"start": "pbiviz start",
"package": "pbiviz package",
Expand All @@ -29,30 +29,30 @@
"homepage": "https://github.com/Microsoft/PowerBI-visuals-WordCloud#readme",
"dependencies": {
"d3": "3.5.5",
"lodash": "4.16.2",
"powerbi-visuals-utils-colorutils": "0.2.1",
"powerbi-visuals-utils-dataviewutils": "1.0.1",
"powerbi-visuals-utils-formattingutils": "0.2.2",
"powerbi-visuals-utils-svgutils": "0.2.1",
"powerbi-visuals-utils-typeutils": "0.2.1"
"lodash": "4.17.4",
"powerbi-visuals-utils-colorutils": "^1.0.0",
"powerbi-visuals-utils-dataviewutils": "^1.0.0",
"powerbi-visuals-utils-formattingutils": "^1.0.0",
"powerbi-visuals-utils-svgutils": "^1.0.0",
"powerbi-visuals-utils-typeutils": "^1.0.0"
},
"devDependencies": {
"@types/d3": "3.5.36",
"@types/jasmine": "2.5.43",
"@types/jasmine-jquery": "1.5.29",
"@types/jquery": "2.0.41",
"@types/jquery": "2.0.46",
"@types/lodash": "4.14.55",
"jasmine": "2.5.3",
"jasmine": "2.6.0",
"jasmine-jquery": "2.1.1",
"karma": "1.5.0",
"karma-chrome-launcher": "2.0.0",
"karma": "1.7.0",
"karma-chrome-launcher": "2.1.1",
"karma-jasmine": "1.1.0",
"karma-sourcemap-loader": "0.3.7",
"karma-typescript-preprocessor": "0.3.1",
"powerbi-visuals-tools": "1.5.0",
"powerbi-visuals-utils-testutils": "^0.2.2",
"tslint": "4.5.1",
"tslint-microsoft-contrib": "4.0.0",
"powerbi-visuals-tools": "1.7.0",
"powerbi-visuals-utils-testutils": "^1.0.0",
"tslint": "5.4.3",
"tslint-microsoft-contrib": "5.0.0",
"typescript": "2.1.4"
}
}
4 changes: 2 additions & 2 deletions pbiviz.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"displayName": "WordCloud",
"guid": "WordCloud1447959067750",
"visualClassName": "WordCloud",
"version": "1.2.11",
"version": "1.2.12",
"description": "Word Cloud is a visual representation of word frequency and value. Use it to get instant insight into the most important terms in a set.",
"supportUrl": "http://community.powerbi.com",
"gitHubUrl": "https://github.com/Microsoft/PowerBI-visuals-WordCloud"
},
"apiVersion": "1.5.0",
"apiVersion": "1.7.0",
"author": {
"name": "Microsoft",
"email": "[email protected]"
Expand Down
15 changes: 7 additions & 8 deletions src/columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,23 @@ module powerbi.extensibility.visual {
// powerbi
import DataView = powerbi.DataView;
import DataViewValueColumns = powerbi.DataViewValueColumns;
import DataViewCategoricalColumn = powerbi.DataViewCategoricalColumn;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import DataViewCategorical = powerbi.DataViewCategorical;
import PrimitiveValue = powerbi.PrimitiveValue;
import DataViewValueColumn = powerbi.DataViewValueColumn;

export class WordCloudColumns<T> {
public static getCategoricalValues(dataView: DataView): WordCloudColumns<DataViewCategoricalColumn[]> {
public static getCategoricalValues(dataView: DataView): WordCloudColumns<DataViewCategoryColumn[]> {
let categorical: DataViewCategorical = dataView && dataView.categorical,
categories: DataViewCategoryColumn[] = categorical && categorical.categories || [],
values: DataViewValueColumns = categorical && categorical.values || [] as DataViewValueColumns,
series: PrimitiveValue[] = categorical && values.source && this.getSeriesValues(dataView);

return categorical && _.mapValues((new this<DataViewCategoricalColumn[]>() as any), (n: any, key: string) => {
return (_.toArray(categories) as DataViewCategoricalColumn[])
.concat(_.toArray(values))
.filter((column: DataViewCategoricalColumn) => column.source.roles && column.source.roles[key])
.map((column: DataViewCategoricalColumn) => column.values)[0]
return categorical && _.mapValues((new this<DataViewCategoryColumn[]>() as any), (n: any, key: string) => {
return (<any[]>_.toArray(categories))
.concat(<any[]>_.toArray(values))
.filter((column: DataViewCategoryColumn) => column.source.roles && column.source.roles[key])
.map((column: DataViewCategoryColumn) => column.values)[0]
|| values.source
&& values.source.roles
&& values.source.roles[key]
Expand All @@ -64,7 +63,7 @@ module powerbi.extensibility.visual {
});
}

public static getCategoricalColumns(dataView: DataView): WordCloudColumns<DataViewCategoricalColumn> {
public static getCategoricalColumns(dataView: DataView): WordCloudColumns<DataViewCategoryColumn> {
let categorical: DataViewCategorical = dataView && dataView.categorical,
categories: DataViewCategoryColumn[] = categorical && categorical.categories || [],
values: DataViewValueColumns = categorical && categorical.values || [] as DataViewValueColumns;
Expand Down
16 changes: 0 additions & 16 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,6 @@

module powerbi.extensibility.visual {
export module wordCloudUtils {
export function hexToRgb(hex: string): string {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
let shorthandRegex: RegExp = /^#?([a-f\d])([a-f\d])([a-f\d])$/i,
result: RegExpExecArray;

hex = hex.replace(shorthandRegex, (m: any, r: string, g: string, b: string) => {
return r + r + g + g + b + b;
});

result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);

return result
? `rgb(${parseInt(result[1], 16)},${parseInt(result[2], 16)},${parseInt(result[3], 16)})`
: null;
}

export function getRandomColor(): string {
const red: number = Math.floor(Math.random() * 255),
green: number = Math.floor(Math.random() * 255),
Expand Down
68 changes: 22 additions & 46 deletions src/visual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ module powerbi.extensibility.visual {
logn,
sqrt,
value
};
}

export class WordCloud implements IVisual {
private static ClassName: string = "wordCloud";
Expand Down Expand Up @@ -139,14 +139,9 @@ module powerbi.extensibility.visual {
private static TheFourthLineHeight: string = PixelConverter.toString(15); // Note: This construction fixes bug #6343.

private static DefaultTextFontSize: string = PixelConverter.toString(1);

private static MinFakeSize: number = 1;

private static DefaultStrokeStyle: string = "red";
private static DefaultTextAlign: string = "center";

private static DefaultCanvasSize: number = 1;

private static ArchimedeanFactor: number = 0.1;

private static WidthOffset: number = 5;
Expand Down Expand Up @@ -220,33 +215,22 @@ module powerbi.extensibility.visual {
private wordsContainerSelection: Selection<any>;
private wordsGroupUpdateSelection: UpdateSelection<WordCloudDataPoint>;
private wordsTextUpdateSelection: UpdateSelection<WordCloudDataPoint>;

/**
* Public for testability.
*/
public canvas: HTMLCanvasElement;

public canvasContext: CanvasRenderingContext2D;
private fontFamily: string;

private layout: VisualLayout;

private visualHost: IVisualHost;
private selectionManager: ValueSelectionManager<string>;

private visualUpdateOptions: VisualUpdateOptions;

private isUpdating: boolean = false;
private incomingUpdateOptions: VisualUpdateOptions;

private oldIdentityKeys: string[];

public static converter(
dataView: DataView,
colors: IColorPalette,
visualHost: IVisualHost,
previousData: WordCloudData): WordCloudData {

let categorical: WordCloudColumns<DataViewCategoricalColumn>,
let categorical: WordCloudColumns<DataViewCategoryColumn>,
catValues: WordCloudColumns<any[]>,
settings: WordCloudSettings,
colorHelper: ColorHelper,
Expand Down Expand Up @@ -296,12 +280,11 @@ module powerbi.extensibility.visual {
let color: string;
let selectionIdBuilder: ISelectionIdBuilder;
if (categorical.Category.objects && categorical.Category.objects[index]) {
color = wordCloudUtils.hexToRgb(colorHelper.getColorForMeasure(
categorical.Category.objects[index], ""));
color = colorHelper.getColorForMeasure(categorical.Category.objects[index], "");
} else {
color = previousData && previousData.texts && previousData.texts[index]
? previousData.texts[index].color
: wordCloudUtils.getRandomColor();
: colors.getColor(index.toString()).value;
}

selectionIdBuilder = visualHost.createSelectionIdBuilder()
Expand Down Expand Up @@ -626,15 +609,17 @@ module powerbi.extensibility.visual {
this.clearSelection();
});

this.fontFamily = this.root.style("font-family"); // TODO: check it.
this.fontFamily = this.root.style("font-family");

this.main = this.root.append("g");

this.wordsContainerSelection = this.main
.append("g")
.classed(WordCloud.Words.class, true);
.classed(WordCloud.Words.className, true);

this.canvas = document.createElement("canvas");
// init canvas context for calculate label positions
const canvas = document.createElement("canvas");
this.canvasContext = this.getCanvasContext(canvas);
}

public update(visualUpdateOptions: VisualUpdateOptions): void {
Expand Down Expand Up @@ -686,8 +671,8 @@ module powerbi.extensibility.visual {

private clear(): void {
this.main
.select(WordCloud.Words.selector)
.selectAll(WordCloud.WordGroup.selector)
.select(WordCloud.Words.selectorName)
.selectAll(WordCloud.WordGroup.selectorName)
.remove();
}

Expand All @@ -704,8 +689,7 @@ module powerbi.extensibility.visual {
let surface: number[] = _.range(
WordCloud.MinViewport.width,
(this.specialViewport.width >> WordCloud.WidthOffset) * this.specialViewport.height,
WordCloud.MinViewport.width),
canvasContext: CanvasRenderingContext2D;
WordCloud.MinViewport.width);

words.forEach((dataPoint: WordCloudDataPoint) => {
dataPoint.getWidthOfWord = () =>
Expand All @@ -718,12 +702,10 @@ module powerbi.extensibility.visual {
}) + WordCloud.AdditionalTextWidth);
});

canvasContext = this.getCanvasContext();

if (canvasContext) {
if (this.canvasContext) {
this.computeCycle(
words,
canvasContext,
this.canvasContext,
surface,
null,
onPositionsComputed);
Expand Down Expand Up @@ -1140,20 +1122,15 @@ module powerbi.extensibility.visual {
*
* Public for testability.
*/
public getCanvasContext(): CanvasRenderingContext2D {
if (!this.canvasViewport || !this.canvas) {
public getCanvasContext(canvasElement: HTMLCanvasElement): CanvasRenderingContext2D {
if (!canvasElement) {
return null;
}

this.canvas.width = WordCloud.DefaultCanvasSize;
this.canvas.height = WordCloud.DefaultCanvasSize;

let context: CanvasRenderingContext2D = this.canvas.getContext("2d");

this.canvas.width = this.canvasViewport.width << WordCloud.WidthOffset;
this.canvas.height = this.canvasViewport.height;
canvasElement.width = this.canvasViewport.width << WordCloud.WidthOffset;
canvasElement.height = this.canvasViewport.height;

context = this.canvas.getContext("2d");
const context = canvasElement.getContext("2d");

context.fillStyle = context.strokeStyle = WordCloud.DefaultStrokeStyle;
context.textAlign = WordCloud.DefaultTextAlign;
Expand Down Expand Up @@ -1195,14 +1172,14 @@ module powerbi.extensibility.visual {
this.scaleMainView(wordCloudDataView);

this.wordsGroupUpdateSelection = this.main
.select(WordCloud.Words.selector)
.select(WordCloud.Words.selectorName)
.selectAll("g")
.data(wordCloudDataView.data);

let wordGroupEnterSelection: Selection<WordCloudDataPoint> = this.wordsGroupUpdateSelection
.enter()
.append("svg:g")
.classed(WordCloud.WordGroup.class, true);
.classed(WordCloud.WordGroup.className, true);

wordGroupEnterSelection
.append("svg:text")
Expand Down Expand Up @@ -1486,7 +1463,6 @@ module powerbi.extensibility.visual {

public destroy(): void {
this.root = null;
this.canvas = null;
}
}
}
6 changes: 1 addition & 5 deletions test/_references.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,8 @@
* THE SOFTWARE.
*/

// External
/// <reference path="../node_modules/@types/jasmine/index.d.ts" />
/// <reference path="../node_modules/@types/jasmine-jquery/index.d.ts" />

// Power BI API
/// <reference path="../.api/v1.5.0/PowerBI-visuals.d.ts" />
/// <reference path="../.api/v1.7.0/PowerBI-visuals.d.ts" />

// Power BI Extensibility
/// <reference path="../node_modules/powerbi-visuals-utils-dataviewutils/lib/index.d.ts" />
Expand Down
9 changes: 0 additions & 9 deletions test/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,6 @@ module powerbi.extensibility.visual.test.helpers {
return { solid: { color } };
}

export function areColorsEqual(firstColor: string, secondColor: string): boolean {
const firstConvertedColor: RgbColor = parseColorString(firstColor),
secondConvertedColor: RgbColor = parseColorString(secondColor);

return firstConvertedColor.R === secondConvertedColor.R
&& firstConvertedColor.G === secondConvertedColor.G
&& firstConvertedColor.B === secondConvertedColor.B;
}

export function getRandomUniqueIntegers(count: number, min: number = 0, max: number): number[] {
const result: number[] = [];

Expand Down
Loading

0 comments on commit 0739310

Please sign in to comment.