image_prepare.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "ui/image/image_prepare.h"
  8. #include "ui/effects/animation_value.h"
  9. #include "ui/style/style_core.h"
  10. #include "ui/painter.h"
  11. #include "base/flat_map.h"
  12. #include "base/debug_log.h"
  13. #include "base/bytes.h"
  14. #include "styles/palette.h"
  15. #include "styles/style_basic.h"
  16. #include <zlib.h>
  17. #include <QtCore/QFile>
  18. #include <QtCore/QBuffer>
  19. #include <QtCore/QMutex>
  20. #include <QtGui/QImageReader>
  21. #include <QtSvg/QSvgRenderer>
  22. #include <jpeglib.h>
  23. #include <setjmp.h>
  24. struct my_error_mgr : public jpeg_error_mgr {
  25. jmp_buf setjmp_buffer;
  26. };
  27. extern "C" {
  28. static void my_error_exit(j_common_ptr cinfo) {
  29. (*cinfo->err->output_message)(cinfo);
  30. my_error_mgr* myerr = (my_error_mgr*)cinfo->err;
  31. longjmp(myerr->setjmp_buffer, 1);
  32. }
  33. } // extern "C"
  34. namespace Images {
  35. namespace {
  36. // They should be smaller.
  37. constexpr auto kMaxGzipFileSize = 5 * 1024 * 1024;
  38. TG_FORCE_INLINE uint64 BlurGetColors(const uchar *p) {
  39. return (uint64)p[0]
  40. + ((uint64)p[1] << 16)
  41. + ((uint64)p[2] << 32)
  42. + ((uint64)p[3] << 48);
  43. }
  44. const QImage &EllipseMaskCached(QSize size) {
  45. const auto key = (uint64(uint32(size.width())) << 32)
  46. | uint64(uint32(size.height()));
  47. static auto Masks = base::flat_map<uint64, QImage>();
  48. static auto Mutex = QMutex();
  49. auto lock = QMutexLocker(&Mutex);
  50. const auto i = Masks.find(key);
  51. if (i != end(Masks)) {
  52. return i->second;
  53. }
  54. lock.unlock();
  55. auto mask = EllipseMask(size, 1.);
  56. lock.relock();
  57. return Masks.emplace(key, std::move(mask)).first->second;
  58. }
  59. std::array<QImage, 4> PrepareCornersMask(int radius) {
  60. auto result = std::array<QImage, 4>();
  61. const auto side = radius * style::DevicePixelRatio();
  62. auto full = QImage(
  63. QSize(side, side) * 3,
  64. QImage::Format_ARGB32_Premultiplied);
  65. full.fill(Qt::transparent);
  66. {
  67. QPainter p(&full);
  68. PainterHighQualityEnabler hq(p);
  69. p.setPen(Qt::NoPen);
  70. p.setBrush(Qt::white);
  71. p.drawRoundedRect(0, 0, side * 3, side * 3, side, side);
  72. }
  73. result[0] = full.copy(0, 0, side, side);
  74. result[1] = full.copy(side * 2, 0, side, side);
  75. result[2] = full.copy(0, side * 2, side, side);
  76. result[3] = full.copy(side * 2, side * 2, side, side);
  77. for (auto &image : result) {
  78. image.setDevicePixelRatio(style::DevicePixelRatio());
  79. }
  80. return result;
  81. }
  82. template <int kBits> // 4 means 16x16, 3 means 8x8
  83. [[nodiscard]] QImage DitherGeneric(const QImage &image) {
  84. static_assert(kBits >= 1 && kBits <= 4);
  85. constexpr auto kSquareSide = (1 << kBits);
  86. constexpr auto kShift = kSquareSide / 2;
  87. constexpr auto kMask = (kSquareSide - 1);
  88. const auto width = image.width();
  89. const auto height = image.height();
  90. const auto area = width * height;
  91. const auto shifts = std::make_unique<uchar[]>(area);
  92. bytes::set_random(bytes::make_span(shifts.get(), area));
  93. // shiftx = int(shift & kMask) - kShift;
  94. // shifty = int((shift >> 4) & kMask) - kShift;
  95. // Clamp shifts close to edges.
  96. for (auto y = 0; y != kShift; ++y) {
  97. const auto min = kShift - y;
  98. const auto shifted = (min << 4);
  99. auto shift = shifts.get() + y * width;
  100. for (const auto till = shift + width; shift != till; ++shift) {
  101. if (((*shift >> 4) & kMask) < min) {
  102. *shift = shifted | (*shift & 0x0F);
  103. }
  104. }
  105. }
  106. for (auto y = height - (kShift - 1); y != height; ++y) {
  107. const auto max = kShift + (height - y - 1);
  108. const auto shifted = (max << 4);
  109. auto shift = shifts.get() + y * width;
  110. for (const auto till = shift + width; shift != till; ++shift) {
  111. if (((*shift >> 4) & kMask) > max) {
  112. *shift = shifted | (*shift & 0x0F);
  113. }
  114. }
  115. }
  116. for (auto shift = shifts.get(), ytill = shift + area
  117. ; shift != ytill
  118. ; shift += width - kShift) {
  119. for (const auto till = shift + kShift; shift != till; ++shift) {
  120. const auto min = (till - shift);
  121. if ((*shift & kMask) < min) {
  122. *shift = (*shift & 0xF0) | min;
  123. }
  124. }
  125. }
  126. for (auto shift = shifts.get(), ytill = shift + area; shift != ytill;) {
  127. shift += width - (kShift - 1);
  128. for (const auto till = shift + (kShift - 1); shift != till; ++shift) {
  129. const auto max = kShift + (till - shift - 1);
  130. if ((*shift & kMask) > max) {
  131. *shift = (*shift & 0xF0) | max;
  132. }
  133. }
  134. }
  135. auto result = image;
  136. result.detach();
  137. const auto src = reinterpret_cast<const uint32*>(image.constBits());
  138. const auto dst = reinterpret_cast<uint32*>(result.bits());
  139. for (auto index = 0; index != area; ++index) {
  140. const auto shift = shifts[index];
  141. const auto shiftx = int(shift & kMask) - kShift;
  142. const auto shifty = int((shift >> 4) & kMask) - kShift;
  143. dst[index] = src[index + (shifty * width) + shiftx];
  144. }
  145. return result;
  146. }
  147. [[nodiscard]] QImage GenerateSmallComplexGradient(
  148. const std::vector<QColor> &colors,
  149. int rotation,
  150. float progress) {
  151. const auto positions = std::vector<std::pair<float, float>>{
  152. { 0.80f, 0.10f },
  153. { 0.60f, 0.20f },
  154. { 0.35f, 0.25f },
  155. { 0.25f, 0.60f },
  156. { 0.20f, 0.90f },
  157. { 0.40f, 0.80f },
  158. { 0.65f, 0.75f },
  159. { 0.75f, 0.40f },
  160. };
  161. const auto positionsForPhase = [&](int phase) {
  162. auto result = std::vector<std::pair<float, float>>(4);
  163. for (auto i = 0; i != 4; ++i) {
  164. result[i] = positions[(phase + i * 2) % 8];
  165. result[i].second = 1.f - result[i].second;
  166. }
  167. return result;
  168. };
  169. const auto phase = std::clamp(rotation, 0, 315) / 45;
  170. const auto previousPhase = (phase + 1) % 8;
  171. const auto previous = positionsForPhase(previousPhase);
  172. const auto current = positionsForPhase(phase);
  173. constexpr auto kWidth = 64;
  174. constexpr auto kHeight = 64;
  175. static const auto pixelCache = [&] {
  176. auto result = std::make_unique<float[]>(kWidth * kHeight * 2);
  177. const auto invwidth = 1.f / kWidth;
  178. const auto invheight = 1.f / kHeight;
  179. auto floats = result.get();
  180. for (auto y = 0; y != kHeight; ++y) {
  181. const auto directPixelY = y * invheight;
  182. const auto centerDistanceY = directPixelY - 0.5f;
  183. const auto centerDistanceY2 = centerDistanceY * centerDistanceY;
  184. for (auto x = 0; x != kWidth; ++x) {
  185. const auto directPixelX = x * invwidth;
  186. const auto centerDistanceX = directPixelX - 0.5f;
  187. const auto centerDistance = sqrtf(
  188. centerDistanceX * centerDistanceX + centerDistanceY2);
  189. const auto swirlFactor = 0.35f * centerDistance;
  190. const auto theta = swirlFactor * swirlFactor * 0.8f * 8.0f;
  191. const auto sinTheta = sinf(theta);
  192. const auto cosTheta = cosf(theta);
  193. *floats++ = std::max(
  194. 0.0f,
  195. std::min(
  196. 1.0f,
  197. (0.5f
  198. + centerDistanceX * cosTheta
  199. - centerDistanceY * sinTheta)));
  200. *floats++ = std::max(
  201. 0.0f,
  202. std::min(
  203. 1.0f,
  204. (0.5f
  205. + centerDistanceX * sinTheta
  206. + centerDistanceY * cosTheta)));
  207. }
  208. }
  209. return result;
  210. }();
  211. const auto colorsCount = int(colors.size());
  212. auto colorsFloat = std::vector<std::array<float, 3>>(colorsCount);
  213. for (auto i = 0; i != colorsCount; ++i) {
  214. colorsFloat[i] = {
  215. float(colors[i].red()),
  216. float(colors[i].green()),
  217. float(colors[i].blue()),
  218. };
  219. }
  220. auto result = QImage(
  221. kWidth,
  222. kHeight,
  223. QImage::Format_RGB32);
  224. Assert(result.bytesPerLine() == kWidth * 4);
  225. auto cache = pixelCache.get();
  226. auto pixels = reinterpret_cast<uint32*>(result.bits());
  227. for (auto y = 0; y != kHeight; ++y) {
  228. for (auto x = 0; x != kWidth; ++x) {
  229. const auto pixelX = *cache++;
  230. const auto pixelY = *cache++;
  231. auto distanceSum = 0.f;
  232. auto r = 0.f;
  233. auto g = 0.f;
  234. auto b = 0.f;
  235. for (auto i = 0; i != colorsCount; ++i) {
  236. const auto colorX = previous[i].first
  237. + (current[i].first - previous[i].first) * progress;
  238. const auto colorY = previous[i].second
  239. + (current[i].second - previous[i].second) * progress;
  240. const auto dx = pixelX - colorX;
  241. const auto dy = pixelY - colorY;
  242. const auto distance = std::max(
  243. 0.0f,
  244. 0.9f - sqrtf(dx * dx + dy * dy));
  245. const auto square = distance * distance;
  246. const auto fourth = square * square;
  247. distanceSum += fourth;
  248. r += fourth * colorsFloat[i][0];
  249. g += fourth * colorsFloat[i][1];
  250. b += fourth * colorsFloat[i][2];
  251. }
  252. const auto red = uint32(r / distanceSum);
  253. const auto green = uint32(g / distanceSum);
  254. const auto blue = uint32(b / distanceSum);
  255. *pixels++ = 0xFF000000U | (red << 16) | (green << 8) | blue;
  256. }
  257. }
  258. return result;
  259. }
  260. [[nodiscard]] QImage GenerateComplexGradient(
  261. QSize size,
  262. const std::vector<QColor> &colors,
  263. int rotation,
  264. float progress) {
  265. auto exact = GenerateSmallComplexGradient(colors, rotation, progress);
  266. return (exact.size() == size)
  267. ? exact
  268. : exact.scaled(
  269. size,
  270. Qt::IgnoreAspectRatio,
  271. Qt::SmoothTransformation);
  272. }
  273. } // namespace
  274. QPixmap PixmapFast(QImage &&image) {
  275. Expects(image.format() == QImage::Format_ARGB32_Premultiplied
  276. || image.format() == QImage::Format_RGB32);
  277. return QPixmap::fromImage(std::move(image), Qt::NoFormatConversion);
  278. }
  279. const std::array<QImage, 4> &CornersMask(ImageRoundRadius radius) {
  280. if (radius == ImageRoundRadius::Large) {
  281. static auto Mask = PrepareCornersMask(st::roundRadiusLarge);
  282. return Mask;
  283. } else {
  284. static auto Mask = PrepareCornersMask(st::roundRadiusSmall);
  285. return Mask;
  286. }
  287. }
  288. std::array<QImage, 4> PrepareCorners(
  289. ImageRoundRadius radius,
  290. const style::color &color) {
  291. auto result = CornersMask(radius);
  292. for (auto &image : result) {
  293. style::colorizeImage(image, color->c, &image);
  294. }
  295. return result;
  296. }
  297. std::array<QImage, 4> CornersMask(int radius) {
  298. return PrepareCornersMask(radius);
  299. }
  300. QImage EllipseMask(QSize size, double ratio) {
  301. size *= ratio;
  302. auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
  303. result.fill(Qt::transparent);
  304. QPainter p(&result);
  305. PainterHighQualityEnabler hq(p);
  306. p.setBrush(Qt::white);
  307. p.setPen(Qt::NoPen);
  308. p.drawEllipse(QRect(QPoint(), size));
  309. p.end();
  310. result.setDevicePixelRatio(ratio);
  311. return result;
  312. }
  313. std::array<QImage, 4> PrepareCorners(
  314. int radius,
  315. const style::color &color) {
  316. auto result = CornersMask(radius);
  317. for (auto &image : result) {
  318. style::colorizeImage(image, color->c, &image);
  319. }
  320. return result;
  321. }
  322. [[nodiscard]] QByteArray UnpackGzip(const QByteArray &bytes) {
  323. z_stream stream;
  324. stream.zalloc = nullptr;
  325. stream.zfree = nullptr;
  326. stream.opaque = nullptr;
  327. stream.avail_in = 0;
  328. stream.next_in = nullptr;
  329. int res = inflateInit2(&stream, 16 + MAX_WBITS);
  330. if (res != Z_OK) {
  331. return bytes;
  332. }
  333. const auto guard = gsl::finally([&] { inflateEnd(&stream); });
  334. auto result = QByteArray(kMaxGzipFileSize + 1, char(0));
  335. stream.avail_in = bytes.size();
  336. stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(bytes.data()));
  337. stream.avail_out = 0;
  338. while (!stream.avail_out) {
  339. stream.avail_out = result.size();
  340. stream.next_out = reinterpret_cast<Bytef*>(result.data());
  341. int res = inflate(&stream, Z_NO_FLUSH);
  342. if (res != Z_OK && res != Z_STREAM_END) {
  343. return bytes;
  344. } else if (!stream.avail_out) {
  345. return bytes;
  346. }
  347. }
  348. result.resize(result.size() - stream.avail_out);
  349. return result;
  350. }
  351. [[nodiscard]] ReadResult ReadGzipSvg(const ReadArgs &args) {
  352. const auto bytes = UnpackGzip(args.content);
  353. if (bytes.isEmpty()) {
  354. LOG(("Svg Error: Couldn't unpack gzip-ed content."));
  355. return {};
  356. }
  357. auto renderer = QSvgRenderer(bytes);
  358. if (!renderer.isValid()) {
  359. LOG(("Svg Error: Invalid data."));
  360. return {};
  361. }
  362. auto size = renderer.defaultSize();
  363. if (!args.maxSize.isEmpty()
  364. && (size.width() > args.maxSize.width()
  365. || size.height() > args.maxSize.height())) {
  366. size = size.scaled(args.maxSize, Qt::KeepAspectRatio);
  367. }
  368. if (size.isEmpty()) {
  369. LOG(("Svg Error: Bad size %1x%2."
  370. ).arg(renderer.defaultSize().width()
  371. ).arg(renderer.defaultSize().height()));
  372. return {};
  373. }
  374. auto result = ReadResult();
  375. result.image = QImage(size, QImage::Format_ARGB32_Premultiplied);
  376. result.image.fill(Qt::transparent);
  377. {
  378. QPainter p(&result.image);
  379. renderer.render(&p, QRect(QPoint(), size));
  380. }
  381. result.format = "svg";
  382. return result;
  383. }
  384. [[nodiscard]] ReadResult ReadOther(const ReadArgs &args) {
  385. auto bytes = args.content;
  386. if (bytes.isEmpty()) {
  387. return {};
  388. }
  389. auto buffer = QBuffer(&bytes);
  390. auto reader = QImageReader(&buffer);
  391. reader.setAutoTransform(true);
  392. if (!reader.canRead()) {
  393. return {};
  394. }
  395. const auto size = reader.size();
  396. if (size.width() * size.height() > kReadMaxArea) {
  397. return {};
  398. }
  399. auto result = ReadResult();
  400. result.format = reader.format().toLower();
  401. result.animated = reader.supportsAnimation()
  402. && (reader.imageCount() > 1);
  403. if (!reader.read(&result.image) || result.image.isNull()) {
  404. return {};
  405. }
  406. return result;
  407. }
  408. ReadResult Read(ReadArgs &&args) {
  409. if (args.content.isEmpty()) {
  410. if (args.path.isEmpty()) {
  411. return {};
  412. }
  413. auto file = QFile(args.path);
  414. if (file.size() > kReadBytesLimit
  415. || !file.open(QIODevice::ReadOnly)) {
  416. return {};
  417. }
  418. args.content = file.readAll();
  419. }
  420. auto result = args.gzipSvg ? ReadGzipSvg(args) : ReadOther(args);
  421. if (result.image.isNull()) {
  422. args = ReadArgs();
  423. return {};
  424. }
  425. if (args.returnContent) {
  426. result.content = args.content;
  427. } else {
  428. args.content = QByteArray();
  429. }
  430. if (!args.maxSize.isEmpty()
  431. && (result.image.width() > args.maxSize.width()
  432. || result.image.height() > args.maxSize.height())) {
  433. result.image = result.image.scaled(
  434. args.maxSize,
  435. Qt::KeepAspectRatio,
  436. Qt::SmoothTransformation);
  437. }
  438. if (args.forceOpaque && result.format != qstr("jpeg")) {
  439. result.image = Opaque(std::move(result.image));
  440. }
  441. return result;
  442. }
  443. [[nodiscard]] Options RoundOptions(
  444. ImageRoundRadius radius,
  445. RectParts corners) {
  446. const auto withCorners = [&](Option rounding) {
  447. if (rounding == Option::None) {
  448. return Options();
  449. }
  450. const auto corner = [&](RectPart part, Option skip) {
  451. return !(corners & part) ? skip : Option();
  452. };
  453. return rounding
  454. | corner(RectPart::TopLeft, Option::RoundSkipTopLeft)
  455. | corner(RectPart::TopRight, Option::RoundSkipTopRight)
  456. | corner(RectPart::BottomLeft, Option::RoundSkipBottomLeft)
  457. | corner(RectPart::BottomRight, Option::RoundSkipBottomRight);
  458. };
  459. return withCorners((radius == ImageRoundRadius::Large)
  460. ? Option::RoundLarge
  461. : (radius == ImageRoundRadius::Small)
  462. ? Option::RoundSmall
  463. : (radius == ImageRoundRadius::Ellipse)
  464. ? Option::RoundCircle
  465. : Option::None);
  466. }
  467. QImage Blur(QImage &&image, bool ignoreAlpha) {
  468. if (image.isNull()) {
  469. return std::move(image);
  470. }
  471. const auto ratio = image.devicePixelRatio();
  472. const auto format = image.format();
  473. if (format != QImage::Format_RGB32
  474. && format != QImage::Format_ARGB32_Premultiplied) {
  475. image = std::move(image).convertToFormat(
  476. QImage::Format_ARGB32_Premultiplied);
  477. image.setDevicePixelRatio(ratio);
  478. }
  479. auto pix = image.bits();
  480. if (!pix) {
  481. return std::move(image);
  482. }
  483. const auto w = image.width();
  484. const auto h = image.height();
  485. const auto radius = 3;
  486. const auto r1 = radius + 1;
  487. const auto div = radius * 2 + 1;
  488. const auto stride = w * 4;
  489. if (radius >= 16 || div >= w || div >= h || stride > w * 4) {
  490. return std::move(image);
  491. }
  492. const auto withalpha = !ignoreAlpha && image.hasAlphaChannel();
  493. if (withalpha) {
  494. auto smaller = QImage(image.size(), image.format());
  495. {
  496. QPainter p(&smaller);
  497. PainterHighQualityEnabler hq(p);
  498. p.setCompositionMode(QPainter::CompositionMode_Source);
  499. p.fillRect(0, 0, w, h, Qt::transparent);
  500. p.drawImage(
  501. QRect(radius, radius, w - 2 * radius, h - 2 * radius),
  502. image,
  503. QRect(0, 0, w, h));
  504. }
  505. smaller.setDevicePixelRatio(ratio);
  506. auto was = std::exchange(image, base::take(smaller));
  507. Assert(!image.isNull());
  508. pix = image.bits();
  509. if (!pix) return was;
  510. }
  511. const auto buffer = std::make_unique<uint64[]>(w * h);
  512. const auto rgb = buffer.get();
  513. int x, y, i;
  514. int yw = 0;
  515. const int we = w - r1;
  516. for (y = 0; y < h; y++) {
  517. uint64 cur = BlurGetColors(&pix[yw]);
  518. uint64 rgballsum = -radius * cur;
  519. uint64 rgbsum = cur * ((r1 * (r1 + 1)) >> 1);
  520. for (i = 1; i <= radius; i++) {
  521. uint64 cur = BlurGetColors(&pix[yw + i * 4]);
  522. rgbsum += cur * (r1 - i);
  523. rgballsum += cur;
  524. }
  525. x = 0;
  526. #define update(start, middle, end) \
  527. rgb[y * w + x] = (rgbsum >> 4) & 0x00FF00FF00FF00FFLL; \
  528. rgballsum += BlurGetColors(&pix[yw + (start) * 4]) - 2 * BlurGetColors(&pix[yw + (middle) * 4]) + BlurGetColors(&pix[yw + (end) * 4]); \
  529. rgbsum += rgballsum; \
  530. x++;
  531. while (x < r1) {
  532. update(0, x, x + r1);
  533. }
  534. while (x < we) {
  535. update(x - r1, x, x + r1);
  536. }
  537. while (x < w) {
  538. update(x - r1, x, w - 1);
  539. }
  540. #undef update
  541. yw += stride;
  542. }
  543. const int he = h - r1;
  544. for (x = 0; x < w; x++) {
  545. uint64 rgballsum = -radius * rgb[x];
  546. uint64 rgbsum = rgb[x] * ((r1 * (r1 + 1)) >> 1);
  547. for (i = 1; i <= radius; i++) {
  548. rgbsum += rgb[i * w + x] * (r1 - i);
  549. rgballsum += rgb[i * w + x];
  550. }
  551. y = 0;
  552. int yi = x * 4;
  553. #define update(start, middle, end) \
  554. uint64 res = rgbsum >> 4; \
  555. pix[yi] = res & 0xFF; \
  556. pix[yi + 1] = (res >> 16) & 0xFF; \
  557. pix[yi + 2] = (res >> 32) & 0xFF; \
  558. pix[yi + 3] = (res >> 48) & 0xFF; \
  559. rgballsum += rgb[x + (start) * w] - 2 * rgb[x + (middle) * w] + rgb[x + (end) * w]; \
  560. rgbsum += rgballsum; \
  561. y++; \
  562. yi += stride;
  563. while (y < r1) {
  564. update(0, y, y + r1);
  565. }
  566. while (y < he) {
  567. update(y - r1, y, y + r1);
  568. }
  569. while (y < h) {
  570. update(y - r1, y, h - 1);
  571. }
  572. #undef update
  573. }
  574. return std::move(image);
  575. }
  576. [[nodiscard]] QImage BlurLargeImage(QImage &&image, int radius) {
  577. const auto width = image.width();
  578. const auto height = image.height();
  579. if (width <= radius || height <= radius || radius < 1) {
  580. return std::move(image);
  581. }
  582. if (image.format() != QImage::Format_RGB32
  583. && image.format() != QImage::Format_ARGB32_Premultiplied) {
  584. image = std::move(image).convertToFormat(
  585. QImage::Format_ARGB32_Premultiplied);
  586. }
  587. const auto pixels = image.bits();
  588. const auto width_m1 = width - 1;
  589. const auto height_m1 = height - 1;
  590. const auto widthxheight = width * height;
  591. const auto div = 2 * radius + 1;
  592. const auto radius_p1 = radius + 1;
  593. const auto divsum = radius_p1 * radius_p1;
  594. const auto dvcount = 256 * divsum;
  595. const auto buffers = (div * 3) // stack
  596. + std::max(width, height) // vmin
  597. + widthxheight * 3 // rgb
  598. + dvcount; // dv
  599. auto storage = std::vector<int>(buffers);
  600. auto taken = 0;
  601. const auto take = [&](int size) {
  602. const auto result = gsl::make_span(storage).subspan(taken, size);
  603. taken += size;
  604. return result;
  605. };
  606. // Small buffers
  607. const auto stack = take(div * 3).data();
  608. const auto vmin = take(std::max(width, height)).data();
  609. // Large buffers
  610. const auto rgb = take(widthxheight * 3).data();
  611. const auto dvs = take(dvcount);
  612. auto &&ints = ranges::views::ints;
  613. for (auto &&[value, index] : ranges::views::zip(dvs, ints(0, ranges::unreachable))) {
  614. value = (index / divsum);
  615. }
  616. const auto dv = dvs.data();
  617. // Variables
  618. auto stackpointer = 0;
  619. for (const auto x : ints(0, width)) {
  620. vmin[x] = std::min(x + radius_p1, width_m1);
  621. }
  622. for (const auto y : ints(0, height)) {
  623. auto rinsum = 0;
  624. auto ginsum = 0;
  625. auto binsum = 0;
  626. auto routsum = 0;
  627. auto goutsum = 0;
  628. auto boutsum = 0;
  629. auto rsum = 0;
  630. auto gsum = 0;
  631. auto bsum = 0;
  632. const auto y_width = y * width;
  633. for (const auto i : ints(-radius, radius + 1)) {
  634. const auto sir = &stack[(i + radius) * 3];
  635. const auto x = std::clamp(i, 0, width_m1);
  636. const auto offset = (y_width + x) * 4;
  637. sir[0] = pixels[offset];
  638. sir[1] = pixels[offset + 1];
  639. sir[2] = pixels[offset + 2];
  640. const auto rbs = radius_p1 - std::abs(i);
  641. rsum += sir[0] * rbs;
  642. gsum += sir[1] * rbs;
  643. bsum += sir[2] * rbs;
  644. if (i > 0) {
  645. rinsum += sir[0];
  646. ginsum += sir[1];
  647. binsum += sir[2];
  648. } else {
  649. routsum += sir[0];
  650. goutsum += sir[1];
  651. boutsum += sir[2];
  652. }
  653. }
  654. stackpointer = radius;
  655. for (const auto x : ints(0, width)) {
  656. const auto position = (y_width + x) * 3;
  657. rgb[position] = dv[rsum];
  658. rgb[position + 1] = dv[gsum];
  659. rgb[position + 2] = dv[bsum];
  660. rsum -= routsum;
  661. gsum -= goutsum;
  662. bsum -= boutsum;
  663. const auto stackstart = (stackpointer - radius + div) % div;
  664. const auto sir = &stack[stackstart * 3];
  665. routsum -= sir[0];
  666. goutsum -= sir[1];
  667. boutsum -= sir[2];
  668. const auto offset = (y_width + vmin[x]) * 4;
  669. sir[0] = pixels[offset];
  670. sir[1] = pixels[offset + 1];
  671. sir[2] = pixels[offset + 2];
  672. rinsum += sir[0];
  673. ginsum += sir[1];
  674. binsum += sir[2];
  675. rsum += rinsum;
  676. gsum += ginsum;
  677. bsum += binsum;
  678. {
  679. stackpointer = (stackpointer + 1) % div;
  680. const auto sir = &stack[stackpointer * 3];
  681. routsum += sir[0];
  682. goutsum += sir[1];
  683. boutsum += sir[2];
  684. rinsum -= sir[0];
  685. ginsum -= sir[1];
  686. binsum -= sir[2];
  687. }
  688. }
  689. }
  690. for (const auto y : ints(0, height)) {
  691. vmin[y] = std::min(y + radius_p1, height_m1) * width;
  692. }
  693. for (const auto x : ints(0, width)) {
  694. auto rinsum = 0;
  695. auto ginsum = 0;
  696. auto binsum = 0;
  697. auto routsum = 0;
  698. auto goutsum = 0;
  699. auto boutsum = 0;
  700. auto rsum = 0;
  701. auto gsum = 0;
  702. auto bsum = 0;
  703. for (const auto i : ints(-radius, radius + 1)) {
  704. const auto y = std::clamp(i, 0, height_m1);
  705. const auto position = (y * width + x) * 3;
  706. const auto sir = &stack[(i + radius) * 3];
  707. sir[0] = rgb[position];
  708. sir[1] = rgb[position + 1];
  709. sir[2] = rgb[position + 2];
  710. const auto rbs = radius_p1 - std::abs(i);
  711. rsum += sir[0] * rbs;
  712. gsum += sir[1] * rbs;
  713. bsum += sir[2] * rbs;
  714. if (i > 0) {
  715. rinsum += sir[0];
  716. ginsum += sir[1];
  717. binsum += sir[2];
  718. } else {
  719. routsum += sir[0];
  720. goutsum += sir[1];
  721. boutsum += sir[2];
  722. }
  723. }
  724. stackpointer = radius;
  725. for (const auto y : ints(0, height)) {
  726. const auto offset = (y * width + x) * 4;
  727. pixels[offset] = dv[rsum];
  728. pixels[offset + 1] = dv[gsum];
  729. pixels[offset + 2] = dv[bsum];
  730. rsum -= routsum;
  731. gsum -= goutsum;
  732. bsum -= boutsum;
  733. const auto stackstart = (stackpointer - radius + div) % div;
  734. const auto sir = &stack[stackstart * 3];
  735. routsum -= sir[0];
  736. goutsum -= sir[1];
  737. boutsum -= sir[2];
  738. const auto position = (vmin[y] + x) * 3;
  739. sir[0] = rgb[position];
  740. sir[1] = rgb[position + 1];
  741. sir[2] = rgb[position + 2];
  742. rinsum += sir[0];
  743. ginsum += sir[1];
  744. binsum += sir[2];
  745. rsum += rinsum;
  746. gsum += ginsum;
  747. bsum += binsum;
  748. {
  749. stackpointer = (stackpointer + 1) % div;
  750. const auto sir = &stack[stackpointer * 3];
  751. routsum += sir[0];
  752. goutsum += sir[1];
  753. boutsum += sir[2];
  754. rinsum -= sir[0];
  755. ginsum -= sir[1];
  756. binsum -= sir[2];
  757. }
  758. }
  759. }
  760. return std::move(image);
  761. }
  762. [[nodiscard]] QImage DitherImage(const QImage &image) {
  763. Expects(image.bytesPerLine() == image.width() * 4);
  764. const auto width = image.width();
  765. const auto height = image.height();
  766. const auto min = std::min(width, height);
  767. const auto max = std::max(width, height);
  768. if (max >= 1024 && min >= 512) {
  769. return DitherGeneric<4>(image);
  770. } else if (max >= 512 && min >= 256) {
  771. return DitherGeneric<3>(image);
  772. } else if (max >= 256 && min >= 128) {
  773. return DitherGeneric<2>(image);
  774. } else if (min >= 32) {
  775. return DitherGeneric<1>(image);
  776. }
  777. return image;
  778. }
  779. [[nodiscard]] QImage GenerateGradient(
  780. QSize size,
  781. const std::vector<QColor> &colors,
  782. int rotation,
  783. float progress) {
  784. Expects(!colors.empty());
  785. Expects(colors.size() <= 4);
  786. if (size.isEmpty()) {
  787. return QImage();
  788. } else if (colors.size() > 2) {
  789. return GenerateComplexGradient(size, colors, rotation, progress);
  790. } else {
  791. return GenerateLinearGradient(size, colors, rotation);
  792. }
  793. }
  794. QImage GenerateLinearGradient(
  795. QSize size,
  796. const std::vector<QColor> &colors,
  797. int rotation) {
  798. Expects(!colors.empty());
  799. auto result = QImage(size, QImage::Format_RGB32);
  800. if (colors.size() == 1) {
  801. result.fill(colors.front());
  802. return result;
  803. }
  804. auto p = QPainter(&result);
  805. const auto width = size.width();
  806. const auto height = size.height();
  807. const auto [start, finalStop] = [&]() -> std::pair<QPoint, QPoint> {
  808. const auto type = std::clamp(rotation, 0, 315) / 45;
  809. switch (type) {
  810. case 0: return { { 0, 0 }, { 0, height } };
  811. case 1: return { { width, 0 }, { 0, height } };
  812. case 2: return { { width, 0 }, { 0, 0 } };
  813. case 3: return { { width, height }, { 0, 0 } };
  814. case 4: return { { 0, height }, { 0, 0 } };
  815. case 5: return { { 0, height }, { width, 0 } };
  816. case 6: return { { 0, 0 }, { width, 0 } };
  817. case 7: return { { 0, 0 }, { width, height } };
  818. }
  819. Unexpected("Rotation value in GenerateDitheredGradient.");
  820. }();
  821. auto gradient = QLinearGradient(start, finalStop);
  822. if (colors.size() == 2) {
  823. gradient.setStops(QGradientStops{
  824. { 0.0, colors[0] },
  825. { 1.0, colors[1] }
  826. });
  827. } else {
  828. auto stops = QGradientStops();
  829. const auto step = 1. / (colors.size() - 1);
  830. auto point = 0.;
  831. for (const auto &color : colors) {
  832. stops.append({ point, color });
  833. point += step;
  834. }
  835. gradient.setStops(std::move(stops));
  836. }
  837. p.fillRect(QRect(QPoint(), size), QBrush(std::move(gradient)));
  838. p.end();
  839. return result;
  840. }
  841. QImage GenerateShadow(
  842. int height,
  843. int topAlpha,
  844. int bottomAlpha,
  845. QColor color) {
  846. Expects(topAlpha >= 0 && topAlpha < 256);
  847. Expects(bottomAlpha >= 0 && bottomAlpha < 256);
  848. Expects(height * style::DevicePixelRatio() < 65536);
  849. const auto base = (uint32(color.red()) << 16)
  850. | (uint32(color.green()) << 8)
  851. | uint32(color.blue());
  852. const auto premultiplied = (topAlpha == bottomAlpha) || !base;
  853. auto result = QImage(
  854. QSize(1, height * style::DevicePixelRatio()),
  855. (premultiplied
  856. ? QImage::Format_ARGB32_Premultiplied
  857. : QImage::Format_ARGB32));
  858. if (topAlpha == bottomAlpha) {
  859. color.setAlpha(topAlpha);
  860. result.fill(color);
  861. return result;
  862. }
  863. constexpr auto kShift = 16;
  864. constexpr auto kMultiply = (1U << kShift);
  865. const auto values = std::abs(topAlpha - bottomAlpha);
  866. const auto rows = uint32(result.height());
  867. const auto step = (values * kMultiply) / (rows - 1);
  868. const auto till = rows * uint32(step);
  869. Assert(result.bytesPerLine() == sizeof(uint32));
  870. auto ints = reinterpret_cast<uint32*>(result.bits());
  871. if (topAlpha < bottomAlpha) {
  872. for (auto i = uint32(0); i != till; i += step) {
  873. *ints++ = base | ((topAlpha + (i >> kShift)) << 24);
  874. }
  875. } else {
  876. for (auto i = uint32(0); i != till; i += step) {
  877. *ints++ = base | ((topAlpha - (i >> kShift)) << 24);
  878. }
  879. }
  880. if (!premultiplied) {
  881. result = std::move(result).convertToFormat(
  882. QImage::Format_ARGB32_Premultiplied);
  883. }
  884. return result;
  885. }
  886. QImage Circle(QImage &&image, QRect target) {
  887. Expects(!image.isNull());
  888. if (target.isNull()) {
  889. target = QRect(QPoint( ), image.size());
  890. } else {
  891. Assert(QRect(QPoint(), image.size()).contains(target));
  892. }
  893. image = std::move(image).convertToFormat(
  894. QImage::Format_ARGB32_Premultiplied);
  895. Assert(!image.isNull());
  896. const auto ratio = image.devicePixelRatio();
  897. auto p = QPainter(&image);
  898. p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
  899. p.drawImage(
  900. QRectF(target.topLeft() / ratio, target.size() / ratio),
  901. EllipseMaskCached(target.size()));
  902. p.end();
  903. return std::move(image);
  904. }
  905. QImage Round(
  906. QImage &&image,
  907. CornersMaskRef mask,
  908. QRect target) {
  909. if (target.isNull()) {
  910. target = QRect(QPoint(), image.size());
  911. } else {
  912. Assert(QRect(QPoint(), image.size()).contains(target));
  913. }
  914. const auto targetWidth = target.width();
  915. const auto targetHeight = target.height();
  916. image = std::move(image).convertToFormat(
  917. QImage::Format_ARGB32_Premultiplied);
  918. Assert(!image.isNull());
  919. // We need to detach image first (if it is shared), before we
  920. // count some offsets using QImage::bytesPerLine etc, because
  921. // bytesPerLine may change on detach, this leads to crashes:
  922. // Real image bytesPerLine is smaller than the one we use for offsets.
  923. auto ints = reinterpret_cast<uint32*>(image.bits());
  924. constexpr auto kImageIntsPerPixel = 1;
  925. const auto imageIntsPerLine = (image.bytesPerLine() >> 2);
  926. Assert(image.depth() == ((kImageIntsPerPixel * sizeof(uint32)) << 3));
  927. Assert(image.bytesPerLine() == (imageIntsPerLine << 2));
  928. const auto maskCorner = [&](
  929. const QImage *mask,
  930. bool right = false,
  931. bool bottom = false) {
  932. const auto maskWidth = mask ? mask->width() : 0;
  933. const auto maskHeight = mask ? mask->height() : 0;
  934. if (!maskWidth
  935. || !maskHeight
  936. || targetWidth < maskWidth
  937. || targetHeight < maskHeight) {
  938. return;
  939. }
  940. const auto maskBytesPerPixel = (mask->depth() >> 3);
  941. const auto maskBytesPerLine = mask->bytesPerLine();
  942. const auto maskBytesAdded = maskBytesPerLine
  943. - maskWidth * maskBytesPerPixel;
  944. Assert(maskBytesAdded >= 0);
  945. Assert(mask->depth() == (maskBytesPerPixel << 3));
  946. const auto imageIntsAdded = imageIntsPerLine
  947. - maskWidth * kImageIntsPerPixel;
  948. Assert(imageIntsAdded >= 0);
  949. auto imageInts = ints + target.x() + target.y() * imageIntsPerLine;
  950. if (right) {
  951. imageInts += targetWidth - maskWidth;
  952. }
  953. if (bottom) {
  954. imageInts += (targetHeight - maskHeight) * imageIntsPerLine;
  955. }
  956. auto maskBytes = mask->constBits();
  957. for (auto y = 0; y != maskHeight; ++y) {
  958. for (auto x = 0; x != maskWidth; ++x) {
  959. auto opacity = static_cast<anim::ShiftedMultiplier>(*maskBytes) + 1;
  960. *imageInts = anim::unshifted(anim::shifted(*imageInts) * opacity);
  961. maskBytes += maskBytesPerPixel;
  962. imageInts += kImageIntsPerPixel;
  963. }
  964. maskBytes += maskBytesAdded;
  965. imageInts += imageIntsAdded;
  966. }
  967. };
  968. maskCorner(mask.p[0]);
  969. maskCorner(mask.p[1], true);
  970. maskCorner(mask.p[2], false, true);
  971. maskCorner(mask.p[3], true, true);
  972. return std::move(image);
  973. }
  974. QImage Round(
  975. QImage &&image,
  976. gsl::span<const QImage, 4> cornerMasks,
  977. RectParts corners,
  978. QRect target) {
  979. return Round(std::move(image), CornersMaskRef({
  980. (corners & RectPart::TopLeft) ? &cornerMasks[0] : nullptr,
  981. (corners & RectPart::TopRight) ? &cornerMasks[1] : nullptr,
  982. (corners & RectPart::BottomLeft) ? &cornerMasks[2] : nullptr,
  983. (corners & RectPart::BottomRight) ? &cornerMasks[3] : nullptr,
  984. }), target);
  985. }
  986. QImage Round(
  987. QImage &&image,
  988. ImageRoundRadius radius,
  989. RectParts corners,
  990. QRect target) {
  991. if (!static_cast<int>(corners)) {
  992. return std::move(image);
  993. } else if (radius == ImageRoundRadius::Ellipse) {
  994. Assert((corners & RectPart::AllCorners) == RectPart::AllCorners);
  995. return Circle(std::move(image), target);
  996. }
  997. Assert(!image.isNull());
  998. const auto masks = CornersMask(radius);
  999. return Round(std::move(image), masks, corners, target);
  1000. }
  1001. QImage Round(QImage &&image, Options options, QRect target) {
  1002. if (options & Option::RoundCircle) {
  1003. return Circle(std::move(image), target);
  1004. } else if (!(options & (Option::RoundLarge | Option::RoundSmall))) {
  1005. return std::move(image);
  1006. }
  1007. const auto corner = [&](Option skip, RectPart part) {
  1008. return !(options & skip) ? part : RectPart::None;
  1009. };
  1010. return Round(
  1011. std::move(image),
  1012. ((options & Option::RoundLarge)
  1013. ? ImageRoundRadius::Large
  1014. : ImageRoundRadius::Small),
  1015. (corner(Option::RoundSkipTopLeft, RectPart::TopLeft)
  1016. | corner(Option::RoundSkipTopRight, RectPart::TopRight)
  1017. | corner(Option::RoundSkipBottomLeft, RectPart::BottomLeft)
  1018. | corner(Option::RoundSkipBottomRight, RectPart::BottomRight)),
  1019. target);
  1020. }
  1021. QImage Colored(QImage &&image, style::color add) {
  1022. return Colored(std::move(image), add->c);
  1023. }
  1024. QImage Colored(QImage &&image, QColor add) {
  1025. const auto format = image.format();
  1026. if (format != QImage::Format_RGB32
  1027. && format != QImage::Format_ARGB32_Premultiplied) {
  1028. image = std::move(image).convertToFormat(
  1029. QImage::Format_ARGB32_Premultiplied);
  1030. }
  1031. if (const auto pix = image.bits()) {
  1032. const auto ca = add.alpha();
  1033. const auto cr = add.red() * (ca + 1);
  1034. const auto cg = add.green() * (ca + 1);
  1035. const auto cb = add.blue() * (ca + 1);
  1036. const auto ra = (0x100 - ca) * 0x100;
  1037. const auto w = image.width();
  1038. const auto h = image.height();
  1039. const auto add = image.bytesPerLine() - (w * 4);
  1040. auto i = index_type();
  1041. for (auto y = 0; y != h; ++y) {
  1042. for (auto to = i + (w * 4); i != to; i += 4) {
  1043. const auto a = pix[i + 3] + 1;
  1044. pix[i + 0] = (ra * pix[i + 0] + a * cb) >> 16;
  1045. pix[i + 1] = (ra * pix[i + 1] + a * cg) >> 16;
  1046. pix[i + 2] = (ra * pix[i + 2] + a * cr) >> 16;
  1047. }
  1048. i += add;
  1049. }
  1050. }
  1051. return std::move(image);
  1052. }
  1053. QImage Opaque(QImage &&image) {
  1054. if (image.hasAlphaChannel()) {
  1055. image = std::move(image).convertToFormat(
  1056. QImage::Format_ARGB32_Premultiplied);
  1057. auto ints = reinterpret_cast<uint32*>(image.bits());
  1058. const auto bg = anim::shifted(QColor(Qt::white));
  1059. const auto width = image.width();
  1060. const auto height = image.height();
  1061. const auto addPerLine = (image.bytesPerLine() / sizeof(uint32)) - width;
  1062. for (auto y = 0; y != height; ++y) {
  1063. for (auto x = 0; x != width; ++x) {
  1064. const auto components = anim::shifted(*ints);
  1065. *ints++ = anim::unshifted(components * 256
  1066. + bg * (256 - anim::getAlpha(components)));
  1067. }
  1068. ints += addPerLine;
  1069. }
  1070. }
  1071. return std::move(image);
  1072. }
  1073. QImage Prepare(QImage image, int w, int h, const PrepareArgs &args) {
  1074. Expects(!image.isNull());
  1075. if (args.options & Option::Blur) {
  1076. image = Blur(std::move(image));
  1077. Assert(!image.isNull());
  1078. }
  1079. if (w <= 0
  1080. || (w == image.width() && (h <= 0 || h == image.height()))) {
  1081. } else if (h <= 0) {
  1082. image = image.scaledToWidth(
  1083. w,
  1084. ((args.options & Images::Option::FastTransform)
  1085. ? Qt::FastTransformation
  1086. : Qt::SmoothTransformation));
  1087. Assert(!image.isNull());
  1088. } else {
  1089. image = image.scaled(
  1090. w,
  1091. h,
  1092. Qt::IgnoreAspectRatio,
  1093. ((args.options & Images::Option::FastTransform)
  1094. ? Qt::FastTransformation
  1095. : Qt::SmoothTransformation));
  1096. Assert(!image.isNull());
  1097. }
  1098. auto outer = args.outer;
  1099. if (!outer.isEmpty()) {
  1100. const auto ratio = style::DevicePixelRatio();
  1101. outer *= ratio;
  1102. if (outer != QSize(w, h)) {
  1103. image.setDevicePixelRatio(ratio);
  1104. auto result = QImage(outer, QImage::Format_ARGB32_Premultiplied);
  1105. result.setDevicePixelRatio(ratio);
  1106. if (args.options & Images::Option::TransparentBackground) {
  1107. result.fill(Qt::transparent);
  1108. }
  1109. {
  1110. QPainter p(&result);
  1111. if (!(args.options & Images::Option::TransparentBackground)) {
  1112. if (w < outer.width() || h < outer.height()) {
  1113. p.fillRect(
  1114. QRect({}, result.size() / ratio),
  1115. Qt::black);
  1116. }
  1117. }
  1118. p.drawImage(
  1119. (result.width() - image.width()) / (2 * ratio),
  1120. (result.height() - image.height()) / (2 * ratio),
  1121. image);
  1122. }
  1123. image = std::move(result);
  1124. Assert(!image.isNull());
  1125. }
  1126. }
  1127. if (args.options
  1128. & (Option::RoundCircle | Option::RoundLarge | Option::RoundSmall)) {
  1129. image = Round(std::move(image), args.options);
  1130. Assert(!image.isNull());
  1131. }
  1132. if (args.colored) {
  1133. image = Colored(std::move(image), *args.colored);
  1134. }
  1135. image.setDevicePixelRatio(style::DevicePixelRatio());
  1136. return image;
  1137. }
  1138. bool IsProgressiveJpeg(const QByteArray &bytes) {
  1139. struct jpeg_decompress_struct info;
  1140. struct my_error_mgr jerr;
  1141. info.err = jpeg_std_error(&jerr);
  1142. jerr.error_exit = my_error_exit;
  1143. if (setjmp(jerr.setjmp_buffer)) {
  1144. jpeg_destroy_decompress(&info);
  1145. return false;
  1146. }
  1147. jpeg_create_decompress(&info);
  1148. jpeg_mem_src(
  1149. &info,
  1150. reinterpret_cast<const unsigned char*>(bytes.data()),
  1151. bytes.size());
  1152. if (jpeg_read_header(&info, TRUE) != 1) {
  1153. jpeg_destroy_decompress(&info);
  1154. return false;
  1155. }
  1156. jpeg_destroy_decompress(&info);
  1157. return (info.progressive_mode > 0);
  1158. }
  1159. QByteArray MakeProgressiveJpeg(const QByteArray &bytes) {
  1160. struct jpeg_decompress_struct srcinfo;
  1161. struct jpeg_compress_struct dstinfo;
  1162. struct my_error_mgr jerr;
  1163. unsigned char* outbuffer = nullptr;
  1164. long unsigned int outsize = 0;
  1165. srcinfo.err = jpeg_std_error(&jerr);
  1166. dstinfo.err = jpeg_std_error(&jerr);
  1167. jerr.error_exit = my_error_exit;
  1168. if (setjmp(jerr.setjmp_buffer)) {
  1169. free(outbuffer);
  1170. jpeg_abort_compress(&dstinfo);
  1171. jpeg_abort_decompress(&srcinfo);
  1172. jpeg_destroy_compress(&dstinfo);
  1173. jpeg_destroy_decompress(&srcinfo);
  1174. return {};
  1175. }
  1176. jpeg_create_decompress(&srcinfo);
  1177. jpeg_create_compress(&dstinfo);
  1178. jpeg_mem_src(
  1179. &srcinfo,
  1180. reinterpret_cast<const unsigned char*>(bytes.data()),
  1181. bytes.size());
  1182. jpeg_save_markers(&srcinfo, JPEG_COM, 0xFFFF);
  1183. for (int m = 0; m < 16; m++) {
  1184. jpeg_save_markers(&srcinfo, JPEG_APP0 + m, 0xFFFF);
  1185. }
  1186. jpeg_read_header(&srcinfo, true);
  1187. const auto coefArrays = jpeg_read_coefficients(&srcinfo);
  1188. jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
  1189. jpeg_simple_progression(&dstinfo);
  1190. jpeg_mem_dest(&dstinfo, &outbuffer, &outsize);
  1191. jpeg_write_coefficients(&dstinfo, coefArrays);
  1192. for (jpeg_saved_marker_ptr marker = srcinfo.marker_list
  1193. ; marker != nullptr
  1194. ; marker = marker->next) {
  1195. if (dstinfo.write_JFIF_header
  1196. && marker->marker == JPEG_APP0
  1197. && marker->data_length >= 5
  1198. && marker->data[0] == 0x4A
  1199. && marker->data[1] == 0x46
  1200. && marker->data[2] == 0x49
  1201. && marker->data[3] == 0x46
  1202. && marker->data[4] == 0)
  1203. continue; // reject duplicate JFIF
  1204. if (dstinfo.write_Adobe_marker
  1205. && marker->marker == JPEG_APP0 + 14
  1206. && marker->data_length >= 5
  1207. && marker->data[0] == 0x41
  1208. && marker->data[1] == 0x64
  1209. && marker->data[2] == 0x6F
  1210. && marker->data[3] == 0x62
  1211. && marker->data[4] == 0x65)
  1212. continue; // reject duplicate Adobe
  1213. jpeg_write_marker(
  1214. &dstinfo,
  1215. marker->marker,
  1216. marker->data,
  1217. marker->data_length);
  1218. }
  1219. jpeg_finish_compress(&dstinfo);
  1220. jpeg_finish_decompress(&srcinfo);
  1221. jpeg_destroy_compress(&dstinfo);
  1222. jpeg_destroy_decompress(&srcinfo);
  1223. const auto outbufferGuard = gsl::finally([&] {
  1224. free(outbuffer);
  1225. });
  1226. return QByteArray(reinterpret_cast<char*>(outbuffer), outsize);
  1227. }
  1228. QByteArray ExpandInlineBytes(const QByteArray &bytes) {
  1229. if (bytes.size() < 3 || bytes[0] != '\x01') {
  1230. return QByteArray();
  1231. }
  1232. const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
  1233. "\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
  1234. "\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
  1235. "\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
  1236. "\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
  1237. "\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
  1238. "\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
  1239. "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
  1240. "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
  1241. "\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
  1242. "\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
  1243. "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
  1244. "\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
  1245. "\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
  1246. "\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
  1247. "\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
  1248. "\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
  1249. "\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
  1250. "\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
  1251. "\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
  1252. "\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
  1253. "\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
  1254. "\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
  1255. "\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
  1256. "\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
  1257. "\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
  1258. "\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
  1259. "\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
  1260. "\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
  1261. "\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
  1262. "\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
  1263. "\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
  1264. "\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
  1265. "\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
  1266. "\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
  1267. "\x3f\x00";
  1268. const char footer[] = "\xff\xd9";
  1269. auto real = QByteArray(header, sizeof(header) - 1);
  1270. real[164] = bytes[1];
  1271. real[166] = bytes[2];
  1272. return real
  1273. + bytes.mid(3)
  1274. + QByteArray::fromRawData(footer, sizeof(footer) - 1);
  1275. }
  1276. QImage FromInlineBytes(const QByteArray &bytes) {
  1277. return Read({ .content = ExpandInlineBytes(bytes) }).image;
  1278. }
  1279. // Thanks TDLib for code.
  1280. QByteArray ExpandPathInlineBytes(const QByteArray &bytes) {
  1281. auto result = QByteArray();
  1282. result.reserve(3 * (bytes.size() + 1));
  1283. result.append('M');
  1284. for (unsigned char c : bytes) {
  1285. if (c >= 128 + 64) {
  1286. result.append("AACAAAAHAAALMAAAQASTAVAAAZ"
  1287. "aacaaaahaaalmaaaqastava.az0123456789-,"[c - 128 - 64]);
  1288. } else {
  1289. if (c >= 128) {
  1290. result.append(',');
  1291. } else if (c >= 64) {
  1292. result.append('-');
  1293. }
  1294. //char buffer[3] = { 0 }; // Unavailable on macOS < 10.15.
  1295. //std::to_chars(buffer, buffer + 3, (c & 63));
  1296. //result.append(buffer);
  1297. result.append(QByteArray::number(c & 63));
  1298. }
  1299. }
  1300. result.append('z');
  1301. return result;
  1302. }
  1303. QPainterPath PathFromInlineBytes(const QByteArray &bytes) {
  1304. if (bytes.isEmpty()) {
  1305. return QPainterPath();
  1306. }
  1307. const auto expanded = ExpandPathInlineBytes(bytes);
  1308. const auto path = expanded.data(); // Allows checking for '\0' by index.
  1309. auto position = 0;
  1310. const auto isAlpha = [](char c) {
  1311. c |= 0x20;
  1312. return 'a' <= c && c <= 'z';
  1313. };
  1314. const auto isDigit = [](char c) {
  1315. return '0' <= c && c <= '9';
  1316. };
  1317. const auto skipCommas = [&] {
  1318. while (path[position] == ',') {
  1319. ++position;
  1320. }
  1321. };
  1322. const auto getNumber = [&] {
  1323. skipCommas();
  1324. auto sign = 1;
  1325. if (path[position] == '-') {
  1326. sign = -1;
  1327. ++position;
  1328. }
  1329. double res = 0;
  1330. while (isDigit(path[position])) {
  1331. res = res * 10 + path[position++] - '0';
  1332. }
  1333. if (path[position] == '.') {
  1334. ++position;
  1335. double mul = 0.1;
  1336. while (isDigit(path[position])) {
  1337. res += (path[position] - '0') * mul;
  1338. mul *= 0.1;
  1339. ++position;
  1340. }
  1341. }
  1342. return sign * res;
  1343. };
  1344. auto result = QPainterPath();
  1345. auto x = 0.;
  1346. auto y = 0.;
  1347. while (path[position] != '\0') {
  1348. skipCommas();
  1349. if (path[position] == '\0') {
  1350. break;
  1351. }
  1352. while (path[position] == 'm' || path[position] == 'M') {
  1353. auto command = path[position++];
  1354. do {
  1355. if (command == 'm') {
  1356. x += getNumber();
  1357. y += getNumber();
  1358. } else {
  1359. x = getNumber();
  1360. y = getNumber();
  1361. }
  1362. skipCommas();
  1363. } while (path[position] != '\0' && !isAlpha(path[position]));
  1364. }
  1365. auto xStart = x;
  1366. auto yStart = y;
  1367. result.moveTo(xStart, yStart);
  1368. auto haveLastEndControlPoint = false;
  1369. auto xLastEndControlPoint = 0.;
  1370. auto yLastEndControlPoint = 0.;
  1371. auto isClosed = false;
  1372. auto command = '-';
  1373. while (!isClosed) {
  1374. skipCommas();
  1375. if (path[position] == '\0') {
  1376. LOG(("SVG Error: Receive unclosed path: %1"
  1377. ).arg(QString::fromLatin1(path)));
  1378. return QPainterPath();
  1379. }
  1380. if (isAlpha(path[position])) {
  1381. command = path[position++];
  1382. }
  1383. switch (command) {
  1384. case 'l':
  1385. case 'L':
  1386. case 'h':
  1387. case 'H':
  1388. case 'v':
  1389. case 'V':
  1390. if (command == 'l' || command == 'h') {
  1391. x += getNumber();
  1392. } else if (command == 'L' || command == 'H') {
  1393. x = getNumber();
  1394. }
  1395. if (command == 'l' || command == 'v') {
  1396. y += getNumber();
  1397. } else if (command == 'L' || command == 'V') {
  1398. y = getNumber();
  1399. }
  1400. result.lineTo(x, y);
  1401. haveLastEndControlPoint = false;
  1402. break;
  1403. case 'C':
  1404. case 'c':
  1405. case 'S':
  1406. case 's': {
  1407. auto xStartControlPoint = 0.;
  1408. auto yStartControlPoint = 0.;
  1409. if (command == 'S' || command == 's') {
  1410. if (haveLastEndControlPoint) {
  1411. xStartControlPoint = 2 * x - xLastEndControlPoint;
  1412. yStartControlPoint = 2 * y - yLastEndControlPoint;
  1413. } else {
  1414. xStartControlPoint = x;
  1415. yStartControlPoint = y;
  1416. }
  1417. } else {
  1418. xStartControlPoint = getNumber();
  1419. yStartControlPoint = getNumber();
  1420. if (command == 'c') {
  1421. xStartControlPoint += x;
  1422. yStartControlPoint += y;
  1423. }
  1424. }
  1425. xLastEndControlPoint = getNumber();
  1426. yLastEndControlPoint = getNumber();
  1427. if (command == 'c' || command == 's') {
  1428. xLastEndControlPoint += x;
  1429. yLastEndControlPoint += y;
  1430. }
  1431. haveLastEndControlPoint = true;
  1432. if (command == 'c' || command == 's') {
  1433. x += getNumber();
  1434. y += getNumber();
  1435. } else {
  1436. x = getNumber();
  1437. y = getNumber();
  1438. }
  1439. result.cubicTo(
  1440. xStartControlPoint,
  1441. yStartControlPoint,
  1442. xLastEndControlPoint,
  1443. yLastEndControlPoint,
  1444. x,
  1445. y);
  1446. break;
  1447. }
  1448. case 'm':
  1449. case 'M':
  1450. --position;
  1451. [[fallthrough]];
  1452. case 'z':
  1453. case 'Z':
  1454. if (x != xStart || y != yStart) {
  1455. x = xStart;
  1456. y = yStart;
  1457. result.lineTo(x, y);
  1458. }
  1459. isClosed = true;
  1460. break;
  1461. default:
  1462. LOG(("SVG Error: Receive invalid command %1 at pos %2: %3"
  1463. ).arg(command
  1464. ).arg(position
  1465. ).arg(QString::fromLatin1(path)));
  1466. return QPainterPath();
  1467. }
  1468. }
  1469. }
  1470. return result;
  1471. }
  1472. } // namespace Images