imageOptimizer.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. var debug = require('debug')('ylt:imageOptimizer');
  2. var Q = require('q');
  3. var Imagemin = require('imagemin');
  4. var jpegoptim = require('imagemin-jpegoptim');
  5. var ImageOptimizer = function() {
  6. var MAX_JPEG_QUALITY = 85;
  7. function optimizeImage(entry) {
  8. var deferred = Q.defer();
  9. var fileSize = entry.weightCheck.body.length;
  10. debug('Current file size is %d', fileSize);
  11. if (isJpeg(entry)) {
  12. debug('File is a JPEG');
  13. // Starting softly with a lossless compression
  14. return compressJpegLosslessly(entry.weightCheck.body)
  15. .then(function(newFile) {
  16. var newFileSize = newFile.contents.length;
  17. debug('JPEG lossless compression complete for %s', entry.url);
  18. if (newFileSize < fileSize) {
  19. entry.weightCheck.lossless = entry.weightCheck.optimized = newFileSize;
  20. entry.weightCheck.isOptimized = false;
  21. debug('Filesize is %d bytes smaller (-%d%)', fileSize - newFileSize, Math.round((fileSize - newFileSize) * 100 / fileSize));
  22. }
  23. // Now let's compress lossy to MAX_JPEG_QUALITY
  24. return compressJpegLossly(entry.weightCheck.body);
  25. })
  26. .then(function(newFile) {
  27. var newFileSize = newFile.contents.length;
  28. debug('JPEG lossy compression complete for %s', entry.url);
  29. if (newFileSize < fileSize) {
  30. entry.weightCheck.lossy = newFileSize;
  31. entry.weightCheck.isOptimized = false;
  32. debug('Filesize is %d bytes smaller (-%d%)', fileSize - newFileSize, Math.round((fileSize - newFileSize) * 100 / fileSize));
  33. if (newFileSize < entry.weightCheck.lossless) {
  34. entry.weightCheck.optimized = newFileSize;
  35. }
  36. }
  37. return entry;
  38. });
  39. } else if (isPNG(entry)) {
  40. debug('File is a PNG');
  41. // Starting softly with a lossless compression
  42. return compressPngLosslessly(entry.weightCheck.body)
  43. .then(function(newFile) {
  44. var newFileSize = newFile.contents.length;
  45. debug('PNG lossless compression complete for %s', entry.url);
  46. if (newFileSize < fileSize) {
  47. entry.weightCheck.lossless = entry.weightCheck.optimized = newFileSize;
  48. entry.weightCheck.isOptimized = false;
  49. debug('Filesize is %d bytes smaller (-%d%)', fileSize - newFileSize, Math.round((fileSize - newFileSize) * 100 / fileSize));
  50. }
  51. return entry;
  52. });
  53. } else {
  54. debug('File type is not an optimizable image');
  55. deferred.resolve(entry);
  56. }
  57. return deferred.promise;
  58. }
  59. function isJpeg(entry) {
  60. return entry.isImage && entry.contentType === 'image/jpeg';
  61. }
  62. function compressJpegLosslessly(imageBody) {
  63. var deferred = Q.defer();
  64. var startTime = Date.now();
  65. debug('Starting JPEG lossless compression');
  66. new Imagemin()
  67. .src(imageBody)
  68. .use(Imagemin.jpegtran())
  69. .run(function (err, files) {
  70. if (err) {
  71. deferred.reject(err);
  72. } else {
  73. deferred.resolve(files[0]);
  74. var endTime = Date.now();
  75. debug('compressJpegLosslessly took %d ms', endTime - startTime);
  76. }
  77. });
  78. return deferred.promise;
  79. }
  80. function compressJpegLossly(imageBody) {
  81. var deferred = Q.defer();
  82. var startTime = Date.now();
  83. debug('Starting JPEG lossy compression');
  84. new Imagemin()
  85. .src(imageBody)
  86. .use(jpegoptim({max: MAX_JPEG_QUALITY}))
  87. .run(function (err, files) {
  88. if (err) {
  89. deferred.reject(err);
  90. } else {
  91. deferred.resolve(files[0]);
  92. var endTime = Date.now();
  93. debug('compressJpegLossly took %d ms', endTime - startTime);
  94. }
  95. });
  96. return deferred.promise;
  97. }
  98. function isPNG(entry) {
  99. return entry.isImage && entry.contentType === 'image/png';
  100. }
  101. function compressPngLosslessly(imageBody) {
  102. var deferred = Q.defer();
  103. var startTime = Date.now();
  104. debug('Starting PNG losslessly compression');
  105. new Imagemin()
  106. .src(imageBody)
  107. .use(Imagemin.optipng({optimizationLevel: 2}))
  108. .run(function (err, files) {
  109. if (err) {
  110. deferred.reject(err);
  111. } else {
  112. deferred.resolve(files[0]);
  113. var endTime = Date.now();
  114. debug('compressPngLosslessly took %d ms', endTime - startTime);
  115. }
  116. });
  117. return deferred.promise;
  118. }
  119. return {
  120. //recompressIfImage: recompressIfImage,
  121. optimizeImage: optimizeImage,
  122. compressJpegLosslessly: compressJpegLosslessly,
  123. compressJpegLossly: compressJpegLossly,
  124. compressPngLosslessly: compressPngLosslessly
  125. };
  126. };
  127. module.exports = new ImageOptimizer();