qt_shim.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/qt/qt_shim.h"
  5. #include <stdio.h>
  6. #include <QApplication>
  7. #include <QFont>
  8. #include <QIcon>
  9. #include <QMimeDatabase>
  10. #include <QMimeType>
  11. #include <QPainter>
  12. #include <QPalette>
  13. #include <QStyle>
  14. #include <QStyleOptionTitleBar>
  15. namespace qt {
  16. namespace {
  17. bool IsStyleItalic(QFont::Style style) {
  18. switch (style) {
  19. case QFont::Style::StyleNormal:
  20. return false;
  21. case QFont::Style::StyleItalic:
  22. // gfx::Font::FontStyle doesn't support oblique, so treat it as italic.
  23. case QFont::Style::StyleOblique:
  24. return true;
  25. }
  26. }
  27. FontHinting QtHintingToFontHinting(QFont::HintingPreference hinting) {
  28. switch (hinting) {
  29. case QFont::PreferDefaultHinting:
  30. return FontHinting::kDefault;
  31. case QFont::PreferNoHinting:
  32. return FontHinting::kNone;
  33. case QFont::PreferVerticalHinting:
  34. // QT treats vertical hinting as "light" for Freetype:
  35. // https://doc.qt.io/qt-5/qfont.html#HintingPreference-enum
  36. return FontHinting::kLight;
  37. case QFont::PreferFullHinting:
  38. return FontHinting::kFull;
  39. }
  40. }
  41. // Obtain the average color of a gradient.
  42. SkColor GradientColor(const QGradient& gradient) {
  43. QGradientStops stops = gradient.stops();
  44. if (stops.empty())
  45. return QColorConstants::Transparent.rgba();
  46. float a = 0;
  47. float r = 0;
  48. float g = 0;
  49. float b = 0;
  50. for (int i = 0; i < stops.size(); i++) {
  51. // Determine the extents of this stop. The whole gradient interval is [0,
  52. // 1], so extend to the endpoint if this is the first or last stop.
  53. float left_interval =
  54. i == 0 ? stops[i].first : (stops[i].first - stops[i - 1].first) / 2;
  55. float right_interval = i == stops.size() - 1
  56. ? 1 - stops[i].first
  57. : (stops[i + 1].first - stops[i].first) / 2;
  58. float length = left_interval + right_interval;
  59. // alpha() returns a value in [0, 255] and alphaF() returns a value in
  60. // [0, 1]. The color values are not premultiplied so the RGB channels need
  61. // to be multiplied by the alpha (in range [0, 1]) before summing. The
  62. // alpha doesn't need to be multiplied, so we just sum color.alpha() in
  63. // range [0, 255] directly.
  64. const QColor& color = stops[i].second;
  65. a += color.alpha() * length;
  66. r += color.alphaF() * color.red() * length;
  67. g += color.alphaF() * color.green() * length;
  68. b += color.alphaF() * color.blue() * length;
  69. }
  70. return qRgba(r, g, b, a);
  71. }
  72. // Obtain the average color of a texture.
  73. SkColor TextureColor(QImage image) {
  74. size_t size = image.width() * image.height();
  75. if (!size)
  76. return QColorConstants::Transparent.rgba();
  77. if (image.format() != QImage::Format_ARGB32_Premultiplied)
  78. image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
  79. size_t a = 0;
  80. size_t r = 0;
  81. size_t g = 0;
  82. size_t b = 0;
  83. const auto* pixels = reinterpret_cast<QRgb*>(image.bits());
  84. for (size_t i = 0; i < size; i++) {
  85. auto color = QColor::fromRgba(pixels[i]);
  86. a += color.alpha();
  87. r += color.red();
  88. g += color.green();
  89. b += color.blue();
  90. }
  91. return qRgba(r / size, g / size, b / size, a / size);
  92. }
  93. SkColor BrushColor(const QBrush& brush) {
  94. QColor color = brush.color();
  95. auto alpha_blend = [&](uint8_t alpha) {
  96. QColor blended = color;
  97. blended.setAlpha(blended.alpha() * alpha / 255);
  98. return blended.rgba();
  99. };
  100. switch (brush.style()) {
  101. case Qt::SolidPattern:
  102. return alpha_blend(0xFF);
  103. case Qt::Dense1Pattern:
  104. return alpha_blend(0xE0);
  105. case Qt::Dense2Pattern:
  106. return alpha_blend(0xC0);
  107. case Qt::Dense3Pattern:
  108. return alpha_blend(0xA0);
  109. case Qt::Dense4Pattern:
  110. return alpha_blend(0x80);
  111. case Qt::Dense5Pattern:
  112. return alpha_blend(0x60);
  113. case Qt::Dense6Pattern:
  114. return alpha_blend(0x40);
  115. case Qt::Dense7Pattern:
  116. return alpha_blend(0x20);
  117. case Qt::NoBrush:
  118. return alpha_blend(0x00);
  119. case Qt::HorPattern:
  120. case Qt::VerPattern:
  121. return alpha_blend(0x20);
  122. case Qt::CrossPattern:
  123. return alpha_blend(0x40);
  124. case Qt::BDiagPattern:
  125. case Qt::FDiagPattern:
  126. return alpha_blend(0x20);
  127. case Qt::DiagCrossPattern:
  128. return alpha_blend(0x40);
  129. case Qt::LinearGradientPattern:
  130. case Qt::RadialGradientPattern:
  131. case Qt::ConicalGradientPattern:
  132. return GradientColor(*brush.gradient());
  133. case Qt::TexturePattern:
  134. return TextureColor(brush.textureImage());
  135. }
  136. }
  137. QPalette::ColorRole ColorTypeToColorRole(ColorType type) {
  138. switch (type) {
  139. case ColorType::kWindowBg:
  140. return QPalette::Window;
  141. case ColorType::kWindowFg:
  142. return QPalette::WindowText;
  143. case ColorType::kHighlightBg:
  144. return QPalette::Highlight;
  145. case ColorType::kHighlightFg:
  146. return QPalette::HighlightedText;
  147. case ColorType::kEntryBg:
  148. return QPalette::Base;
  149. case ColorType::kEntryFg:
  150. return QPalette::Text;
  151. case ColorType::kButtonBg:
  152. return QPalette::Button;
  153. case ColorType::kButtonFg:
  154. return QPalette::ButtonText;
  155. case ColorType::kLight:
  156. return QPalette::Light;
  157. case ColorType::kMidlight:
  158. return QPalette::Midlight;
  159. case ColorType::kMidground:
  160. return QPalette::Mid;
  161. case ColorType::kDark:
  162. return QPalette::Dark;
  163. case ColorType::kShadow:
  164. return QPalette::Shadow;
  165. }
  166. }
  167. QPalette::ColorGroup ColorStateToColorGroup(ColorState state) {
  168. switch (state) {
  169. case ColorState::kNormal:
  170. return QPalette::Normal;
  171. case ColorState::kDisabled:
  172. return QPalette::Disabled;
  173. case ColorState::kInactive:
  174. return QPalette::Inactive;
  175. }
  176. }
  177. } // namespace
  178. QtShim::QtShim(QtInterface::Delegate* delegate, int* argc, char** argv)
  179. : delegate_(delegate), app_(*argc, argv) {
  180. connect(&app_, SIGNAL(fontChanged(const QFont&)), this,
  181. SLOT(FontChanged(const QFont&)));
  182. connect(&app_, SIGNAL(paletteChanged(const QPalette&)), this,
  183. SLOT(PaletteChanged(const QPalette&)));
  184. }
  185. QtShim::~QtShim() = default;
  186. double QtShim::GetScaleFactor() const {
  187. return app_.devicePixelRatio();
  188. }
  189. FontRenderParams QtShim::GetFontRenderParams() const {
  190. QFont font = app_.font();
  191. auto style = font.styleStrategy();
  192. return {
  193. .antialiasing = !(style & QFont::StyleStrategy::NoAntialias),
  194. .use_bitmaps = !!(style & QFont::StyleStrategy::PreferBitmap),
  195. .hinting = QtHintingToFontHinting(font.hintingPreference()),
  196. };
  197. }
  198. FontDescription QtShim::GetFontDescription() const {
  199. QFont font = app_.font();
  200. return {
  201. .family = String(font.family().toStdString().c_str()),
  202. .size_pixels = font.pixelSize(),
  203. .size_points = font.pointSize(),
  204. .is_italic = IsStyleItalic(font.style()),
  205. .weight = font.weight(),
  206. };
  207. }
  208. Image QtShim::GetIconForContentType(const String& content_type,
  209. int size) const {
  210. QMimeDatabase db;
  211. for (const char* mime : {content_type.c_str(), "application/octet-stream"}) {
  212. auto mt = db.mimeTypeForName(mime);
  213. for (const auto& name : {mt.iconName(), mt.genericIconName()}) {
  214. auto icon = QIcon::fromTheme(name);
  215. auto pixmap = icon.pixmap(size);
  216. auto image = pixmap.toImage();
  217. if (image.format() != QImage::Format_ARGB32_Premultiplied)
  218. image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
  219. if (auto bytes = image.sizeInBytes()) {
  220. return {image.width(), image.height(),
  221. static_cast<float>(image.devicePixelRatio()),
  222. Buffer(image.bits(), bytes)};
  223. }
  224. }
  225. }
  226. return {};
  227. }
  228. SkColor QtShim::GetColor(ColorType role, ColorState state) const {
  229. return BrushColor(app_.palette().brush(ColorStateToColorGroup(state),
  230. ColorTypeToColorRole(role)));
  231. }
  232. SkColor QtShim::GetFrameColor(ColorState state, bool use_custom_frame) const {
  233. constexpr int kSampleSize = 32;
  234. return TextureColor(DrawHeaderImpl(kSampleSize, kSampleSize,
  235. GetColor(ColorType::kWindowBg, state),
  236. state, use_custom_frame));
  237. }
  238. int QtShim::GetCursorBlinkIntervalMs() const {
  239. return app_.cursorFlashTime();
  240. }
  241. int QtShim::GetAnimationDurationMs() const {
  242. return app_.style()->styleHint(QStyle::SH_Widget_Animation_Duration);
  243. }
  244. void QtShim::FontChanged(const QFont& font) {
  245. delegate_->FontChanged();
  246. }
  247. void QtShim::PaletteChanged(const QPalette& palette) {
  248. delegate_->ThemeChanged();
  249. }
  250. Image QtShim::DrawHeader(int width,
  251. int height,
  252. SkColor default_color,
  253. ColorState state,
  254. bool use_custom_frame) const {
  255. QImage image =
  256. DrawHeaderImpl(width, height, default_color, state, use_custom_frame);
  257. return {width, height, 1.0f, Buffer(image.bits(), image.sizeInBytes())};
  258. }
  259. QImage QtShim::DrawHeaderImpl(int width,
  260. int height,
  261. SkColor default_color,
  262. ColorState state,
  263. bool use_custom_frame) const {
  264. QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
  265. image.fill(default_color);
  266. QPainter painter(&image);
  267. if (use_custom_frame) {
  268. // Chrome renders it's own window border, so clip the border out by
  269. // rendering the titlebar larger than the image.
  270. constexpr int kBorderWidth = 5;
  271. QStyleOptionTitleBar opt;
  272. opt.rect = QRect(-kBorderWidth, -kBorderWidth, width + 2 * kBorderWidth,
  273. height + 2 * kBorderWidth);
  274. if (state == ColorState::kNormal)
  275. opt.titleBarState = QStyle::State_Active;
  276. app_.style()->drawComplexControl(QStyle::CC_TitleBar, &opt, &painter,
  277. nullptr);
  278. } else {
  279. painter.fillRect(
  280. 0, 0, width, height,
  281. app_.palette().brush(ColorStateToColorGroup(state), QPalette::Window));
  282. }
  283. return image;
  284. }
  285. } // namespace qt
  286. qt::QtInterface* CreateQtInterface(qt::QtInterface::Delegate* delegate,
  287. int* argc,
  288. char** argv) {
  289. return new qt::QtShim(delegate, argc, argv);
  290. }