Re: [whatwg] HTMLCanvasElement toBlob Promise

2015-04-28 Thread Garrett Smith
On 3/18/15, acmesquares . acmesqua...@gmail.com wrote:
 Admittedly a wrapper function is trivial, mostly for API consistency, and
 does simply move the callback elsewhere.


There is a lot of room for improvement for toBlob.

 However that's not unprecedented as Navigator.mediaDevices.getUserMedia()
 was just a promisification of an existing API.


I did not know there was in-place promisification of gUM, but here it is:-
http://w3c.github.io/mediacapture-main/getusermedia.html#dom-mediadevices-getusermedia

The original non-promise version is still documented on MDN as of today:-
 https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia


 Nor is the alternative since OfflineAudioContext.startRendering is being
 superseded by a Promise version in-place.


They really went overboard there with promises on the new draft. I
wonder what will break from those changes.


My gut tells me two things 1) The canvas API (for toBlob, toDataURL)
should have been fixed years ago: 2) don't break the web.

I usually am pretty conservative about not breaking the web. But now
that I'm looking at crumb camera code again, and that API, you're
right (and Adria too). How much would break if toBlob is changed to
return a promise?

  Personally I monkey patched a new method toBlobPromise, and use that to
 the total exclusion of the original. It's unavoidable when using
 Promise.all() anyway.


What are you building?

I used if-else and a callback function to work around the
toDataURL/toBlob sync vs async paths. I'm not particularly proud of
the code, but I will say that the quality of the toBlob/toDataURL API
is reflected in it.

In fact, I was not happy about it when I wrote it and I had other
complaints about the canvas methods (still do). But because I wasn't
an official member of the Nokia project, it would have been out of
place for me to submit a bug re that, and potentially would have had
ramifications, with her employer wondering about the extend of my
help, or why I am filing a bug and providing examples of a project
that I wasn't on. And she did not want to file a bug or provide
feedback, though I offered to help with both of those. The problems
with the canvas API should have been addressed then. But as we can
see, it has been sitting there rotting with no feedback for over two
and a half years, and apparently only Firefox implements it, is that
right?


Chromium Issue 67587:   Add toBlob to Canvas element.
https://code.google.com/p/chromium/issues/detail?id=67587


 Actually on initial encounter, I'd be least surprised if toBlob did return
 a 'promise which resolves with a Blob'. Just like Fetch API Body.blob()


OK, so it seems intuitive to you to have it be designed with a promise.

My gut tells me don't break the web by changing existing behavior. And
I don't care about promises much.

The toBlob algorithm has problems. If it is to be redesigned, it
should address these problems: The formal parameters are missing, it
accepts variable arguments, some those arguments are passed through.
The pass through arguments are passed through as a variable
ambiguously named named `arguments`, there is no type checking to see
if the callback is callable, only to make sure that it is not `null`,
in the middle of the algorithm, there is a step that says return, but
continue, there are two additional parallel processes steps: Step 6
begins in parallel and step 7 kicks off a new task with Queue a
task.

There is an internal method named  a serialisation of the canvas
element's bitmap as a file for which no parameter variables are
specified and in lieu of that, the note optionally with some given
arguments,

I can think of many ways this could be redesigned and respecified.

https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob


If you want to have a stab at a better redesign, I think that's a great idea.


  Regarding existing implementations, Mozilla implemented in core in Feb
 2013, and moved processing off main-thread Feb 2014. Webkit deferred to
 Chromium which never finished implementation, so usage in the wild will
 have been heavily impeded, and would necessarily fall back on toDataURL.
 Jankiness is the least concern given this function can easily crash the
 context.


