Bug 891884 - toBlob should support the quality parameter as toDataURL does r=joedrew sr=mrbkap

--HG--
extra : rebase_source : 22574a2852a6147019cc787ab1a8711adca7c0b2
This commit is contained in:
Julien Wajsberg 2013-07-19 20:19:48 +01:00
Родитель ad4d3e11cc
Коммит 6cc840ad79
38 изменённых файлов: 397 добавлений и 38 удалений

Просмотреть файл

@ -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();

Двоичные данные
content/html/content/reftests/toblob-todataurl/images/original.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 49 KiB

Двоичные данные
content/html/content/reftests/toblob-todataurl/images/q0.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичные данные
content/html/content/reftests/toblob-todataurl/images/q100.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 53 KiB

Двоичные данные
content/html/content/reftests/toblob-todataurl/images/q25.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.8 KiB

Двоичные данные
content/html/content/reftests/toblob-todataurl/images/q50.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.8 KiB

Двоичные данные
content/html/content/reftests/toblob-todataurl/images/q75.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.3 KiB

Двоичные данные
content/html/content/reftests/toblob-todataurl/images/q92.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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