import { rendererLogger as logger } from '../../configuration/LoggerConfig'
import { drawStroke } from './symbols/StrokeSymbolCanvasRenderer'
import { drawTextSymbol, TextSymbols } from './symbols/TextSymbolCanvasRenderer'
import { drawShapeSymbol, ShapeSymbols } from './symbols/ShapeSymbolCanvasRenderer'
import * as InkModel from '../../model/InkModel'
/**
* Renderer info
* @typedef {Object} RendererInfo
* @property {String} type Renderer type.
* @property {String} apiVersion Supported api version.
*/
/**
* Default renderer
* @typedef {Object} Renderer
* @property {function} getInfo Get some information about this renderer
* @property {function} attach Populate the DOM element to create rendering area.
* @property {function} detach Remove rendering area from the DOM element.
* @property {function} resize Explicitly resize the rendering area.
* @property {function} drawCurrentStroke Draw the model currentStroke.
* @property {function} drawModel Draw the model defaultSymbols and recognizedSymbols.
*/
/**
* Get info
* @return {RendererInfo} Information about this renderer
*/
export function getInfo () {
return {
type: 'canvas'
}
}
function getPixelRatio (canvas) {
if (canvas) {
const context = canvas.getContext('2d')
// we are using a browser object
// eslint-disable-next-line no-undef
const devicePixelRatio = window.devicePixelRatio || 1
const backingStoreRatio = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1
return devicePixelRatio / backingStoreRatio
}
return 1
}
function detectPixelRatio (element) {
// we are using a browser object
// eslint-disable-next-line no-undef
const tempCanvas = document.createElement('canvas')
const canvasRatio = getPixelRatio(tempCanvas)
// document.removeChild(tempCanvas);
return canvasRatio
}
function createCanvas (element, type) {
// eslint-disable-next-line no-undef
const browserDocument = document
const canvas = browserDocument.createElement('canvas')
canvas.classList.add(type)
canvas.classList.add('ms-canvas')
element.appendChild(canvas)
logger.debug('canvas created', canvas)
return canvas
}
function resizeContent (context) {
const elements = [context.renderingCanvas, context.capturingCanvas]
elements.forEach((canvas) => {
const domElement = canvas.parentNode
const width = domElement.clientWidth < context.minWidth ? context.minWidth : domElement.clientWidth
const height = domElement.clientHeight < context.minHeight ? context.minHeight : domElement.clientHeight
/* eslint-disable no-param-reassign */
canvas.width = width * context.pixelRatio
canvas.height = height * context.pixelRatio
canvas.style.width = `${width}px`
canvas.style.height = `${height}px`
/* eslint-enable no-param-reassign */
canvas.getContext('2d').scale(context.pixelRatio, context.pixelRatio)
logger.debug('canvas size changed', canvas)
})
return context
}
/**
* Attach the renderer to the DOM element
* @param {Element} element DOM element to attach the rendering elements
* @param {Number} [minHeight=0] Minimal height of the editor
* @param {Number} [minWidth=0] Minimal width of the editor
* @return {Object} The renderer context to give as parameter when a draw model will be call
*/
export function attach (element, minHeight = 0, minWidth = 0) {
logger.debug('attach renderer', element)
const pixelRatio = detectPixelRatio(element)
const renderingCanvas = createCanvas(element, 'ms-rendering-canvas')
const capturingCanvas = createCanvas(element, 'ms-capture-canvas')
const context = {
pixelRatio,
minHeight,
minWidth,
renderingCanvas,
renderingCanvasContext: renderingCanvas.getContext('2d'),
capturingCanvas,
capturingCanvasContext: capturingCanvas.getContext('2d')
}
return resizeContent(context)
}
/**
* Detach the renderer from the DOM element
* @param {Element} element DOM element to attach the rendering elements
* @param {Object} context Current rendering context
*/
export function detach (element, context) {
logger.debug('detach renderer', element)
element.removeChild(context.renderingCanvas)
element.removeChild(context.capturingCanvas)
}
/**
* Update the rendering context size
* @param {Object} context Current rendering context
* @param {Model} model Current model
* @param {Stroker} stroker Current stroker
* @return {Model}
*/
export function resize (context, model, stroker) {
return this.drawModel(resizeContent(context), model, stroker)
}
function drawSymbol (context, symbol, stroker) {
const type = symbol.elementType ? symbol.elementType : symbol.type
logger.trace(`attempting to draw ${type} symbol`)
if (type === 'stroke') {
drawStroke(context, symbol, stroker)
} else if (TextSymbols[type]) {
drawTextSymbol(context, symbol)
} else if (ShapeSymbols[type]) {
drawShapeSymbol(context, symbol)
} else {
logger.warn(`impossible to draw ${type} symbol`)
}
}
/**
* Draw the current stroke from the model
* @param {Object} context Current rendering context
* @param {Model} model Current model
* @param {Stroker} stroker Current stroker
* @return {Model}
*/
export function drawCurrentStroke (context, model, stroker) {
// Render the current stroke
context.capturingCanvasContext.clearRect(0, 0, context.capturingCanvas.width, context.capturingCanvas.height)
logger.trace('drawing current stroke ', model.currentStroke)
drawStroke(context.capturingCanvasContext, model.currentStroke, stroker)
return model
}
/**
* Draw all symbols contained into the model
* @param {Object} context Current rendering context
* @param {Model} model Current model
* @param {Stroker} stroker Current stroker
* @return {Model}
*/
export function drawModel (context, model, stroker) {
context.renderingCanvasContext.clearRect(0, 0, context.renderingCanvas.width, context.renderingCanvas.height)
// Displaying the default symbols and pending strokes
const symbols = [...model.defaultSymbols]
// Displaying the recognition symbols or raw strokes
if (model.recognizedSymbols) {
symbols.push(...model.recognizedSymbols)
symbols.push(...InkModel.extractPendingStrokes(model))
} else {
symbols.push(...model.rawStrokes)
}
symbols.forEach(symbol => drawSymbol(context.renderingCanvasContext, symbol, stroker))
context.capturingCanvasContext.clearRect(0, 0, context.capturingCanvas.width, context.capturingCanvas.height)
return model
}