That's useful to know. Always good to have an idea of what the
consequences are before we go and break it.

 Since the spec is converging on Canvas-in-Worker (transferable ImageData,
 DOM independent CanvasRenderingContext2D etc), I'd also consider how to get
 my Blob image representation inside a Worker.


  I probably shouldn't have dragged toDataURL into this, but in response I'd
 avoid using it. A function which returns either dataURL or Blob is useless
 as no other API speaks dataURL. If forced for legacy reasons I'd probably
 convert using FileReaderSync.readAsDataURL( blob )


Having a mix of sync/async is confusing.
-- 
Garrett
@xkit
ChordCycles.com
garretts.github.io
personx.tumblr.com


Re: [whatwg] HTMLCanvasElement toBlob Promise

2015-04-28 Thread Garrett Smith
On 4/27/15, Garrett Smith dhtmlkitc...@gmail.com wrote:
 On 3/18/15, acmesquares . acmesqua...@gmail.com wrote:

...

 My gut tells me two things 1) The canvas API (for toBlob, toDataURL)
 should have been fixed years ago: 2) don't break the web.

 I usually am pretty conservative about not breaking the web. But now
 that I'm looking at crumb camera

Coremob Camera, not crumb camera (the spellchecker got me).
link: 
https://github.com/coremob/camera/commit/72f73c417725208dd5bf38e5a1b25b458ee7c424#diff-63cc4d41b7c8563893859fa3acdec6beR364
-- 
Garrett
@xkit
ChordCycles.com
garretts.github.io
personx.tumblr.com


Re: [whatwg] HTMLCanvasElement toBlob Promise

2015-03-18 Thread acmesquares .
Admittedly a wrapper function is trivial, mostly for API consistency, and
does simply move the callback elsewhere.

However that's not unprecedented as Navigator.mediaDevices.getUserMedia()
was just a promisification of an existing API.

Nor is the alternative since OfflineAudioContext.startRendering is being
superseded by a Promise version in-place.


 Personally I monkey patched a new method toBlobPromise, and use that to
the total exclusion of the original. It's unavoidable when using
Promise.all() anyway.

Actually on initial encounter, I'd be least surprised if toBlob did return
a 'promise which resolves with a Blob'. Just like Fetch API Body.blob()


 Regarding existing implementations, Mozilla implemented in core in Feb
2013, and moved processing off main-thread Feb 2014. Webkit deferred to
Chromium which never finished implementation, so usage in the wild will
have been heavily impeded, and would necessarily fall back on toDataURL.
Jankiness is the least concern given this function can easily crash the
context.

Since the spec is converging on Canvas-in-Worker (transferable ImageData,
DOM independent CanvasRenderingContext2D etc), I'd also consider how to get
my Blob image representation inside a Worker.


 I probably shouldn't have dragged toDataURL into this, but in response I'd
avoid using it. A function which returns either dataURL or Blob is useless
as no other API speaks dataURL. If forced for legacy reasons I'd probably
convert using FileReaderSync.readAsDataURL( blob )

So yes, I'd leave that synchronous API alone.

 ~adria


