'use strict'
var isObj = require('is-plain-obj')
var isBase64 = require('is-base64')
var rect = require('parse-rect')
var extend = require('object-assign')
var isBlob = require('is-blob')
var clipPixels = require('clip-pixels')
var isBrowser = require('is-browser')
var loadUrl = require('./lib/url')
var loadRaw = require('./lib/raw')
var loadGl = require('./lib/gl')
var cache = require('./lib/cache')
var pxls = require('pxls')
module.exports = function (src, o, cb) {
// tagged template
if (Array.isArray(src) && src.raw) src = String.raw.apply(this, arguments)
// detect callback arg
if (typeof o === 'function') {
cb = o
o = isObj(src) ? src : null
}
return getPixels(src, o).then(function (data) {
// cache self pixel data
if (!cache.get(data)) {
cache.set(data, data)
}
if (cb) cb(null, data)
return data
}, function (err) {
if (cb) cb(err)
throw err
})
}
module.exports.cache = cache
module.exports.all = function getPixelsAll (src, o, cb) {
if (!src) return null
if (typeof o === 'function') {
cb = o
o = null
}
// list
if (Array.isArray(src)) {
var list = src.map(function (source) {
return getPixels(source, o)
})
// return promise resolved with list
return Promise.all(list).then(function (list) {
cb && cb(null, list)
return list
}, function (err) {
cb && cb(err)
return Promise.reject(err)
})
}
// dict
var handlers = {}
var list = []
for (var name in src) {
handlers[name] = list.push(getPixels(src[name], o)) - 1
}
// return promise resolved with dict
return Promise.all(list).then(function (list) {
var result = {}
for (var name in handlers) {
result[name] = list[handlers[name]]
}
cb && cb(null, result)
return result
}, function (err) {
cb && cb(err)
return Promise.reject(err)
})
}
function getPixels(src, o) {
// handle arguments
if (typeof o === 'string') o = {type: o}
else if (!o) o = {}
else if (Array.isArray(o)) o = {shape: o}
else o = extend({}, o)
var cached
// cases when the source in options and options are in the source
if (isObj(src)) o = extend(src, o)
if (o.src || o.source) src = o.src || o.source
if (isObj(src) && (src.src || src.source)) src = src.src || src.source
if (!src) src = {}
// turn cache on by default
if (o.cache == null) o.cache = true
// detect clipping
var width, height
var clip = o.clip && rect(o.clip) || {x: 0, y: 0}
var type = o.type || o.mime
if (cached = checkCached(src, clip)) return cached
var cacheAs = []
captureShape(o)
captureShape(src)
// File & Blob
if (isBrowser && (isBlob(src) || (src instanceof File))) {
// FIXME: try to use createImageBitmap for Blob
src = URL.createObjectURL(src)
cacheAs.push(src)
if (cached = checkCached(src, clip)) return cached
// TODO: detect raw data and decode here, possibly use array-buffer
}
// handle source type
if (typeof src === 'string') {
if (!src) return Promise.reject(new Error('Bad URL'))
cacheAs.push(src)
// convert base64 to datauri
if (isBase64(src, {mime: false})) {
src = pxls(src)
return loadRaw(src, {type: type, cache: o.cache && cacheAs, shape: [width, height], clip: clip})
}
// url, path, datauri
return loadUrl(src, clip).then(function (src) {
if (cached = checkCached(src, clip)) {
return cached
}
captureShape(src)
return loadRaw(src, {type: type, cache: o.cache && cacheAs, shape: [width, height], clip: clip})
})
}
if (src.tagName) {
// SVG Image
if (src.tagName.toLowerCase() === 'image') {
var url = src.getAttribute('xlink:href')
src = new Image()
src.src = url
if (cached = checkCached(url, clip)) return cached
}
// fetch closest image/video
if (src.tagName.toLowerCase() === 'picture') {
src = src.querySelector('img')
if (cached = checkCached(src, clip)) return cached
}
//
if (src.tagName.toLowerCase() === 'img') {
if (cached = checkCached(src.src, clip)) return cached
cacheAs.push(src.src)
if (src.complete) {
captureShape(src)
return loadRaw(src, {type: type, cache: o.cache && cacheAs, shape: [width, height], clip: clip})
}
return new Promise(function (ok, nok) {
src.addEventListener('load', function () {
captureShape(src)
ok(src)
})
src.addEventListener('error', function(err) {
nok(err)
})
}).then(function (src) {
return loadRaw(src, {type: type, cache: o.cache && cacheAs, shape: [width, height], clip: clip})
})
}
//