Skip to content

Commit

Permalink
cleaner handling of image data
Browse files Browse the repository at this point in the history
  • Loading branch information
mourner committed Nov 3, 2016
1 parent 3aa308f commit 2b50f16
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 34 deletions.
2 changes: 1 addition & 1 deletion js/source/image_source.js
Expand Up @@ -142,7 +142,7 @@ class ImageSource extends Evented {
}

prepare() {
if (!this.tile || !this.image || !this.image.complete) return;
if (!this.tile || !this.image) return;
this._prepareImage(this.map.painter.gl, this.image);
}

Expand Down
24 changes: 14 additions & 10 deletions js/style/image_sprite.js
Expand Up @@ -32,7 +32,7 @@ class ImageSprite extends Evented {
}

this.data = data;
if (this.img) this.fire('data', {dataType: 'style'});
if (this.imgData) this.fire('data', {dataType: 'style'});
});

ajax.getImage(normalizeURL(base, format, '.png'), (err, img) => {
Expand All @@ -41,15 +41,18 @@ class ImageSprite extends Evented {
return;
}

this.imgData = browser.getImageData(img);

// premultiply the sprite
for (let i = 0; i < img.data.length; i += 4) {
const alpha = img.data[i + 3] / 255;
img.data[i + 0] *= alpha;
img.data[i + 1] *= alpha;
img.data[i + 2] *= alpha;
for (let i = 0; i < this.imgData.length; i += 4) {
const alpha = this.imgData[i + 3] / 255;
this.imgData[i + 0] *= alpha;
this.imgData[i + 1] *= alpha;
this.imgData[i + 2] *= alpha;
}

this.img = img;
this.width = img.width;

if (this.data) this.fire('data', {dataType: 'style'});
});
}
Expand All @@ -59,15 +62,16 @@ class ImageSprite extends Evented {
}

loaded() {
return !!(this.data && this.img);
return !!(this.data && this.imgData);
}

resize(/*gl*/) {
if (browser.devicePixelRatio > 1 !== this.retina) {
const newSprite = new ImageSprite(this.base);
newSprite.on('data', () => {
this.img = newSprite.img;
this.data = newSprite.data;
this.imgData = newSprite.imgData;
this.width = newSprite.width;
this.retina = newSprite.retina;
});
}
Expand All @@ -77,7 +81,7 @@ class ImageSprite extends Evented {
if (!this.loaded()) return new SpritePosition();

const pos = this.data && this.data[name];
if (pos && this.img) return pos;
if (pos && this.imgData) return pos;

return new SpritePosition();
}
Expand Down
6 changes: 3 additions & 3 deletions js/symbol/sprite_atlas.js
Expand Up @@ -109,8 +109,8 @@ class SpriteAtlas {
}

copy(dst, src, wrap) {
if (!this.sprite.img.data) return;
const srcImg = new Uint32Array(this.sprite.img.data.buffer);
if (!this.sprite.imgData) return;
const srcImg = new Uint32Array(this.sprite.imgData.buffer);

this.allocate();
const dstImg = this.data;
Expand All @@ -119,7 +119,7 @@ class SpriteAtlas {

copyBitmap(
/* source buffer */ srcImg,
/* source stride */ this.sprite.img.width,
/* source stride */ this.sprite.width,
/* source x */ src.x,
/* source y */ src.y,
/* dest buffer */ dstImg,
Expand Down
10 changes: 1 addition & 9 deletions js/util/ajax.js
Expand Up @@ -59,15 +59,7 @@ exports.getImage = function(url, callback) {
return exports.getArrayBuffer(url, (err, imgData) => {
if (err) return callback(err);
const img = new window.Image();
img.onload = () => {
const canvas = window.document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
img.data = context.getImageData(0, 0, img.width, img.height).data;
callback(null, img);
};
img.onload = () => callback(null, img);
if (!sameOrigin(url)) {
img.crossOrigin = "Anonymous";
}
Expand Down
9 changes: 9 additions & 0 deletions js/util/browser.js
Expand Up @@ -64,6 +64,15 @@ exports.timed = function (fn, dur, ctx) {
return function() { abort = true; };
};

exports.getImageData = function (img) {
const canvas = window.document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
return context.getImageData(0, 0, img.width, img.height).data;
};

/**
* Test if the current browser supports Mapbox GL JS
* @param {Object} options
Expand Down
18 changes: 7 additions & 11 deletions test/suite_implementation.js
Expand Up @@ -6,6 +6,7 @@ const request = require('request');
const PNG = require('pngjs').PNG;
const Map = require('../js/ui/map');
const window = require('../js/util/window');
const browser = require('../js/util/browser');

module.exports = function(style, options, _callback) {
let wasCallbackCalled = false;
Expand Down Expand Up @@ -100,15 +101,6 @@ function applyOperations(map, operations, callback) {
}
}

function fakeImage(png) {
return {
width: png.width,
height: png.height,
data: new Uint8Array(png.data),
complete: true
};
}

const cache = {};

function cached(data, callback) {
Expand Down Expand Up @@ -148,20 +140,24 @@ sinon.stub(ajax, 'getArrayBuffer', (url, callback) => {
});

sinon.stub(ajax, 'getImage', (url, callback) => {
if (cache[url]) return cached(fakeImage(cache[url]), callback);
if (cache[url]) return cached(cache[url], callback);
return request({url: url, encoding: null}, (error, response, body) => {
if (!error && response.statusCode >= 200 && response.statusCode < 300) {
new PNG().parse(body, (err, png) => {
if (err) return callback(err);
cache[url] = png;
callback(null, fakeImage(png));
callback(null, png);
});
} else {
callback(error || new Error(response.statusCode));
}
});
});

sinon.stub(browser, 'getImageData', (img) => {
return new Uint8Array(img.data);
});

// Hack: since node doesn't have any good video codec modules, just grab a png with
// the first frame and fake the video API.
sinon.stub(ajax, 'getVideo', (urls, callback) => {
Expand Down

0 comments on commit 2b50f16

Please sign in to comment.