On 18 March 2015 at 06:04, Garrett Smith dhtmlkitc...@gmail.com wrote:

 On 3/17/15, Ashley Gullen ash...@scirra.com wrote:
  Making toBlob return a promise is definitely in keeping with the rest of
  the web platform, but it's easy to write a wrapper function to promisify
  it. IMO toBlob is better than toDataURL since it is async so the image
  encoding can happen off main thread, reducing jank. I don't know how easy
  it is for existing implementations to change - is it easy to return a
  promise if no callback is provided?
 

 Do you want the promise to provide an error handler or is this just
 for style consistency?

 // current API
 canvas.toBlob(callback, type, options);

 // Promise with  without error handler
 canvas.toBlob(type, options).then(callback, errback);
 canvas.toBlob(type, options).then(callback)
 canvas.toBlob(type).then(callback);
 canvas.toBlobl().then(callback);

 You could write a wrapper, but I think it's not as simple as you think it
 is.

 Optional middle argument overloading adds complexity. And the
 complexity of adding that complexity is propagated to anyone using
 that API. Because then, if they wanted to get a Promise from toBlob,
 they would first need to feature test the environment see if
 `canvas.toBlob()` with no arguments returns a promise.

 Then, after you've feature tested, you'll need to branch to handle the
 environments which aren't up to speed with the new promise-returning
 canvas.toBlob(). This is going to get into the complexity of handling
 added complexity in a dynamic deployment environment, and it will also
 entail writing a Promise adapter for the fallbacks to the proposal
 will need.

 Promise support is spotty and ECMAScript 6 isn't done. So you'll need
 a fallback for the getBlob adapter branch that handles the
 environments where toBlob doesn't return a promise and the environment
 doesn't support Promises.

 var toBlobSupportsPromise = function() {
   var x = document.createElement(canvas);
   try {
 x = x.toBlob();
 return''+x==[object Promise];
   } catch(ex) {
 return false;
   }
 }();

 A wrapper (untested):

 function getBlob(canvas) {
   if (toBlobSupportsPromise) {
 getBlob = function getBlob(canvas) {
   return canvas.toBlob();
 };
   } else {
 getBlob = function(canvas) {
   return new Promise(function(resolve, reject) {
 canvas.toBlob(function(blob) {
 resolve(blob);
 });
   });
 };
   }
   var ret = getBlob(canvas);
   canvas = null;
   return ret;
 }

 But then you'll need to add support for Promises, where that isn't
 supported. Rewriting a new javascript library sounds like a terrible
 idea.

 Instead of trying to change toBlob, how about a different-named
 function, like `getBlobPromise`? Give browser vendors time and
 discourage libraries from trying to write more wrappers, adapters,
 polyfills, etc.

 Overloading with optional middle arguments creates more problems (like
 Dart, Polymer, Angular, etc).

 I can definitely understand the lure and attraction of getting sucked
 into a promise -- believe me on that!  But when there are too many
 things riding on and coupled to it, well, it gets complicated.

 Me, I'm a simple guy. I'm fine with callbacks. But I don't mind
 exploring these API design ideas.
 --
 Garrett
 @xkit
 ChordCycles.com
 garretts.github.io
 personx.tumblr.com



Re: [whatwg] HTMLCanvasElement toBlob Promise

2015-03-18 Thread Garrett Smith
On 3/17/15, Ashley Gullen ash...@scirra.com wrote:
 Making toBlob return a promise is definitely in keeping with the rest of
 the web platform, but it's easy to write a wrapper function to promisify
 it. IMO toBlob is better than toDataURL since it is async so the image
 encoding can happen off main thread, reducing jank. I don't know how easy
 it is for existing implementations to change - is it easy to return a
 promise if no callback is provided?


Do you want the promise to provide an error handler or is this just
for style consistency?

// current API
canvas.toBlob(callback, type, options);

// Promise with  without error handler
canvas.toBlob(type, options).then(callback, errback);
canvas.toBlob(type, options).then(callback)
canvas.toBlob(type).then(callback);
canvas.toBlobl().then(callback);

You could write a wrapper, but I think it's not as simple as you think it is.

Optional middle argument overloading adds complexity. And the
complexity of adding that complexity is propagated to anyone using
that API. Because then, if they wanted to get a Promise from toBlob,
they would first need to feature test the environment see if
`canvas.toBlob()` with no arguments returns a promise.

Then, after you've feature tested, you'll need to branch to handle the
environments which aren't up to speed with the new promise-returning
canvas.toBlob(). This is going to get into the complexity of handling
added complexity in a dynamic deployment environment, and it will also
entail writing a Promise adapter for the fallbacks to the proposal
will need.

Promise support is spotty and ECMAScript 6 isn't done. So you'll need
a fallback for the getBlob adapter branch that handles the
environments where toBlob doesn't return a promise and the environment
doesn't support Promises.

