Source: core/sprites/canvas/CanvasTinter.js

core/sprites/canvas/CanvasTinter.js

  1. import { hex2rgb, rgb2hex } from '../../utils';
  2. import canUseNewCanvasBlendModes from '../../renderers/canvas/utils/canUseNewCanvasBlendModes';
  3. //引入小程序补丁
  4. import { documentAlias, Image} from '@ali/pixi-miniprogram-adapter';
  5. /**
  6. * Utility methods for Sprite/Texture tinting.
  7. *
  8. * @class
  9. * @memberof PIXI
  10. */
  11. const CanvasTinter = {
  12. /**
  13. * Basically this method just needs a sprite and a color and tints the sprite with the given color.
  14. *
  15. * @memberof PIXI.CanvasTinter
  16. * @param {PIXI.Sprite} sprite - the sprite to tint
  17. * @param {number} color - the color to use to tint the sprite with
  18. * @return {HTMLCanvasElement} The tinted canvas
  19. */
  20. getTintedTexture: (sprite, color) =>
  21. {
  22. const texture = sprite._texture;
  23. color = CanvasTinter.roundColor(color);
  24. const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`;
  25. texture.tintCache = texture.tintCache || {};
  26. const cachedTexture = texture.tintCache[stringColor];
  27. let canvas;
  28. if (cachedTexture)
  29. {
  30. if (cachedTexture.tintId === texture._updateID)
  31. {
  32. return texture.tintCache[stringColor];
  33. }
  34. canvas = texture.tintCache[stringColor];
  35. }
  36. else
  37. {
  38. canvas = CanvasTinter.canvas || documentAlias.createElement('canvas');
  39. }
  40. CanvasTinter.tintMethod(texture, color, canvas);
  41. canvas.tintId = texture._updateID;
  42. if (CanvasTinter.convertTintToImage)
  43. {
  44. // is this better?
  45. const tintImage = new Image();
  46. tintImage.src = canvas.toDataURL();
  47. texture.tintCache[stringColor] = tintImage;
  48. }
  49. else
  50. {
  51. texture.tintCache[stringColor] = canvas;
  52. // if we are not converting the texture to an image then we need to lose the reference to the canvas
  53. CanvasTinter.canvas = null;
  54. }
  55. return canvas;
  56. },
  57. /**
  58. * Tint a texture using the 'multiply' operation.
  59. *
  60. * @memberof PIXI.CanvasTinter
  61. * @param {PIXI.Texture} texture - the texture to tint
  62. * @param {number} color - the color to use to tint the sprite with
  63. * @param {HTMLCanvasElement} canvas - the current canvas
  64. */
  65. tintWithMultiply: (texture, color, canvas) =>
  66. {
  67. const context = canvas.getContext('2d');
  68. const crop = texture._frame.clone();
  69. const resolution = texture.baseTexture.resolution;
  70. crop.x *= resolution;
  71. crop.y *= resolution;
  72. crop.width *= resolution;
  73. crop.height *= resolution;
  74. canvas.width = Math.ceil(crop.width);
  75. canvas.height = Math.ceil(crop.height);
  76. context.save();
  77. context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`;
  78. context.fillRect(0, 0, crop.width, crop.height);
  79. context.globalCompositeOperation = 'multiply';
  80. context.drawImage(
  81. texture.baseTexture.source,
  82. crop.x,
  83. crop.y,
  84. crop.width,
  85. crop.height,
  86. 0,
  87. 0,
  88. crop.width,
  89. crop.height
  90. );
  91. context.globalCompositeOperation = 'destination-atop';
  92. context.drawImage(
  93. texture.baseTexture.source,
  94. crop.x,
  95. crop.y,
  96. crop.width,
  97. crop.height,
  98. 0,
  99. 0,
  100. crop.width,
  101. crop.height
  102. );
  103. context.restore();
  104. },
  105. /**
  106. * Tint a texture using the 'overlay' operation.
  107. *
  108. * @memberof PIXI.CanvasTinter
  109. * @param {PIXI.Texture} texture - the texture to tint
  110. * @param {number} color - the color to use to tint the sprite with
  111. * @param {HTMLCanvasElement} canvas - the current canvas
  112. */
  113. tintWithOverlay(texture, color, canvas)
  114. {
  115. const context = canvas.getContext('2d');
  116. const crop = texture._frame.clone();
  117. const resolution = texture.baseTexture.resolution;
  118. crop.x *= resolution;
  119. crop.y *= resolution;
  120. crop.width *= resolution;
  121. crop.height *= resolution;
  122. canvas.width = Math.ceil(crop.width);
  123. canvas.height = Math.ceil(crop.height);
  124. context.save();
  125. context.globalCompositeOperation = 'copy';
  126. context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`;
  127. context.fillRect(0, 0, crop.width, crop.height);
  128. context.globalCompositeOperation = 'destination-atop';
  129. context.drawImage(
  130. texture.baseTexture.source,
  131. crop.x,
  132. crop.y,
  133. crop.width,
  134. crop.height,
  135. 0,
  136. 0,
  137. crop.width,
  138. crop.height
  139. );
  140. // context.globalCompositeOperation = 'copy';
  141. context.restore();
  142. },
  143. /**
  144. * Tint a texture pixel per pixel.
  145. *
  146. * @memberof PIXI.CanvasTinter
  147. * @param {PIXI.Texture} texture - the texture to tint
  148. * @param {number} color - the color to use to tint the sprite with
  149. * @param {HTMLCanvasElement} canvas - the current canvas
  150. */
  151. tintWithPerPixel: (texture, color, canvas) =>
  152. {
  153. const context = canvas.getContext('2d');
  154. const crop = texture._frame.clone();
  155. const resolution = texture.baseTexture.resolution;
  156. crop.x *= resolution;
  157. crop.y *= resolution;
  158. crop.width *= resolution;
  159. crop.height *= resolution;
  160. canvas.width = Math.ceil(crop.width);
  161. canvas.height = Math.ceil(crop.height);
  162. context.save();
  163. context.globalCompositeOperation = 'copy';
  164. context.drawImage(
  165. texture.baseTexture.source,
  166. crop.x,
  167. crop.y,
  168. crop.width,
  169. crop.height,
  170. 0,
  171. 0,
  172. crop.width,
  173. crop.height
  174. );
  175. context.restore();
  176. const rgbValues = hex2rgb(color);
  177. const r = rgbValues[0];
  178. const g = rgbValues[1];
  179. const b = rgbValues[2];
  180. const pixelData = context.getImageData(0, 0, crop.width, crop.height);
  181. const pixels = pixelData.data;
  182. for (let i = 0; i < pixels.length; i += 4)
  183. {
  184. pixels[i + 0] *= r;
  185. pixels[i + 1] *= g;
  186. pixels[i + 2] *= b;
  187. }
  188. context.putImageData(pixelData, 0, 0);
  189. },
  190. /**
  191. * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel.
  192. *
  193. * @memberof PIXI.CanvasTinter
  194. * @param {number} color - the color to round, should be a hex color
  195. * @return {number} The rounded color.
  196. */
  197. roundColor: (color) =>
  198. {
  199. const step = CanvasTinter.cacheStepsPerColorChannel;
  200. const rgbValues = hex2rgb(color);
  201. rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step);
  202. rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step);
  203. rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step);
  204. return rgb2hex(rgbValues);
  205. },
  206. /**
  207. * Number of steps which will be used as a cap when rounding colors.
  208. *
  209. * @memberof PIXI.CanvasTinter
  210. * @type {number}
  211. */
  212. cacheStepsPerColorChannel: 8,
  213. /**
  214. * Tint cache boolean flag.
  215. *
  216. * @memberof PIXI.CanvasTinter
  217. * @type {boolean}
  218. */
  219. convertTintToImage: false,
  220. /**
  221. * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method.
  222. *
  223. * @memberof PIXI.CanvasTinter
  224. * @type {boolean}
  225. */
  226. canUseMultiply: canUseNewCanvasBlendModes(),
  227. /**
  228. * The tinting method that will be used.
  229. *
  230. * @memberof PIXI.CanvasTinter
  231. * @type {tintMethodFunctionType}
  232. */
  233. tintMethod: 0,
  234. };
  235. CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel;
  236. /**
  237. * The tintMethod type.
  238. *
  239. * @memberof PIXI.CanvasTinter
  240. * @callback tintMethodFunctionType
  241. * @param texture {PIXI.Texture} the texture to tint
  242. * @param color {number} the color to use to tint the sprite with
  243. * @param canvas {HTMLCanvasElement} the current canvas
  244. */
  245. export default CanvasTinter;