123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- /**
- * Mega pixel image rendering library for iOS6 Safari
- *
- * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel),
- * which causes unexpected subsampling when drawing it in canvas.
- * By using this library, you can safely render the image with proper stretching.
- *
- * Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>
- * Released under the MIT license
- */
- (function () {
- /**
- * Detect subsampling in loaded image.
- * In iOS, larger images than 2M pixels may be subsampled in rendering.
- */
- function detectSubsampling(img) {
- var iw = img.naturalWidth, ih = img.naturalHeight;
- if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image
- var canvas = document.createElement('canvas');
- canvas.width = canvas.height = 1;
- var ctx = canvas.getContext('2d');
- ctx.drawImage(img, -iw + 1, 0);
- // subsampled image becomes half smaller in rendering size.
- // check alpha channel value to confirm image is covering edge pixel or not.
- // if alpha value is 0 image is not covering, hence subsampled.
- return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
- } else {
- return false;
- }
- }
- /**
- * Detecting vertical squash in loaded image.
- * Fixes a bug which squash image vertically while drawing into canvas for some images.
- */
- function detectVerticalSquash(img, iw, ih) {
- var canvas = document.createElement('canvas');
- canvas.width = 1;
- canvas.height = ih;
- var ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0);
- var data = ctx.getImageData(0, 0, 1, ih).data;
- // search image edge pixel position in case it is squashed vertically.
- var sy = 0;
- var ey = ih;
- var py = ih;
- while (py > sy) {
- var alpha = data[(py - 1) * 4 + 3];
- if (alpha === 0) {
- ey = py;
- } else {
- sy = py;
- }
- py = (ey + sy) >> 1;
- }
- var ratio = (py / ih);
- return (ratio === 0) ? 1 : ratio;
- }
- /**
- * Rendering image element (with resizing) and get its data URL
- */
- function renderImageToDataURL(img, options, doSquash) {
- var canvas = document.createElement('canvas');
- renderImageToCanvas(img, canvas, options, doSquash);
- return canvas.toDataURL("image/jpeg", options.quality || 0.8);
- }
- /**
- * Rendering image element (with resizing) into the canvas element
- */
- function renderImageToCanvas(img, canvas, options, doSquash) {
- var iw = img.naturalWidth, ih = img.naturalHeight;
- var width = options.width, height = options.height;
- var ctx = canvas.getContext('2d');
- ctx.save();
- transformCoordinate(canvas, ctx, width, height, options.orientation);
- var subsampled = detectSubsampling(img);
- if (subsampled) {
- iw /= 2;
- ih /= 2;
- }
- var d = 1024; // size of tiling canvas
- var tmpCanvas = document.createElement('canvas');
- tmpCanvas.width = tmpCanvas.height = d;
- var tmpCtx = tmpCanvas.getContext('2d');
- var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1;
- var dw = Math.ceil(d * width / iw);
- var dh = Math.ceil(d * height / ih / vertSquashRatio);
- var sy = 0;
- var dy = 0;
- while (sy < ih) {
- var sx = 0;
- var dx = 0;
- while (sx < iw) {
- tmpCtx.clearRect(0, 0, d, d);
- tmpCtx.drawImage(img, -sx, -sy);
- ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh);
- sx += d;
- dx += dw;
- }
- sy += d;
- dy += dh;
- }
- ctx.restore();
- tmpCanvas = tmpCtx = null;
- }
- /**
- * Transform canvas coordination according to specified frame size and orientation
- * Orientation value is from EXIF tag
- */
- function transformCoordinate(canvas, ctx, width, height, orientation) {
- switch (orientation) {
- case 5:
- case 6:
- case 7:
- case 8:
- canvas.width = height;
- canvas.height = width;
- break;
- default:
- canvas.width = width;
- canvas.height = height;
- }
- switch (orientation) {
- case 2:
- // horizontal flip
- ctx.translate(width, 0);
- ctx.scale(-1, 1);
- break;
- case 3:
- // 180 rotate left
- ctx.translate(width, height);
- ctx.rotate(Math.PI);
- break;
- case 4:
- // vertical flip
- ctx.translate(0, height);
- ctx.scale(1, -1);
- break;
- case 5:
- // vertical flip + 90 rotate right
- ctx.rotate(0.5 * Math.PI);
- ctx.scale(1, -1);
- break;
- case 6:
- // 90 rotate right
- ctx.rotate(0.5 * Math.PI);
- ctx.translate(0, -height);
- break;
- case 7:
- // horizontal flip + 90 rotate right
- ctx.rotate(0.5 * Math.PI);
- ctx.translate(width, -height);
- ctx.scale(-1, 1);
- break;
- case 8:
- // 90 rotate left
- ctx.rotate(-0.5 * Math.PI);
- ctx.translate(-width, 0);
- break;
- default:
- break;
- }
- }
- /**
- * MegaPixImage class
- */
- function MegaPixImage(srcImage) {
- if (window.Blob && srcImage instanceof Blob) {
- var img = new Image();
- var URL = window.URL && window.URL.createObjectURL ? window.URL :
- window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL :
- null;
- if (!URL) { throw Error("No createObjectURL function found to create blob url"); }
- img.src = URL.createObjectURL(srcImage);
- this.blob = srcImage;
- srcImage = img;
- }
- if (!srcImage.naturalWidth && !srcImage.naturalHeight) {
- var _this = this;
- srcImage.onload = function () {
- var listeners = _this.imageLoadListeners;
- if (listeners) {
- _this.imageLoadListeners = null;
- for (var i = 0, len = listeners.length; i < len; i++) {
- listeners[i]();
- }
- }
- };
- this.imageLoadListeners = [];
- }
- this.srcImage = srcImage;
- }
- /**
- * Rendering megapix image into specified target element
- */
- MegaPixImage.prototype.render = function (target, options) {
- if (this.imageLoadListeners) {
- var _this = this;
- this.imageLoadListeners.push(function () { _this.render(target, options) });
- return;
- }
- options = options || {};
- var imgWidth = this.srcImage.naturalWidth, imgHeight = this.srcImage.naturalHeight,
- width = options.width, height = options.height,
- maxWidth = options.maxWidth, maxHeight = options.maxHeight,
- doSquash = !this.blob || this.blob.type === 'image/jpeg';
- if (width && !height) {
- height = (imgHeight * width / imgWidth) << 0;
- } else if (height && !width) {
- width = (imgWidth * height / imgHeight) << 0;
- } else {
- width = imgWidth;
- height = imgHeight;
- }
- if (maxWidth && width > maxWidth) {
- width = maxWidth;
- height = (imgHeight * width / imgWidth) << 0;
- }
- if (maxHeight && height > maxHeight) {
- height = maxHeight;
- width = (imgWidth * height / imgHeight) << 0;
- }
- var opt = { width: width, height: height };
- for (var k in options) opt[k] = options[k];
- var tagName = target.tagName.toLowerCase();
- if (tagName === 'img') {
- target.src = renderImageToDataURL(this.srcImage, opt, doSquash);
- } else if (tagName === 'canvas') {
- renderImageToCanvas(this.srcImage, target, opt, doSquash);
- }
- if (typeof this.onrender === 'function') {
- this.onrender(target);
- }
- };
- /**
- * Export class to global
- */
- if (typeof define === 'function' && define.amd) {
- define([], function () { return MegaPixImage; }); // for AMD loader
- } else {
- this.MegaPixImage = MegaPixImage;
- }
- })();
|