Bug 891884 - toBlob should support the quality parameter as toDataURL does r=joedrew sr=mrbkap
--HG-- extra : rebase_source : 22574a2852a6147019cc787ab1a8711adca7c0b2
|
@ -100,10 +100,16 @@ public:
|
|||
: JS::UndefinedValue();
|
||||
aRv = ToDataURL(aType, params, aCx, aDataURL);
|
||||
}
|
||||
void ToBlob(nsIFileCallback* aCallback, const nsAString& aType,
|
||||
void ToBlob(JSContext* aCx,
|
||||
nsIFileCallback* aCallback,
|
||||
const nsAString& aType,
|
||||
const Optional<JS::Handle<JS::Value> >& aParams,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
aRv = ToBlob(aCallback, aType);
|
||||
JS::Value params = aParams.WasPassed()
|
||||
? aParams.Value()
|
||||
: JS::UndefinedValue();
|
||||
aRv = ToBlob(aCallback, aType, params, aCx);
|
||||
}
|
||||
|
||||
bool MozOpaque() const
|
||||
|
@ -227,6 +233,11 @@ protected:
|
|||
nsIntSize GetWidthHeight();
|
||||
|
||||
nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
|
||||
nsresult ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aParams,
|
||||
bool* usingCustomParseOptions);
|
||||
nsresult ExtractData(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
nsIInputStream** aStream,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# autofocus attribute (we can't test with mochitests)
|
||||
include autofocus/reftest.list
|
||||
include toblob-todataurl/reftest.list
|
||||
|
||||
skip-if(B2G) == 41464-1a.html 41464-1-ref.html
|
||||
skip-if(B2G) == 41464-1b.html 41464-1-ref.html
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
function init() {
|
||||
function end() {
|
||||
document.documentElement.className = '';
|
||||
}
|
||||
|
||||
function next() {
|
||||
compressAndDisplay(original, end);
|
||||
}
|
||||
|
||||
var original = getImageFromDataUrl(sample);
|
||||
setImgLoadListener(original, next);
|
||||
}
|
||||
|
||||
function compressAndDisplay(image, next) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(image, 0, 0);
|
||||
|
||||
function gotBlob(blob) {
|
||||
var img = getImageFromBlob(blob);
|
||||
setImgLoadListener(img, next);
|
||||
document.body.appendChild(img);
|
||||
}
|
||||
|
||||
// I want to test passing 'undefined' as quality as well
|
||||
if ('quality' in window) {
|
||||
canvas.toBlob(gotBlob, 'image/jpeg', quality);
|
||||
} else {
|
||||
canvas.toBlob(gotBlob, 'image/jpeg');
|
||||
}
|
||||
}
|
||||
|
||||
function setImgLoadListener(img, func) {
|
||||
if (img.complete) {
|
||||
func.call(img, { target: img});
|
||||
} else {
|
||||
img.addEventListener('load', func);
|
||||
}
|
||||
}
|
||||
|
||||
function naturalDimensionsHandler(e) {
|
||||
var img = e.target;
|
||||
img.width = img.naturalWidth;
|
||||
img.height = img.naturalHeight;
|
||||
}
|
||||
|
||||
function getImageFromBlob(blob) {
|
||||
var img = document.createElement('img');
|
||||
img.src = window.URL.createObjectURL(blob);
|
||||
setImgLoadListener(img, naturalDimensionsHandler);
|
||||
setImgLoadListener(img, function(e) {
|
||||
window.URL.revokeObjectURL(e.target.src);
|
||||
});
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
function getImageFromDataUrl(url) {
|
||||
var img = document.createElement('img');
|
||||
img.src = url;
|
||||
setImgLoadListener(img, naturalDimensionsHandler);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
init();
|
|
@ -0,0 +1,56 @@
|
|||
function init() {
|
||||
function end() {
|
||||
document.documentElement.className = '';
|
||||
}
|
||||
|
||||
function next() {
|
||||
compressAndDisplay(original, end);
|
||||
}
|
||||
|
||||
var original = getImageFromDataUrl(sample);
|
||||
setImgLoadListener(original, next);
|
||||
}
|
||||
|
||||
function compressAndDisplay(image, next) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(image, 0, 0);
|
||||
|
||||
var dataUrl;
|
||||
// I want to test passing undefined as well
|
||||
if ('quality' in window) {
|
||||
dataUrl = canvas.toDataURL('image/jpeg', quality);
|
||||
} else {
|
||||
dataUrl = canvas.toDataURL('image/jpeg');
|
||||
}
|
||||
|
||||
var img = getImageFromDataUrl(dataUrl);
|
||||
setImgLoadListener(img, next);
|
||||
document.body.appendChild(img);
|
||||
}
|
||||
|
||||
function setImgLoadListener(img, func) {
|
||||
if (img.complete) {
|
||||
func.call(img, { target: img});
|
||||
} else {
|
||||
img.addEventListener('load', func);
|
||||
}
|
||||
}
|
||||
|
||||
function naturalDimensionsHandler(e) {
|
||||
var img = e.target;
|
||||
img.width = img.naturalWidth;
|
||||
img.height = img.naturalHeight;
|
||||
}
|
||||
|
||||
function getImageFromDataUrl(url) {
|
||||
var img = document.createElement('img');
|
||||
img.src = url;
|
||||
setImgLoadListener(img, naturalDimensionsHandler);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
init();
|
После Ширина: | Высота: | Размер: 49 KiB |
После Ширина: | Высота: | Размер: 2.1 KiB |
После Ширина: | Высота: | Размер: 53 KiB |
После Ширина: | Высота: | Размер: 3.8 KiB |
После Ширина: | Высота: | Размер: 4.8 KiB |
После Ширина: | Высота: | Размер: 6.3 KiB |
После Ширина: | Высота: | Размер: 14 KiB |
|
@ -0,0 +1,2 @@
|
|||
<!doctype html>
|
||||
<html><body><img src='images/q0.jpg'/></table></body></html>
|
|
@ -0,0 +1,2 @@
|
|||
<!doctype html>
|
||||
<html><body><img src='images/q100.jpg'/></table></body></html>
|
|
@ -0,0 +1,2 @@
|
|||
<!doctype html>
|
||||
<html><body><img src='images/q25.jpg'/></table></body></html>
|
|
@ -0,0 +1,2 @@
|
|||
<!doctype html>
|
||||
<html><body><img src='images/q50.jpg'/></table></body></html>
|
|
@ -0,0 +1,2 @@
|
|||
<!doctype html>
|
||||
<html><body><img src='images/q75.jpg'/></table></body></html>
|
|
@ -0,0 +1,2 @@
|
|||
<!doctype html>
|
||||
<html><body><img src='images/q92.jpg'/></table></body></html>
|
|
@ -0,0 +1,16 @@
|
|||
== toblob-quality-0.html quality-0-ref.html
|
||||
== toblob-quality-25.html quality-25-ref.html
|
||||
== toblob-quality-50.html quality-50-ref.html
|
||||
== toblob-quality-75.html quality-75-ref.html
|
||||
== toblob-quality-92.html quality-92-ref.html
|
||||
== toblob-quality-100.html quality-100-ref.html
|
||||
== toblob-quality-undefined.html quality-92-ref.html
|
||||
== toblob-quality-default.html quality-92-ref.html
|
||||
== todataurl-quality-0.html quality-0-ref.html
|
||||
== todataurl-quality-25.html quality-25-ref.html
|
||||
== todataurl-quality-50.html quality-50-ref.html
|
||||
== todataurl-quality-75.html quality-75-ref.html
|
||||
== todataurl-quality-92.html quality-92-ref.html
|
||||
== todataurl-quality-100.html quality-100-ref.html
|
||||
== todataurl-quality-undefined.html quality-92-ref.html
|
||||
== todataurl-quality-default.html quality-92-ref.html
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='blob.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 1;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='blob.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0.25;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='blob.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0.50;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='blob.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0.75;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='blob.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0.92;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='blob.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script src='sample.js'></script>
|
||||
<script src='blob.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = undefined;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='blob.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='dataurl.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 1;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='dataurl.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0.25;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='dataurl.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0.50;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='dataurl.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0.75;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='dataurl.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = 0.92;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='dataurl.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script src='sample.js'></script>
|
||||
<script src='dataurl.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html class='reftest-wait'>
|
||||
<body>
|
||||
<script>
|
||||
var quality = undefined;
|
||||
</script>
|
||||
<script src='sample.js'></script>
|
||||
<script src='dataurl.js'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -475,6 +475,48 @@ HTMLCanvasElement::ExtractData(const nsAString& aType,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aParams,
|
||||
bool* usingCustomParseOptions)
|
||||
{
|
||||
// Quality parameter is only valid for the image/jpeg MIME type
|
||||
if (aType.EqualsLiteral("image/jpeg")) {
|
||||
if (aEncoderOptions.isNumber()) {
|
||||
double quality = aEncoderOptions.toNumber();
|
||||
// Quality must be between 0.0 and 1.0, inclusive
|
||||
if (quality >= 0.0 && quality <= 1.0) {
|
||||
aParams.AppendLiteral("quality=");
|
||||
aParams.AppendInt(NS_lround(quality * 100.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't parsed the aParams check for proprietary options.
|
||||
// The proprietary option -moz-parse-options will take a image lib encoder
|
||||
// parse options string as is and pass it to the encoder.
|
||||
*usingCustomParseOptions = false;
|
||||
if (aParams.Length() == 0 && aEncoderOptions.isString()) {
|
||||
NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
|
||||
nsDependentJSString paramString;
|
||||
if (!paramString.init(aCx, aEncoderOptions.toString())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (StringBeginsWith(paramString, mozParseOptions)) {
|
||||
nsDependentSubstring parseOptions = Substring(paramString,
|
||||
mozParseOptions.Length(),
|
||||
paramString.Length() -
|
||||
mozParseOptions.Length());
|
||||
aParams.Append(parseOptions);
|
||||
*usingCustomParseOptions = true;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
||||
const nsAString& aMimeType,
|
||||
|
@ -496,37 +538,10 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
|||
}
|
||||
|
||||
nsAutoString params;
|
||||
|
||||
// Quality parameter is only valid for the image/jpeg MIME type
|
||||
if (type.EqualsLiteral("image/jpeg")) {
|
||||
if (aEncoderOptions.isNumber()) {
|
||||
double quality = aEncoderOptions.toNumber();
|
||||
// Quality must be between 0.0 and 1.0, inclusive
|
||||
if (quality >= 0.0 && quality <= 1.0) {
|
||||
params.AppendLiteral("quality=");
|
||||
params.AppendInt(NS_lround(quality * 100.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't parsed the params check for proprietary options.
|
||||
// The proprietary option -moz-parse-options will take a image lib encoder
|
||||
// parse options string as is and pass it to the encoder.
|
||||
bool usingCustomParseOptions = false;
|
||||
if (params.Length() == 0 && aEncoderOptions.isString()) {
|
||||
NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
|
||||
nsDependentJSString paramString;
|
||||
if (!paramString.init(aCx, aEncoderOptions.toString())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (StringBeginsWith(paramString, mozParseOptions)) {
|
||||
nsDependentSubstring parseOptions = Substring(paramString,
|
||||
mozParseOptions.Length(),
|
||||
paramString.Length() -
|
||||
mozParseOptions.Length());
|
||||
params.Append(parseOptions);
|
||||
usingCustomParseOptions = true;
|
||||
}
|
||||
bool usingCustomParseOptions;
|
||||
rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
|
@ -559,7 +574,9 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
|||
// XXXkhuey the encoding should be off the main thread, but we're lazy.
|
||||
NS_IMETHODIMP
|
||||
HTMLCanvasElement::ToBlob(nsIFileCallback* aCallback,
|
||||
const nsAString& aType)
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
JSContext* aCx)
|
||||
{
|
||||
// do a trust check if this is a write-only canvas
|
||||
if (mWriteOnly && !nsContentUtils::IsCallerChrome()) {
|
||||
|
@ -576,10 +593,24 @@ HTMLCanvasElement::ToBlob(nsIFileCallback* aCallback,
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsAutoString params;
|
||||
bool usingCustomParseOptions;
|
||||
rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool fallbackToPNG = false;
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
|
||||
rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
|
||||
// If there are unrecognized custom parse options, we should fall back to
|
||||
// the default values for the encoder without any options at all.
|
||||
if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
|
||||
fallbackToPNG = false;
|
||||
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (fallbackToPNG) {
|
||||
|
|
|
@ -46,7 +46,7 @@ interface nsIFileCallback : nsISupports {
|
|||
void receive(in nsIDOMBlob file);
|
||||
};
|
||||
|
||||
[scriptable, uuid(350fddae-4476-4dcd-bccf-9c9c356354a6)]
|
||||
[scriptable, uuid(788f69a4-30e0-42b4-804d-b99549f9d463)]
|
||||
interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
|
||||
{
|
||||
attribute unsigned long width;
|
||||
|
@ -66,8 +66,14 @@ interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
|
|||
// mozGetAsFile(name, type); -- uses given type
|
||||
nsIDOMFile mozGetAsFile(in DOMString name, [optional] in DOMString type);
|
||||
|
||||
// Valid calls are:
|
||||
// toBlob(); -- defaults to image/png
|
||||
// toBlob(type); -- uses given type
|
||||
// toBlob(type, params); -- uses given type, and any valid parameters
|
||||
[implicit_jscontext]
|
||||
void toBlob(in nsIFileCallback callback,
|
||||
[optional] in DOMString type);
|
||||
[optional] in DOMString type,
|
||||
[optional] in jsval params);
|
||||
|
||||
// A Mozilla-only extension to get a canvas context backed by double-buffered
|
||||
// shared memory. Only privileged callers can call this.
|
||||
|
|
|
@ -30,7 +30,9 @@ interface HTMLCanvasElement : HTMLElement {
|
|||
DOMString toDataURL(optional DOMString type = "",
|
||||
optional any encoderOptions);
|
||||
[Throws]
|
||||
void toBlob(FileCallback _callback, optional DOMString type = "");
|
||||
void toBlob(FileCallback _callback,
|
||||
optional DOMString type = "",
|
||||
optional any encoderOptions);
|
||||
};
|
||||
|
||||
// Mozilla specific bits
|
||||
|
|