Merge pull request #11277 from sbrunner/hires-regularshape-master
Have high resolution regular shape
This commit is contained in:
@@ -32,6 +32,12 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
*/
|
||||
this.image_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.imagePixelRatio_ = undefined;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number|undefined}
|
||||
@@ -136,17 +142,20 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
myEnd,
|
||||
this.image_,
|
||||
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
||||
this.anchorX_,
|
||||
this.anchorY_,
|
||||
this.anchorX_ * this.imagePixelRatio_,
|
||||
this.anchorY_ * this.imagePixelRatio_,
|
||||
this.declutterGroups_,
|
||||
this.height_,
|
||||
Math.ceil(this.height_ * this.imagePixelRatio_),
|
||||
this.opacity_,
|
||||
this.originX_,
|
||||
this.originY_,
|
||||
this.rotateWithView_,
|
||||
this.rotation_,
|
||||
[this.scale_[0] * this.pixelRatio, this.scale_[1] * this.pixelRatio],
|
||||
this.width_,
|
||||
[
|
||||
(this.scale_[0] * this.pixelRatio) / this.imagePixelRatio_,
|
||||
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
|
||||
],
|
||||
Math.ceil(this.width_ * this.imagePixelRatio_),
|
||||
]);
|
||||
this.hitDetectionInstructions.push([
|
||||
CanvasInstruction.DRAW_IMAGE,
|
||||
@@ -154,17 +163,20 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
myEnd,
|
||||
this.hitDetectionImage_,
|
||||
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
||||
this.anchorX_,
|
||||
this.anchorY_,
|
||||
this.anchorX_ * this.imagePixelRatio_,
|
||||
this.anchorY_ * this.imagePixelRatio_,
|
||||
this.declutterGroups_,
|
||||
this.height_,
|
||||
Math.ceil(this.height_ * this.imagePixelRatio_),
|
||||
this.opacity_,
|
||||
this.originX_,
|
||||
this.originY_,
|
||||
this.rotateWithView_,
|
||||
this.rotation_,
|
||||
this.scale_,
|
||||
this.width_,
|
||||
[
|
||||
(this.scale_[0] * this.pixelRatio) / this.imagePixelRatio_,
|
||||
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
|
||||
],
|
||||
Math.ceil(this.width_ * this.imagePixelRatio_),
|
||||
]);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
@@ -193,17 +205,20 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
myEnd,
|
||||
this.image_,
|
||||
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
||||
this.anchorX_,
|
||||
this.anchorY_,
|
||||
this.anchorX_ * this.imagePixelRatio_,
|
||||
this.anchorY_ * this.imagePixelRatio_,
|
||||
this.declutterGroups_,
|
||||
this.height_,
|
||||
Math.ceil(this.height_ * this.imagePixelRatio_),
|
||||
this.opacity_,
|
||||
this.originX_,
|
||||
this.originY_,
|
||||
this.rotateWithView_,
|
||||
this.rotation_,
|
||||
[this.scale_[0] * this.pixelRatio, this.scale_[1] * this.pixelRatio],
|
||||
this.width_,
|
||||
[
|
||||
(this.scale_[0] * this.pixelRatio) / this.imagePixelRatio_,
|
||||
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
|
||||
],
|
||||
Math.ceil(this.width_ * this.imagePixelRatio_),
|
||||
]);
|
||||
this.hitDetectionInstructions.push([
|
||||
CanvasInstruction.DRAW_IMAGE,
|
||||
@@ -211,17 +226,20 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
myEnd,
|
||||
this.hitDetectionImage_,
|
||||
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
||||
this.anchorX_,
|
||||
this.anchorY_,
|
||||
this.anchorX_ * this.imagePixelRatio_,
|
||||
this.anchorY_ * this.imagePixelRatio_,
|
||||
this.declutterGroups_,
|
||||
this.height_,
|
||||
Math.ceil(this.height_ * this.imagePixelRatio_),
|
||||
this.opacity_,
|
||||
this.originX_,
|
||||
this.originY_,
|
||||
this.rotateWithView_,
|
||||
this.rotation_,
|
||||
this.scale_,
|
||||
this.width_,
|
||||
[
|
||||
(this.scale_[0] * this.pixelRatio) / this.imagePixelRatio_,
|
||||
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
|
||||
],
|
||||
Math.ceil(this.width_ * this.imagePixelRatio_),
|
||||
]);
|
||||
this.endGeometry(feature);
|
||||
}
|
||||
@@ -236,6 +254,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
this.anchorY_ = undefined;
|
||||
this.hitDetectionImage_ = null;
|
||||
this.image_ = null;
|
||||
this.imagePixelRatio_ = undefined;
|
||||
this.height_ = undefined;
|
||||
this.scale_ = undefined;
|
||||
this.opacity_ = undefined;
|
||||
@@ -254,9 +273,10 @@ class CanvasImageBuilder extends CanvasBuilder {
|
||||
setImageStyle(imageStyle, declutterGroups) {
|
||||
const anchor = imageStyle.getAnchor();
|
||||
const size = imageStyle.getSize();
|
||||
const hitDetectionImage = imageStyle.getHitDetectionImage(1);
|
||||
const image = imageStyle.getImage(1);
|
||||
const hitDetectionImage = imageStyle.getHitDetectionImage(this.pixelRatio);
|
||||
const image = imageStyle.getImage(this.pixelRatio);
|
||||
const origin = imageStyle.getOrigin();
|
||||
this.imagePixelRatio_ = imageStyle.getPixelRatio(this.pixelRatio);
|
||||
this.anchorX_ = anchor[0];
|
||||
this.anchorY_ = anchor[1];
|
||||
this.declutterGroups_ = declutterGroups;
|
||||
|
||||
@@ -320,6 +320,16 @@ class Icon extends ImageStyle {
|
||||
return this.iconImage_.getImage(pixelRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pixel ratio.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @return {number} The pixel ration of the image.
|
||||
* @api
|
||||
*/
|
||||
getPixelRatio(pixelRatio) {
|
||||
return this.iconImage_.getPixelRatio(pixelRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {import("../size.js").Size} Image size.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,11 @@ import {createCanvasContext2D} from '../dom.js';
|
||||
import {shared as iconImageCache} from './IconImageCache.js';
|
||||
import {listenImage} from '../Image.js';
|
||||
|
||||
/**
|
||||
* @type {CanvasRenderingContext2D}
|
||||
*/
|
||||
let taintedTestContext = null;
|
||||
|
||||
class IconImage extends EventTarget {
|
||||
/**
|
||||
* @param {HTMLImageElement|HTMLCanvasElement} image Image.
|
||||
@@ -23,9 +28,9 @@ class IconImage extends EventTarget {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLImageElement|HTMLCanvasElement}
|
||||
* @type {Object<number, HTMLImageElement|HTMLCanvasElement>}
|
||||
*/
|
||||
this.hitDetectionImage_ = null;
|
||||
this.hitDetectionImage_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -39,9 +44,9 @@ class IconImage extends EventTarget {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLCanvasElement}
|
||||
* @type {Object<number, HTMLCanvasElement>}
|
||||
*/
|
||||
this.canvas_ = color ? document.createElement('canvas') : null;
|
||||
this.canvas_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -75,26 +80,25 @@ class IconImage extends EventTarget {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean|undefined}
|
||||
*/
|
||||
this.tainted_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {CanvasRenderingContext2D=} context A context with the image already drawn into.
|
||||
* @return {boolean} The image canvas is tainted.
|
||||
*/
|
||||
isTainted_(context) {
|
||||
isTainted_() {
|
||||
if (this.tainted_ === undefined && this.imageState_ === ImageState.LOADED) {
|
||||
if (!context) {
|
||||
context = createCanvasContext2D(1, 1);
|
||||
context.drawImage(this.image_, 0, 0);
|
||||
if (!taintedTestContext) {
|
||||
taintedTestContext = createCanvasContext2D(1, 1);
|
||||
}
|
||||
taintedTestContext.drawImage(this.image_, 0, 0);
|
||||
try {
|
||||
context.getImageData(0, 0, 1, 1);
|
||||
taintedTestContext.getImageData(0, 0, 1, 1);
|
||||
this.tainted_ = false;
|
||||
} catch (e) {
|
||||
taintedTestContext = null;
|
||||
this.tainted_ = true;
|
||||
}
|
||||
}
|
||||
@@ -125,10 +129,10 @@ class IconImage extends EventTarget {
|
||||
if (this.size_) {
|
||||
this.image_.width = this.size_[0];
|
||||
this.image_.height = this.size_[1];
|
||||
} else {
|
||||
this.size_ = [this.image_.width, this.image_.height];
|
||||
}
|
||||
this.size_ = [this.image_.width, this.image_.height];
|
||||
this.unlistenImage_();
|
||||
this.replaceColor_();
|
||||
this.dispatchChangeEvent_();
|
||||
}
|
||||
|
||||
@@ -137,7 +141,17 @@ class IconImage extends EventTarget {
|
||||
* @return {HTMLImageElement|HTMLCanvasElement} Image or Canvas element.
|
||||
*/
|
||||
getImage(pixelRatio) {
|
||||
return this.canvas_ ? this.canvas_ : this.image_;
|
||||
this.replaceColor_(pixelRatio);
|
||||
return this.canvas_[pixelRatio] ? this.canvas_[pixelRatio] : this.image_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @return {number} Image or Canvas element.
|
||||
*/
|
||||
getPixelRatio(pixelRatio) {
|
||||
this.replaceColor_(pixelRatio);
|
||||
return this.canvas_[pixelRatio] ? pixelRatio : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,18 +166,23 @@ class IconImage extends EventTarget {
|
||||
* @return {HTMLImageElement|HTMLCanvasElement} Image element.
|
||||
*/
|
||||
getHitDetectionImage(pixelRatio) {
|
||||
if (!this.hitDetectionImage_) {
|
||||
if (!this.hitDetectionImage_[pixelRatio]) {
|
||||
if (this.isTainted_()) {
|
||||
const usedPixelRatio = this.color_ ? pixelRatio : 1;
|
||||
const width = this.size_[0];
|
||||
const height = this.size_[1];
|
||||
const context = createCanvasContext2D(width, height);
|
||||
const context = createCanvasContext2D(
|
||||
Math.ceil(width * usedPixelRatio),
|
||||
Math.ceil(height * usedPixelRatio)
|
||||
);
|
||||
context.scale(usedPixelRatio, usedPixelRatio);
|
||||
context.fillRect(0, 0, width, height);
|
||||
this.hitDetectionImage_ = context.canvas;
|
||||
this.hitDetectionImage_[pixelRatio] = context.canvas;
|
||||
} else {
|
||||
this.hitDetectionImage_ = this.image_;
|
||||
this.hitDetectionImage_[pixelRatio] = this.image_;
|
||||
}
|
||||
}
|
||||
return this.hitDetectionImage_;
|
||||
return this.hitDetectionImage_[pixelRatio];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,20 +220,25 @@ class IconImage extends EventTarget {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @private
|
||||
*/
|
||||
replaceColor_() {
|
||||
if (!this.color_) {
|
||||
replaceColor_(pixelRatio) {
|
||||
if (!this.color_ || this.canvas_[pixelRatio]) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.canvas_.width = this.image_.width;
|
||||
this.canvas_.height = this.image_.height;
|
||||
const canvas = document.createElement('canvas');
|
||||
this.canvas_[pixelRatio] = canvas;
|
||||
|
||||
const ctx = this.canvas_.getContext('2d');
|
||||
canvas.width = Math.ceil(this.image_.width * pixelRatio);
|
||||
canvas.height = Math.ceil(this.image_.height * pixelRatio);
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.scale(pixelRatio, pixelRatio);
|
||||
ctx.drawImage(this.image_, 0, 0);
|
||||
|
||||
if (this.isTainted_(ctx)) {
|
||||
if (this.isTainted_()) {
|
||||
// If reading from the canvas throws a SecurityError the same effect can be
|
||||
// achieved with globalCompositeOperation.
|
||||
// This could be used as the default, but it is not fully supported by all
|
||||
@@ -226,19 +250,14 @@ class IconImage extends EventTarget {
|
||||
const c = this.color_;
|
||||
ctx.globalCompositeOperation = 'multiply';
|
||||
ctx.fillStyle = 'rgb(' + c[0] + ',' + c[1] + ',' + c[2] + ')';
|
||||
ctx.fillRect(0, 0, this.image_.width, this.image_.height);
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.globalCompositeOperation = 'destination-in';
|
||||
ctx.drawImage(this.image_, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const imgData = ctx.getImageData(
|
||||
0,
|
||||
0,
|
||||
this.image_.width,
|
||||
this.image_.height
|
||||
);
|
||||
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const data = imgData.data;
|
||||
const r = this.color_[0] / 255.0;
|
||||
const g = this.color_[1] / 255.0;
|
||||
|
||||
@@ -161,6 +161,14 @@ class ImageStyle {
|
||||
return abstract();
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the image pixel ratio.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* */
|
||||
getPixelRatio(pixelRatio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @return {import("../ImageState.js").default} Image state.
|
||||
|
||||
@@ -73,15 +73,15 @@ class RegularShape extends ImageStyle {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLCanvasElement}
|
||||
* @type {Object<number, HTMLCanvasElement>}
|
||||
*/
|
||||
this.canvas_ = null;
|
||||
this.canvas_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLCanvasElement}
|
||||
* @type {Object<number, HTMLCanvasElement>}
|
||||
*/
|
||||
this.hitDetectionCanvas_ = null;
|
||||
this.hitDetectionCanvas_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -205,20 +205,45 @@ class RegularShape extends ImageStyle {
|
||||
|
||||
/**
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @return {HTMLImageElement|HTMLCanvasElement} Image element.
|
||||
* @return {HTMLCanvasElement} Image element.
|
||||
*/
|
||||
getHitDetectionImage(pixelRatio) {
|
||||
return this.hitDetectionCanvas_;
|
||||
if (!this.hitDetectionCanvas_[pixelRatio || 1]) {
|
||||
const renderOptions = this.createRenderOptions();
|
||||
|
||||
this.createHitDetectionCanvas_(renderOptions, pixelRatio || 1);
|
||||
}
|
||||
return this.hitDetectionCanvas_[pixelRatio || 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image icon.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @return {HTMLImageElement|HTMLCanvasElement} Image or Canvas element.
|
||||
* @return {HTMLCanvasElement} Image or Canvas element.
|
||||
* @api
|
||||
*/
|
||||
getImage(pixelRatio) {
|
||||
return this.canvas_;
|
||||
if (!this.canvas_[pixelRatio || 1]) {
|
||||
const renderOptions = this.createRenderOptions();
|
||||
|
||||
const context = createCanvasContext2D(
|
||||
renderOptions.size * pixelRatio || 1,
|
||||
renderOptions.size * pixelRatio || 1
|
||||
);
|
||||
|
||||
this.draw_(renderOptions, context, 0, 0, pixelRatio || 1);
|
||||
|
||||
this.canvas_[pixelRatio || 1] = context.canvas;
|
||||
}
|
||||
return this.canvas_[pixelRatio || 1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the image pixel ratio.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* */
|
||||
getPixelRatio(pixelRatio) {
|
||||
return pixelRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,9 +337,10 @@ class RegularShape extends ImageStyle {
|
||||
unlistenImageChange(listener) {}
|
||||
|
||||
/**
|
||||
* @returns {RenderOptions} The render options
|
||||
* @protected
|
||||
*/
|
||||
render() {
|
||||
createRenderOptions() {
|
||||
let lineCap = defaultLineCap;
|
||||
let lineJoin = defaultLineJoin;
|
||||
let miterLimit = 0;
|
||||
@@ -349,9 +375,9 @@ class RegularShape extends ImageStyle {
|
||||
}
|
||||
}
|
||||
|
||||
let size = 2 * (this.radius_ + strokeWidth) + 1;
|
||||
const size = 2 * (this.radius_ + strokeWidth) + 1;
|
||||
|
||||
const renderOptions = {
|
||||
return {
|
||||
strokeStyle: strokeStyle,
|
||||
strokeWidth: strokeWidth,
|
||||
size: size,
|
||||
@@ -361,18 +387,30 @@ class RegularShape extends ImageStyle {
|
||||
lineJoin: lineJoin,
|
||||
miterLimit: miterLimit,
|
||||
};
|
||||
}
|
||||
|
||||
const context = createCanvasContext2D(size, size);
|
||||
this.canvas_ = context.canvas;
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
render() {
|
||||
const renderOptions = this.createRenderOptions();
|
||||
|
||||
const context = createCanvasContext2D(
|
||||
renderOptions.size,
|
||||
renderOptions.size
|
||||
);
|
||||
|
||||
this.draw_(renderOptions, context, 0, 0, 1);
|
||||
|
||||
this.canvas_[1] = context.canvas;
|
||||
|
||||
// canvas.width and height are rounded to the closest integer
|
||||
size = this.canvas_.width;
|
||||
const size = context.canvas.width;
|
||||
const imageSize = size;
|
||||
const displacement = this.getDisplacement();
|
||||
|
||||
this.draw_(renderOptions, context, 0, 0);
|
||||
|
||||
this.createHitDetectionCanvas_(renderOptions);
|
||||
this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size];
|
||||
this.createHitDetectionCanvas_(renderOptions, 1);
|
||||
|
||||
this.anchor_ = [size / 2 - displacement[0], size / 2 + displacement[1]];
|
||||
this.size_ = [size, size];
|
||||
@@ -385,11 +423,13 @@ class RegularShape extends ImageStyle {
|
||||
* @param {CanvasRenderingContext2D} context The rendering context.
|
||||
* @param {number} x The origin for the symbol (x).
|
||||
* @param {number} y The origin for the symbol (y).
|
||||
* @param {number} pixelRatio The pixel ratio.
|
||||
*/
|
||||
draw_(renderOptions, context, x, y) {
|
||||
draw_(renderOptions, context, x, y, pixelRatio) {
|
||||
let i, angle0, radiusC;
|
||||
|
||||
// reset transform
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
||||
|
||||
// then move to (x, y)
|
||||
context.translate(x, y);
|
||||
@@ -448,10 +488,10 @@ class RegularShape extends ImageStyle {
|
||||
/**
|
||||
* @private
|
||||
* @param {RenderOptions} renderOptions Render options.
|
||||
* @param {number} pixelRatio The pixel ratio.
|
||||
*/
|
||||
createHitDetectionCanvas_(renderOptions) {
|
||||
this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size];
|
||||
this.hitDetectionCanvas_ = this.canvas_;
|
||||
createHitDetectionCanvas_(renderOptions, pixelRatio) {
|
||||
this.hitDetectionCanvas_[pixelRatio] = this.getImage(pixelRatio);
|
||||
if (this.fill_) {
|
||||
let color = this.fill_.getColor();
|
||||
|
||||
@@ -469,12 +509,12 @@ class RegularShape extends ImageStyle {
|
||||
// if a transparent fill style is set, create an extra hit-detection image
|
||||
// with a default fill style
|
||||
const context = createCanvasContext2D(
|
||||
renderOptions.size,
|
||||
renderOptions.size
|
||||
renderOptions.size * pixelRatio,
|
||||
renderOptions.size * pixelRatio
|
||||
);
|
||||
this.hitDetectionCanvas_ = context.canvas;
|
||||
this.hitDetectionCanvas_[pixelRatio] = context.canvas;
|
||||
|
||||
this.drawHitDetectionCanvas_(renderOptions, context, 0, 0);
|
||||
this.drawHitDetectionCanvas_(renderOptions, context, 0, 0, pixelRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -485,10 +525,11 @@ class RegularShape extends ImageStyle {
|
||||
* @param {CanvasRenderingContext2D} context The context.
|
||||
* @param {number} x The origin for the symbol (x).
|
||||
* @param {number} y The origin for the symbol (y).
|
||||
* @param {number} pixelRatio The pixel ratio.
|
||||
*/
|
||||
drawHitDetectionCanvas_(renderOptions, context, x, y) {
|
||||
drawHitDetectionCanvas_(renderOptions, context, x, y, pixelRatio) {
|
||||
// reset transform
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
||||
|
||||
// then move to (x, y)
|
||||
context.translate(x, y);
|
||||
|
||||
Reference in New Issue
Block a user