identiconpp is a library to generate identicons for C++.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
4.3KB

  1. /* This file is part of identiconpp.
  2. * Copyright © 2018 tastytea <tastytea@tastytea.de>
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, version 3.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <exception>
  17. #include <stdexcept>
  18. #include <sstream>
  19. #include <cmath>
  20. #include "identiconpp.hpp"
  21. #include "debug.hpp"
  22. Identiconpp::Identiconpp(const uint8_t columns, const uint8_t rows,
  23. algorithm type,
  24. const string &background,
  25. const vector<string> &foreground,
  26. const array<const uint8_t, 2> &padding)
  27. : _rows(rows)
  28. , _columns(columns)
  29. , _type(type)
  30. , _background(background)
  31. , _foreground(foreground)
  32. , _padding(padding)
  33. {
  34. check_color(background);
  35. for (const string &color : foreground)
  36. {
  37. check_color(color);
  38. }
  39. }
  40. Magick::Image Identiconpp::generate(const string &digest, const uint16_t width)
  41. {
  42. ttdebug << "Using digest: " << digest << '\n';
  43. check_entropy(digest, _type);
  44. const std::int16_t imgwidth = width - _padding[0] * 2;
  45. const std::int16_t imgheight =
  46. std::round(static_cast<float>(imgwidth) / _columns * _rows);
  47. ttdebug << "width: " << std::to_string(imgwidth)
  48. << "+" << std::to_string(_padding[0] * 2)
  49. << ", height: " << std::to_string(imgheight)
  50. << "+" << std::to_string(_padding[1] * 2)
  51. << "\n";
  52. if (imgwidth <= 0 || imgheight <= 0)
  53. {
  54. throw std::invalid_argument("Width or height is zero or less.");
  55. }
  56. Magick::Image img;
  57. switch (_type)
  58. {
  59. case algorithm::ltr_symmetric:
  60. {
  61. img = generate_ltr_symmetric(digest);
  62. break;
  63. }
  64. case algorithm::ltr_asymmetric:
  65. {
  66. img = generate_ltr_asymmetric(digest);
  67. break;
  68. }
  69. case algorithm::sigil:
  70. {
  71. img = generate_sigil(digest);
  72. break;
  73. }
  74. }
  75. img.backgroundColor(Magick::Color('#' + _background));
  76. img.scale(Magick::Geometry(imgwidth, imgheight));
  77. img.borderColor(Magick::Color('#' + _background));
  78. img.border(Magick::Geometry(_padding[0], _padding[1]));
  79. return img;
  80. }
  81. bool Identiconpp::get_bit(const uint16_t bit, const string &digest)
  82. {
  83. std::stringstream ss;
  84. ss << std::hex << digest[bit / 4];
  85. // std::stringstream does not support writing into uint8_t
  86. unsigned short nibble;
  87. ss >> nibble;
  88. // Shift nibble to the right until the bit we want is on the right border.
  89. // Then check if it is set.
  90. if (nibble >> (3 - bit % 4) & 1)
  91. {
  92. ttdebug << "Bit " << std::to_string(bit + 1) << " is set.\n";
  93. return true;
  94. }
  95. return false;
  96. }
  97. Magick::Color Identiconpp::get_color(const uint16_t firstbit,
  98. const string &digest)
  99. {
  100. // Number of bits to use
  101. const uint16_t colorbits = std::floor(std::log2(_foreground.size())) + 1;
  102. // Extract approximation
  103. std::stringstream ss;
  104. if (colorbits % 4 == 0)
  105. {
  106. ss << std::hex << digest.substr(firstbit / 4, colorbits / 4);
  107. }
  108. else
  109. {
  110. ss << std::hex << digest.substr(firstbit / 4, colorbits / 4 + 1);
  111. }
  112. // std::stringstream does not support writing into uint16_t
  113. unsigned short bits;
  114. ss >> bits;
  115. // Shift an one $colorbits times to the left, substract 1. This leaves us
  116. // with $colorbits ones. Then AND bits and our ones to keep only as many
  117. // bits as we need.
  118. bits = bits & ((1 << colorbits) - 1);
  119. // We may get a number that is slightly too big if _foreground.size() is not
  120. // a power of 2.
  121. if (bits > (_foreground.size() - 1))
  122. {
  123. bits -= _foreground.size();
  124. }
  125. // Lookup und set color
  126. ttdebug << "Color: #" << _foreground[bits] << '\n';
  127. return Magick::Color("#" + _foreground[bits]);
  128. }