var toBlobSupportsPromise = function() {
  var x = document.createElement(canvas);
  try {
x = x.toBlob();
return''+x==[object Promise];
  } catch(ex) {
return false;
  }
}();

A wrapper (untested):

function getBlob(canvas) {
  if (toBlobSupportsPromise) {
getBlob = function getBlob(canvas) {
  return canvas.toBlob();
};
  } else {
getBlob = function(canvas) {
  return new Promise(function(resolve, reject) {
canvas.toBlob(function(blob) {
resolve(blob);
});
  });
};
  }
  var ret = getBlob(canvas);
  canvas = null;
  return ret;
}

But then you'll need to add support for Promises, where that isn't
supported. Rewriting a new javascript library sounds like a terrible
idea.

Instead of trying to change toBlob, how about a different-named
function, like `getBlobPromise`? Give browser vendors time and
discourage libraries from trying to write more wrappers, adapters,
polyfills, etc.

Overloading with optional middle arguments creates more problems (like
Dart, Polymer, Angular, etc).

I can definitely understand the lure and attraction of getting sucked
into a promise -- believe me on that!  But when there are too many
things riding on and coupled to it, well, it gets complicated.

Me, I'm a simple guy. I'm fine with callbacks. But I don't mind
exploring these API design ideas.
-- 
Garrett
@xkit
ChordCycles.com
garretts.github.io
personx.tumblr.com


Re: [whatwg] HTMLCanvasElement toBlob Promise

2015-03-17 Thread Ashley Gullen
Making toBlob return a promise is definitely in keeping with the rest of
the web platform, but it's easy to write a wrapper function to promisify
it. IMO toBlob is better than toDataURL since it is async so the image
encoding can happen off main thread, reducing jank. I don't know how easy
it is for existing implementations to change - is it easy to return a
promise if no callback is provided?

I don't see any need to change toDataURL - it's already inefficiently
designed by the fact it returns a data URL which will be bloated compared
to a blob. So toBlob is naturally the preferred option for efficiency.



On 17 March 2015 at 20:02, Garrett Smith dhtmlkitc...@gmail.com wrote:

 On 3/14/15, acmesquares . acmesqua...@gmail.com wrote:
  It would be great if there was a promise-based version of toBlob. Same
  parameters as toDataURL, but return a Promise to a blob.
  I use toBlob heavily with other promise APIs, and this one really stands
  out as in need of modernization.
 
 Hi Adria,

 toBlob:
 https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob
 ...

 The disparity between toDataURL  - sync - and toBlob  - async - can be
 awkward to handle. How would you write it as a promise? I don't agree
 that a promise would make that better; IMO it would just move the
 place of the callback. Maybe you can offer more insight on it. You can
 use a callback in the enclosing scope for both, as an adapter for
 toBlob. For example, (I helped anonymously get this working): -

 https://github.com/coremob/camera/blob/master/vanilla/js/main.js#L339

 function getBlobFromCanvas(canvas, data, callback) {
   if (canvas.toBlob) { //canvas.blob() supported. Store blob.
   // (GS) This assignment is still a mistake; should result `undefined`
 var blob = canvas.toBlob(function(blob){
   data.photo = blob;
   callback(data);
 }, 'image/jpeg');
   } else { // get Base64 dataurl from canvas, then convert it to Blob
 var dataUrl = canvas.toDataURL('image/jpeg');
 data.photo = util.dataUrlToBlob(dataUrl);
 if(data.photo == null) {
   console.log('Storing DataURL instead.');
   isBlobSupported = false;
   data.photo = canvas.toDataURL('image/jpeg');
   }
   callback(data);
   }
 }

 A comment on comments:
 I generally initial my code review comments so that once all the
 issues have been resolved, the resultant code has as few explanatory
 comments as needed (deliberate omission of copyright notice is
 tantamount to professional fraud). This strategy leads to asking if
 there are any comments left, and helps motivate the removal of
 comments which leads to authoring code that is self-explanatory and
 well-named. It also avoids misleading comments, or situations where
 the code comments fall out of sync with what the code is actually
 doing, such as in the example above:- //canvas.blob() supported.
 Store blob.

 This code comment review practice can be replaced by repo commit
 comments (stash/gitlab/github), but comments work great in email or
 other mediums such as this (despite contradicting articles

 http://www.thinkful.com/learn/javascript-best-practices-1/#Comment-as-Much-as-Needed-but-Not-More
 )

 But to get back to your `toBlob` promise idea, would you want it as a
 new method like `getBlob(mimeType, quality)`? But then what about
 toDataURL? Would you have that as a promise, too, or would you leave
 it alone?

 canvas.getBlob(mimeType, quality).then(callback, errback);
  ~ vs ~
 var dataUrl = canvas.getDataURL(mimeType, quality);
 util.dataUrlToBlob(dataUrl, callback, errback);

 ?
 --
 Garrett
 @xkit
 ChordCycles.com
 garretts.github.io
 personx.tumblr.com



