0

I have a small webapp written in JavaScript for WeChat platform, where users can create some images and submit it to server.

Image creation is done using Canvas2D.

Submitting image to server I do using image data string that I receive from canvas.toDataURL(type,quality) and then server PHP script creates binary JPG file on server side once received from the webapp.

All works great on all phones I had opportunity to try (Samsung S3/4/5, Xiaomi, Huawei Honor, Lenovo, Nexus 4/5, iPhone 4S/5/5S/6/6S, iPads...), no issues. Except on client's Samsung Note 2.

Their device runs Android 4.3 and up-to-date WeChat (6.2.6) just like on my Android phones. However, when they submit picture, filesize is 6-7 times larger.

I've checked and actually received file is PNG, and not JPG.

Codes that I use to get image data string from Canvas2D image is:

var jpgData = this.finalPicCanvas.toDataURL("image/jpeg",0.5);

I have set that PHP script that receives this data string, to write down log, what was in the header, received string size etc, and I can clearly see that only when client submits image, received header is "image/png" regardless that in javascript code is set to "image/jpeg".

Here is the log showing png header and large data size: http://snag.gy/ma39y.jpg

Here is the log showing jpg header and normal data size:http://snag.gy/enPvZ.jpg

I am puzzled, what did I do wrong? I have checked specification and toDataURL should be supported since Android 3.4

I've found this specification from 2011 that browsers must support PNG but they are not required to support any other formats and that if provided type is not recognized, that it will return PNG: http://www.w3.org/TR/2011/WD-html5-20110405/the-canvas-element.html#dom-canvas-todataurl

However, considering this is 2015, age of WebGL and mobile devices, I find it hard to believe that there's nothing I can do.

Does anyone has a solution? Highly appreciated!!!

Siniša
  • 2,988
  • 4
  • 24
  • 36

1 Answers1

1

You are correct. It seems like the browser you are running your code on probably returns PNG because it does not handle JPEG. You can read about the exact same situation here. Another reason could possibly be that you have an alpha channel present in the image and the browser does not support coercing to JPEG if there is any alpha channel present. You can potentially get around this by setting the fillStyle to white. Read more about that here.

If it's not an alpha issue and the fillStyle workaround doesn't do the trick, you can try a JPEG encoder like the one found here and do something like the following (modified from code found in the thread of the first link to the GitHub issue):

var toDataURLFailed = false,
    encoder,
    url;

try {
  url = canvas.toDataURL('image/jpeg', 0.5);
} catch (e) {
  // android may generate PNG
  toDataURLFailed = true;
}

if (toDataURLFailed || url.slice(0, 'data:image/jpeg'.length) !== 'data:image/jpeg') {
  try {
    encoder = new JPEGEncoder();
    url = encoder.encode(ctx.getImageData(0, 0, width, height), 100);
  } catch (e) {
    // everything has failed here - do whatever you want in this case
  }
}
Community
  • 1
  • 1
Vinay
  • 6,204
  • 6
  • 38
  • 55
  • 1
    Thanks for answer, provides good references and solution options. – Siniša Sep 20 '15 at 00:56
  • Cool @Sinisa what did you end up going with? Was there an alpha channel, or did you just use JPEGEncoder? – Vinay Sep 21 '15 at 02:05
  • Quick workaround was to check if header "data:image/jpeg" was in returned data string, and if not, for those users, we would generated image 25% of original size, generate PNG and submit to server. File size was about the same as JPG and client didn't mind reduced resolution. Their main concern was upload speed. That was the quickest solution and we didn't have enough time to test JPGEncoder on Chinese Android phones. Server PHP would then accept both PNG and JPG and store them properly on server side. – Siniša Sep 21 '15 at 07:04
  • Good solution given your time constraints. Cheers! – Vinay Sep 21 '15 at 19:15