DiscardMissingTileImagePolicy.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import defaultValue from "../Core/defaultValue.js";
  2. import defined from "../Core/defined.js";
  3. import DeveloperError from "../Core/DeveloperError.js";
  4. import getImagePixels from "../Core/getImagePixels.js";
  5. import Resource from "../Core/Resource.js";
  6. /**
  7. * A policy for discarding tile images that match a known image containing a
  8. * "missing" image.
  9. *
  10. * @alias DiscardMissingTileImagePolicy
  11. * @constructor
  12. *
  13. * @param {object} options Object with the following properties:
  14. * @param {Resource|string} options.missingImageUrl The URL of the known missing image.
  15. * @param {Cartesian2[]} options.pixelsToCheck An array of {@link Cartesian2} pixel positions to
  16. * compare against the missing image.
  17. * @param {boolean} [options.disableCheckIfAllPixelsAreTransparent=false] If true, the discard check will be disabled
  18. * if all of the pixelsToCheck in the missingImageUrl have an alpha value of 0. If false, the
  19. * discard check will proceed no matter the values of the pixelsToCheck.
  20. */
  21. function DiscardMissingTileImagePolicy(options) {
  22. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  23. //>>includeStart('debug', pragmas.debug);
  24. if (!defined(options.missingImageUrl)) {
  25. throw new DeveloperError("options.missingImageUrl is required.");
  26. }
  27. if (!defined(options.pixelsToCheck)) {
  28. throw new DeveloperError("options.pixelsToCheck is required.");
  29. }
  30. //>>includeEnd('debug');
  31. this._pixelsToCheck = options.pixelsToCheck;
  32. this._missingImagePixels = undefined;
  33. this._missingImageByteLength = undefined;
  34. this._isReady = false;
  35. const resource = Resource.createIfNeeded(options.missingImageUrl);
  36. const that = this;
  37. function success(image) {
  38. if (defined(image.blob)) {
  39. that._missingImageByteLength = image.blob.size;
  40. }
  41. let pixels = getImagePixels(image);
  42. if (options.disableCheckIfAllPixelsAreTransparent) {
  43. let allAreTransparent = true;
  44. const width = image.width;
  45. const pixelsToCheck = options.pixelsToCheck;
  46. for (
  47. let i = 0, len = pixelsToCheck.length;
  48. allAreTransparent && i < len;
  49. ++i
  50. ) {
  51. const pos = pixelsToCheck[i];
  52. const index = pos.x * 4 + pos.y * width;
  53. const alpha = pixels[index + 3];
  54. if (alpha > 0) {
  55. allAreTransparent = false;
  56. }
  57. }
  58. if (allAreTransparent) {
  59. pixels = undefined;
  60. }
  61. }
  62. that._missingImagePixels = pixels;
  63. that._isReady = true;
  64. }
  65. function failure() {
  66. // Failed to download "missing" image, so assume that any truly missing tiles
  67. // will also fail to download and disable the discard check.
  68. that._missingImagePixels = undefined;
  69. that._isReady = true;
  70. }
  71. resource
  72. .fetchImage({
  73. preferBlob: true,
  74. preferImageBitmap: true,
  75. flipY: true,
  76. })
  77. .then(success)
  78. .catch(failure);
  79. }
  80. /**
  81. * Determines if the discard policy is ready to process images.
  82. * @returns {boolean} True if the discard policy is ready to process images; otherwise, false.
  83. */
  84. DiscardMissingTileImagePolicy.prototype.isReady = function () {
  85. return this._isReady;
  86. };
  87. /**
  88. * Given a tile image, decide whether to discard that image.
  89. *
  90. * @param {HTMLImageElement} image An image to test.
  91. * @returns {boolean} True if the image should be discarded; otherwise, false.
  92. *
  93. * @exception {DeveloperError} <code>shouldDiscardImage</code> must not be called before the discard policy is ready.
  94. */
  95. DiscardMissingTileImagePolicy.prototype.shouldDiscardImage = function (image) {
  96. //>>includeStart('debug', pragmas.debug);
  97. if (!this._isReady) {
  98. throw new DeveloperError(
  99. "shouldDiscardImage must not be called before the discard policy is ready."
  100. );
  101. }
  102. //>>includeEnd('debug');
  103. const pixelsToCheck = this._pixelsToCheck;
  104. const missingImagePixels = this._missingImagePixels;
  105. // If missingImagePixels is undefined, it indicates that the discard check has been disabled.
  106. if (!defined(missingImagePixels)) {
  107. return false;
  108. }
  109. if (defined(image.blob) && image.blob.size !== this._missingImageByteLength) {
  110. return false;
  111. }
  112. const pixels = getImagePixels(image);
  113. const width = image.width;
  114. for (let i = 0, len = pixelsToCheck.length; i < len; ++i) {
  115. const pos = pixelsToCheck[i];
  116. const index = pos.x * 4 + pos.y * width;
  117. for (let offset = 0; offset < 4; ++offset) {
  118. const pixel = index + offset;
  119. if (pixels[pixel] !== missingImagePixels[pixel]) {
  120. return false;
  121. }
  122. }
  123. }
  124. return true;
  125. };
  126. export default DiscardMissingTileImagePolicy;