Re: [whatwg] HTMLCanvasElement toBlob Promise

2015-03-17 Thread Garrett Smith
On 3/14/15, acmesquares . acmesqua...@gmail.com wrote:
 It would be great if there was a promise-based version of toBlob. Same
 parameters as toDataURL, but return a Promise to a blob.
 I use toBlob heavily with other promise APIs, and this one really stands
 out as in need of modernization.

Hi Adria,

toBlob: https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob
...

The disparity between toDataURL  - sync - and toBlob  - async - can be
awkward to handle. How would you write it as a promise? I don't agree
that a promise would make that better; IMO it would just move the
place of the callback. Maybe you can offer more insight on it. You can
use a callback in the enclosing scope for both, as an adapter for
toBlob. For example, (I helped anonymously get this working): -

https://github.com/coremob/camera/blob/master/vanilla/js/main.js#L339

function getBlobFromCanvas(canvas, data, callback) {
  if (canvas.toBlob) { //canvas.blob() supported. Store blob.
  // (GS) This assignment is still a mistake; should result `undefined`
var blob = canvas.toBlob(function(blob){
  data.photo = blob;
  callback(data);
}, 'image/jpeg');
  } else { // get Base64 dataurl from canvas, then convert it to Blob
var dataUrl = canvas.toDataURL('image/jpeg');
data.photo = util.dataUrlToBlob(dataUrl);
if(data.photo == null) {
  console.log('Storing DataURL instead.');
  isBlobSupported = false;
  data.photo = canvas.toDataURL('image/jpeg');
  }
  callback(data);
  }
}

A comment on comments:
I generally initial my code review comments so that once all the
issues have been resolved, the resultant code has as few explanatory
comments as needed (deliberate omission of copyright notice is
tantamount to professional fraud). This strategy leads to asking if
there are any comments left, and helps motivate the removal of
comments which leads to authoring code that is self-explanatory and
well-named. It also avoids misleading comments, or situations where
the code comments fall out of sync with what the code is actually
doing, such as in the example above:- //canvas.blob() supported.
Store blob.

This code comment review practice can be replaced by repo commit
comments (stash/gitlab/github), but comments work great in email or
other mediums such as this (despite contradicting articles
http://www.thinkful.com/learn/javascript-best-practices-1/#Comment-as-Much-as-Needed-but-Not-More
)

But to get back to your `toBlob` promise idea, would you want it as a
new method like `getBlob(mimeType, quality)`? But then what about
toDataURL? Would you have that as a promise, too, or would you leave
it alone?

canvas.getBlob(mimeType, quality).then(callback, errback);
 ~ vs ~
var dataUrl = canvas.getDataURL(mimeType, quality);
util.dataUrlToBlob(dataUrl, callback, errback);

?
-- 
Garrett
@xkit
ChordCycles.com
garretts.github.io
personx.tumblr.com