skins.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. var logging = require("./logging");
  2. var lwip = require("lwip");
  3. var fs = require("fs");
  4. var exp = {};
  5. // extracts the face from an image +buffer+
  6. // result is saved to a file called +outname+
  7. // callback: error
  8. exp.extract_face = function(buffer, outname, callback) {
  9. lwip.open(buffer, "png", function(err, image) {
  10. if (err) {
  11. callback(err);
  12. } else {
  13. image.batch()
  14. .crop(8, 8, 15, 15) // face
  15. .opacify() // remove transparency
  16. .writeFile(outname, function(write_err) {
  17. if (write_err) {
  18. callback(write_err);
  19. } else {
  20. callback(null);
  21. }
  22. });
  23. }
  24. });
  25. };
  26. // extracts the helm from an image +buffer+ and lays it over a +facefile+
  27. // +facefile+ is the filename of an image produced by extract_face
  28. // result is saved to a file called +outname+
  29. // callback: error
  30. exp.extract_helm = function(rid, facefile, buffer, outname, callback) {
  31. lwip.open(buffer, "png", function(err, skin_img) {
  32. if (err) {
  33. callback(err);
  34. } else {
  35. lwip.open(facefile, function(open_err, face_img) {
  36. if (open_err) {
  37. callback(open_err);
  38. } else {
  39. face_img.toBuffer("png", { compression: "none" }, function(buf_err, face_buffer) {
  40. if (buf_err) {
  41. callback(buf_err);
  42. } else {
  43. skin_img.crop(40, 8, 47, 15, function(crop_err, helm_img) {
  44. if (crop_err) {
  45. callback(crop_err);
  46. } else {
  47. face_img.paste(0, 0, helm_img, function(img_err, face_helm_img) {
  48. if (img_err) {
  49. callback(img_err);
  50. } else {
  51. if (!skin_img.__trans) {
  52. logging.debug(rid, "Skin is not transparent, skipping helm!");
  53. callback(null);
  54. } else {
  55. face_helm_img.toBuffer("png", {compression: "none"}, function(buf_err2, face_helm_buffer) {
  56. if (buf_err2) {
  57. callback(buf_err2);
  58. } else {
  59. if (face_helm_buffer.toString() !== face_buffer.toString()) {
  60. face_helm_img.writeFile(outname, function(write_err) {
  61. callback(write_err);
  62. });
  63. } else {
  64. logging.debug(rid, "helm img == face img, not storing!");
  65. callback(null);
  66. }
  67. }
  68. });
  69. }
  70. }
  71. });
  72. }
  73. });
  74. }
  75. });
  76. }
  77. });
  78. }
  79. });
  80. };
  81. // resizes the image file +inname+ to +size+ by +size+ pixels
  82. // callback: error, image buffer
  83. exp.resize_img = function(inname, size, callback) {
  84. lwip.open(inname, function(err, image) {
  85. if (err) {
  86. callback(err, null);
  87. } else {
  88. image.batch()
  89. .resize(size, size, "nearest-neighbor") // nearest-neighbor doesn't blur
  90. .toBuffer("png", function(buf_err, buffer) {
  91. if (buf_err) {
  92. callback(buf_err, null);
  93. } else {
  94. callback(null, buffer);
  95. }
  96. });
  97. }
  98. });
  99. };
  100. // returns "mhf_alex" or "mhf_steve" calculated by the +uuid+
  101. exp.default_skin = function(uuid) {
  102. if (uuid.length <= 16) {
  103. if (uuid.toLowerCase() === "mhf_alex") {
  104. return uuid;
  105. } else {
  106. // we can't get the skin type by username
  107. return "mhf_steve";
  108. }
  109. } else {
  110. // great thanks to Minecrell for research into Minecraft and Java's UUID hashing!
  111. // https://git.io/xJpV
  112. // MC uses `uuid.hashCode() & 1` for alex
  113. // that can be compacted to counting the LSBs of every 4th byte in the UUID
  114. // an odd sum means alex, an even sum means steve
  115. // XOR-ing all the LSBs gives us 1 for alex and 0 for steve
  116. var lsbs_even = parseInt(uuid[ 7], 16) ^
  117. parseInt(uuid[15], 16) ^
  118. parseInt(uuid[23], 16) ^
  119. parseInt(uuid[31], 16);
  120. return lsbs_even ? "mhf_alex" : "mhf_steve";
  121. }
  122. };
  123. // helper method for opening a skin file from +skinpath+
  124. // callback: error, image buffer
  125. exp.open_skin = function(rid, skinpath, callback) {
  126. fs.readFile(skinpath, function(err, buf) {
  127. if (err) {
  128. callback(err, null);
  129. } else {
  130. callback(null, buf);
  131. }
  132. });
  133. };
  134. // write the image +buffer+ to the +outpath+ file
  135. // the image is stripped down by lwip.
  136. // callback: error, image
  137. exp.save_image = function(buffer, outpath, callback) {
  138. lwip.open(buffer, "png", function(err, image) {
  139. if (err) {
  140. callback(err, image);
  141. } else {
  142. image.writeFile(outpath, function(write_err) {
  143. if (write_err) {
  144. callback(write_err, image);
  145. } else {
  146. callback(null, image);
  147. }
  148. });
  149. }
  150. });
  151. };
  152. module.exports = exp;