Compare commits

..

200 Commits

Author SHA1 Message Date
Emmanuele Bassi
24b69a9a10 examples: Update to bind settings inside UI definitions 2021-09-09 19:22:48 +01:00
Emmanuele Bassi
043eea6ae3 docs: Document the settings binding in GtkBuilder UI files
And clean up the structure while we're at it.
2021-09-09 19:22:06 +01:00
Emmanuele Bassi
181fce8342 Support binding properties to GSettings keys in GtkBuilder
We already support binding properties; by adding new attributes to the
`property` element we can automatically call g_settings_bind() on the
object ourselves.
2021-09-09 19:12:40 +01:00
Boyuan Yang
db3858a204 Update Chinese (China) translation 2021-09-05 20:33:21 +00:00
Daniel Șerbănescu
07bff74544 Update Romanian translation
(cherry picked from commit bb72acf9cd)
2021-09-05 18:43:44 +00:00
Daniel Șerbănescu
b4262883a8 Update Romanian translation
(cherry picked from commit 63d829bbc1)
2021-09-05 18:34:37 +00:00
Matthias Clasen
563c85e222 Merge branch 'matthiasc/for-master' into 'master'
inspector: Cosmetics

See merge request GNOME/gtk!3925
2021-09-05 17:26:13 +00:00
Matthias Clasen
d75246a543 inspector: Cosmetics
Make some settings entries smaller, and make the
scales match in size. Also, inline adjustments
in the ui file, since we can do that now.
2021-09-05 12:59:11 -04:00
Matthias Clasen
b3856a3ea0 Merge branch 'matthiasc/for-master' into 'master'
Avoid hand-rolled color glyph information

See merge request GNOME/gtk!3924
2021-09-05 16:12:34 +00:00
Matthias Clasen
bd4d24f8ed Bump pango req to 1.49.1
We are using pango api that was introduced in 1.49.1
without ifdefs, so we should require it.
2021-09-05 10:58:50 -04:00
Matthias Clasen
65ce3eb207 Avoid hand-rolled color glyph information
Followup to b244f31337. Pango provides color glyph
information for us now, so we don't need to steal a
bit anymore.
2021-09-05 10:58:50 -04:00
Anders Jonsson
865ac44963 Update Swedish translation
(cherry picked from commit 19af960163)
2021-09-05 14:11:51 +00:00
Yaron Shahrabani
55a935db53 Update Hebrew translation 2021-09-05 13:45:49 +00:00
Matthias Clasen
6fefae3dbb Merge branch 'better-font-rendering-demo' into 'master'
gtk-demo: Improve the font rendering demo

See merge request GNOME/gtk!3923
2021-09-05 04:21:29 +00:00
Matthias Clasen
f7c84ddc7f gtk-demo: Font rendering - Add some bling
Fade the outlines and pixels in and out.
2021-09-04 23:41:44 -04:00
Matthias Clasen
f4a4dd2615 gtk-demo: Font rendering - expand docs 2021-09-04 23:05:55 -04:00
Matthias Clasen
b718d99d1f gtk-demo: Font rendering - Add keynav
Add mnemonics to most controls, and make
Ctrl+/- change the zoom.
2021-09-04 23:00:17 -04:00
Matthias Clasen
c7215d1199 gtk-demo: Font rendering - add outlines
Should outlines as well.
2021-09-04 22:56:07 -04:00
Matthias Clasen
24d69ef125 gtk-demo: Font rendering - better start
Turn antialiasing on initially, and turn the
extents and grid off. Otherwise we show pretty
shocking rendering right from the start.
2021-09-04 22:56:07 -04:00
Matthias Clasen
7c3fbdecf2 gtk-demo: Font rendering - improve the grid
Allow changing the character shown in the grid.
2021-09-04 22:56:07 -04:00
Matthias Clasen
b77938f40f Cosmetics
Remove an unused object from the font rendering
demo ui file.
2021-09-04 22:55:47 -04:00
Matthias Clasen
f703744564 Merge branch 'ebassi/accel-parse-doc' into 'master'
Improve the gtk_accelerator_parse() docs

See merge request GNOME/gtk!3921
2021-09-04 23:39:10 +00:00
Matthias Clasen
a3ccff3773 Merge branch 'text-fixes' into 'master'
textview: fix pango context invalidation

See merge request GNOME/gtk!3922
2021-09-04 21:39:30 +00:00
Asier Sarasua Garmendia
9869082c1c Update Basque translation
(cherry picked from commit 0768addbb3)
2021-09-04 20:54:41 +00:00
Matthias Clasen
754212d0f9 textview: Invalidate Pango contexts
We need to invalidate the Pango contexts when
font settings change. Use the new helper
gtk_widget_update_pango_context to make it less
likely that we forget to update some things.
2021-09-04 15:42:05 -04:00
Matthias Clasen
a9b81577c6 widget: Provide a helper for updating pango contexts 2021-09-04 15:42:05 -04:00
Matthias Clasen
eaa2dada54 Rename an internal function
It occupies a name I want to reuse for something else.
2021-09-04 15:42:05 -04:00
Matthias Clasen
ce2b2128bb Revert "gsk: Add font options to text nodes"
This reverts commit f1347f5841.
2021-09-04 15:42:05 -04:00
Matthias Clasen
41c25b8809 Revert "gsk: Add font options to the glyph cache"
This reverts commit 6599cb001f.
2021-09-04 15:42:05 -04:00
Matthias Clasen
b5ff35ff3e Revert "gsk: Pass font options along"
This reverts commit 299c7c3514.
2021-09-04 15:42:05 -04:00
Matthias Clasen
06423d807f Revert "gtk: Pass font options along"
This reverts commit 062a15310a.
2021-09-04 15:42:05 -04:00
Goran Vidović
0395395d52 Update Croatian translation
(cherry picked from commit 6fe3b21a01)
2021-09-04 18:12:03 +00:00
Emmanuele Bassi
c3ca48b249 Improve the gtk_accelerator_parse() docs
We need to escape the modifiers in angular brackets, or Markdown will
consider them as HTML tags.

We also should document the modifiers we're parsing.
2021-09-04 18:56:08 +01:00
Boyuan Yang
464e0aed04 Update Chinese (China) translation 2021-09-04 17:38:16 +00:00
Goran Vidović
d46e4fcecd Update Croatian translation
(cherry picked from commit 543b7defec)
2021-09-04 17:16:26 +00:00
Changwoo Ryu
363fb96e81 Update Korean translation 2021-09-04 09:02:48 +00:00
Changwoo Ryu
2382bd3bb2 Update Korean translation
(cherry picked from commit 0a5af76932)
2021-09-04 08:59:40 +00:00
Anders Jonsson
3b0a9e84ab Update Swedish translation
(cherry picked from commit 2dbcad428a)
2021-09-03 22:59:37 +00:00
Matthias Clasen
850aebea5d Merge branch 'matthiasc/for-master' into 'master'
widget: Redraw when font options change

See merge request GNOME/gtk!3917
2021-09-03 18:16:33 +00:00
Matthias Clasen
26e632e549 widget: Redraw when font options change
Its the right thing to do, even if it is a rare event.
2021-09-03 13:56:29 -04:00
Matthias Clasen
64d2d7074f Merge branch 'small-text-fixes' into 'master'
gsk: Pass font options down

See merge request GNOME/gtk!3908
2021-09-03 17:21:08 +00:00
Matthias Clasen
062a15310a gtk: Pass font options along
Pass the widget's font options along when we
are creating text nodes.
2021-09-03 12:52:05 -04:00
Matthias Clasen
299c7c3514 gsk: Pass font options along
Use the font options from the text node when
looking up glyphs.
2021-09-03 12:52:05 -04:00
Matthias Clasen
6599cb001f gsk: Add font options to the glyph cache
The cairo_t that we create to render glyphs for
the glyph cache needs to match the font options
that are supposedly governing how glyphs are
drawn.

Since we allow font options to be different per
widget in gtk, we need to have them at least at
the level of individual render nodes. Adding them
to the lookup key for the glyph cache has the
side effect of solving another problem: We are
not flushing the cache when font options change.
2021-09-03 12:52:05 -04:00
Matthias Clasen
f1347f5841 gsk: Add font options to text nodes
Since font options affect how the glyphs get rendered,
we need to pass the font options down from the gtk level
to where the glyph cache is populated.

Add a new gsk_text_node_new_full api that takes a
cairo_font_options_t in addition to the other parameters.
2021-09-03 12:43:35 -04:00
Matthias Clasen
f3ccf62463 gtk-demo: Improve the font rendering demo
Add a toggle for antialiasing, and make the metrics
hinting turn on rounding of positions, to match what
we do with settings now.
2021-09-03 11:29:44 -04:00
Matthias Clasen
44fea33c5d Merge branch 'wip/exalm/buttons' into 'master'
Allow custom children on GtkMenuButton

Closes #4205

See merge request GNOME/gtk!3904
2021-09-03 14:49:09 +00:00
Alexander Mikhaylenko
8b48cf11f9 menubutton: Support custom children
Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/4205
2021-09-03 17:52:46 +05:00
Matthias Clasen
e681fdd958 Merge branch 'matthiasc/for-master' into 'master'
Add a README in gtk/compose

See merge request GNOME/gtk!3916
2021-09-03 00:55:45 +00:00
Matthias Clasen
d4b7a78c54 Add a README in gtk/compose 2021-09-02 20:10:19 -04:00
Matthias Clasen
a45cbad553 Merge branch 'wip/smcv/transparent-is-transparent' into 'master'
reftest-compare: Treat colour channels as undefined if alpha is zero

Closes #4227

See merge request GNOME/gtk!3914
2021-09-02 22:19:27 +00:00
Simon McVittie
16b9a30655 reftest-compare: Treat colour channels as undefined if alpha is zero
If the alpha channel is zero, it doesn't matter what the values of the
red, green and blue channels are: the pixel is still fully transparent.
On most architectures, fully transparent pixels end up all-zeroes
(fully transparent black), matching what's in the reference PNG file;
but on mips*el the blend-difference and blend-normal tests get all-ones
(fully transparent white) and a test failure.

Resolves: https://gitlab.gnome.org/GNOME/gtk/-/issues/4227
Signed-off-by: Simon McVittie <smcv@debian.org>
2021-09-02 22:34:48 +01:00
Jiri Grönroos
82a184a7b7 Update Finnish translation
(cherry picked from commit 33a4ae12ef)
2021-09-02 16:06:57 +00:00
Aurimas Černius
9e198a59b6 Updated Lithuanian translation 2021-09-02 13:51:39 +03:00
Matthias Clasen
c002678085 Merge branch 'fix-gst-subproject' into 'master'
media: Check for gstreamer verion instead of using cc.links()

See merge request GNOME/gtk!3910
2021-09-02 01:33:06 +00:00
Matthias Clasen
486cffc361 Merge branch 'hint-metrics-setting' into 'master'
Add a gtk-hint-font-metrics setting

See merge request GNOME/gtk!3912
2021-09-01 23:15:50 +00:00
Matthias Clasen
67495fcc77 Update settings tests
We have a new setting, so the test that is counting
settings needs an update.
2021-09-01 17:16:08 -04:00
Matthias Clasen
b26a370ce4 Add a gtk-hint-font-metrics setting
This lets people switch back to font rendering that is closer
to what GTK 3 does. It is not perfect - subpixel antialiasing
is not going to work. But it give us an Escape hatch while
we shake out the bugs in our linear layout.

Related: #3787
2021-09-01 15:52:07 -04:00
Matthias Clasen
4dc0e67ac7 Merge branch 'color-glyph-info' into 'master'
Use color glyph information from Pango

See merge request GNOME/gtk!3909
2021-09-01 16:59:38 +00:00
Xavier Claessens
2c060663cf media: Check for gstreamer verion instead of using cc.links()
This fix error when gstgl_dep comes from a subproject because in that
case it cannot be used in compiler checks.
2021-09-01 12:04:05 -04:00
Matthias Clasen
76a8eb4960 Merge branch 'matthiasc/for-master' into 'master'
Initial support for new Pango attributes

See merge request GNOME/gtk!3911
2021-09-01 15:06:26 +00:00
Matthias Clasen
ad3dad1965 Initial support for new Pango attributes
This is still missing the GtkTextTag hookup,
but it fixes the build.
2021-09-01 10:32:45 -04:00
Danial Behzadi
172d97de05 Update Persian translation 2021-09-01 09:42:39 +00:00
Daniel Mustieles
4c967d5b45 Updated Spanish translation 2021-09-01 11:02:13 +02:00
Matthias Clasen
b244f31337 Use color glyph information from Pango
Pango now sets a bit in PangoGlyphVisAttr for
color glyphs, so we don't need to do that
ourselves anymore.
2021-09-01 01:47:06 -04:00
Nathan Follens
8550a04bf4 Update Dutch translation
(cherry picked from commit 9aa08dd1b8)
2021-08-31 15:55:14 +00:00
eshagh shahidani
cb38ead48e Update Persian translation
(cherry picked from commit 7691c94a37)
2021-08-31 14:42:34 +00:00
Matthias Clasen
9713c3394d Merge branch 'matthiasc/for-master' into 'master'
editable: Clarify the docs

See merge request GNOME/gtk!3906
2021-08-31 13:15:47 +00:00
Matthias Clasen
fa57b006c9 editable: Clarify the docs
Make it clear that your class must have all the editable properties
already before you call the (confusingly named) function
gtk_editable_install_properties.
2021-08-31 08:41:45 -04:00
Matthias Clasen
9f73f0234a Merge branch 'default-settings-non-debug' into 'master'
Revert "reftests: Enforce default settings"

See merge request GNOME/gtk!3903
2021-08-30 18:09:28 +00:00
Matthias Clasen
a4f067481c gdk: Make GDK_DEBUG=default-settings unconditional
Our tests use this settings, so we should respect it
in non-debug builds as well.
2021-08-30 11:01:55 -04:00
Matthias Clasen
733fb527fa Revert "reftests: Enforce default settings"
This reverts commit 4e4f57e091.

This should not be necessary, since we have GDK_DEBUG=default-settings.
2021-08-30 11:01:22 -04:00
Matthias Clasen
3667c6b053 Merge branch 'wip/smcv/compose-files' into 'master'
Fix Compose handling on big-endian host architectures

Closes #4217

See merge request GNOME/gtk!3902
2021-08-30 12:52:54 +00:00
Simon McVittie
1071818df8 compose: Update sequences from libX11 1.7.2
This adds support for sequences like <Compose>,G,u -> capital G with
breve. Previously, only a capital U was accepted for E, G, I and O
(but a lower-case u was accepted for A and U for some reason).

Signed-off-by: Simon McVittie <smcv@debian.org>
2021-08-30 13:09:27 +01:00
Simon McVittie
11c2d9ea30 compose: Document how to get compose-parse input from libX11 source
Signed-off-by: Simon McVittie <smcv@debian.org>
2021-08-30 13:09:05 +01:00
Simon McVittie
52cdf3056d compose: Generate endian-dependent compact Compose data
The GtkComposeTable cache is always in big-endian format and is
byteswapped on load for the more common little-endian CPUs, but
init_builtin_table() in GtkIMContextSimple can't byteswap the built-in
data without copying it, which is undesirable. Pregenerate both big-
and little-endian compose data, and compile the correct flavour into
each build of GTK. This fixes failure of the composetable test when
building for a big-endian architecture such as s390x and (traditional,
big-endian) powerpc.

Resolves: https://gitlab.gnome.org/GNOME/gtk/-/issues/4217
Signed-off-by: Simon McVittie <smcv@debian.org>
2021-08-30 11:29:46 +01:00
Benjamin Otte
32899a1edd Merge branch 'wip/otte/for-master' into 'master'
fontbutton: Make sure the window is on the same display

See merge request GNOME/gtk!3901
2021-08-30 04:37:49 +00:00
Benjamin Otte
119458f13e contentdeserializer: Fix some API awkwardness
Allow uninitialized GValues to be passed. Makes life easier for the
inspector code I'm writing.
2021-08-30 06:02:16 +02:00
Benjamin Otte
2863095f06 node-editor: Allow dragging and dropping the center picture
Dragging will just drag the render node.

Dropping will replace the current contents of the textview with the
dropped node.

Neat side effect: You can drag the node onto itself to do a
deserialize/serialize of the current text.
2021-08-30 06:02:16 +02:00
Benjamin Otte
cbc050b9ed recorder: Make the render nodes in the list draggable
You can (soon) drag any of the nodes directly into the node-editor.
2021-08-30 06:02:16 +02:00
Benjamin Otte
18affbd390 dragicon: Add a default drag icon for GskRenderNode
Somebody should make this default drag icon machinery pluggable.
2021-08-30 06:02:16 +02:00
Benjamin Otte
0b7a36ce33 rendernode: Add (de)serialization support for render nodes
This includes our own new mime type for render nodes, too.
2021-08-30 06:02:15 +02:00
Benjamin Otte
1957915940 rendernode: Add GValue functions for render nodes
This is needed as GskRenderNode is its own fundamental type and has its
own GValue infrastructure. And I want to put render nodes into the
clipboard which uses GValues.
2021-08-30 06:02:15 +02:00
Benjamin Otte
552b71d4e2 stylecontext: Remove unneeded headers 2021-08-30 06:02:15 +02:00
Benjamin Otte
480112f9aa fontbutton: Destroy dialog in unrealize()
This way, we can be sure it's always using the right display.

We can also be sure that it doesn't leak.
2021-08-30 06:02:15 +02:00
Benjamin Otte
9a2f4d8026 fontbutton: Make sure the window is on the same display
We don't want the font buttons created by the inspector to run on the
default display.
2021-08-30 06:02:15 +02:00
Benjamin Otte
3b15f32335 Merge branch 'wip/otte/symbolic-paintable' into 'master'
Add GtkSymbolicPaintable

See merge request GNOME/gtk!3895
2021-08-30 03:54:35 +00:00
Benjamin Otte
7404a6fc4f Merge branch 'wip/otte/G_TYPE_POINTER' into 'master'
Get rid of G_TYPE_POINTER

See merge request GNOME/gtk!3900
2021-08-30 03:50:30 +00:00
Benjamin Otte
91d1ec41c2 symbolicpaintable: Refactor default colors path 2021-08-30 05:29:04 +02:00
Benjamin Otte
9237c8be67 icontheme: Refactor gtk_icon_theme_lookup_symbolic_colors()
With the new enum, the code looks much cleaner.
2021-08-30 04:52:47 +02:00
Benjamin Otte
6c87d362c5 enums: Add a GtkSymbolicColor enum
That way we don't nedmagic numbres to index the colors array.

Use it in the symbolic paintable demo.
2021-08-30 04:36:16 +02:00
Baurzhan Muftakhidinov
4f31d3587d Update Kazakh translation
(cherry picked from commit 37d2d407a1)
2021-08-29 17:09:01 +00:00
Baurzhan Muftakhidinov
29a2f4021a Update Kazakh translation
(cherry picked from commit 9e62fd82ea)
2021-08-29 07:41:58 +00:00
Benjamin Otte
e2d321a16a searchengine: Rename private headers
The code uses G_TYPE_POINTER and I got all sorts of scared.
2021-08-29 06:52:28 +02:00
Benjamin Otte
7366f5099a filesystemmodel: Don't use G_TYPE_POINTER
It's a GError, use G_TYPE_ERROR.
2021-08-29 06:52:28 +02:00
Benjamin Otte
4fbef5f466 placessidebar: Don't use G_TYPE_POINTER
We have a type for lists of GFiles. We invented it for DND.

Keep the annotation info, so bindings don't have to know this.
2021-08-29 06:52:28 +02:00
Benjamin Otte
2e48ff3362 pathbar: Use correct GType in signal 2021-08-29 06:52:28 +02:00
Benjamin Otte
7cdbdb663c gtk-demo: Add a symbolic paintable demo
Don't click the button!
2021-08-29 04:29:53 +02:00
Benjamin Otte
1b390d3857 gtk-demo: Explicitly pass nuclear icon colors 2021-08-29 04:29:53 +02:00
Benjamin Otte
2808f9c75b gtk: Port icontheme to GtkSymbolicPaintable 2021-08-29 04:29:53 +02:00
Benjamin Otte
49d109c29e gtk: Add the GtkSymbolicPaintable interface 2021-08-29 04:29:53 +02:00
Benjamin Otte
36dd959bf9 build: Check that introspection is enabled for docs build 2021-08-29 04:29:53 +02:00
Benjamin Otte
2f9a67b8a1 docs: Fix vfunc references
vfunc references should just use the object name, not the
class/interface/iface name.
2021-08-29 04:29:02 +02:00
Matthias Clasen
6d5d9ea073 Merge branch 'text-selection-missing' into 'master'
ngl: Avoid a coordinate overflow

Closes #4214

See merge request GNOME/gtk!3897
2021-08-28 21:21:02 +00:00
Matthias Clasen
1cfd340cae ngl: Avoid a coordinate overflow
This was showing up as big text selections going
missing sporadically.

Fixes: #4214
2021-08-28 15:42:30 -04:00
Matthias Clasen
369ccfc34f Merge branch 'matthiasc/for-master' into 'master'
Draw negatively sloped carets correctly

See merge request GNOME/gtk!3892
2021-08-28 17:18:21 +00:00
Matthias Clasen
e556513de6 Merge branch 'menu-accel' into 'master'
window: Prefer menu bars when handling F10

See merge request GNOME/gtk!3893
2021-08-28 17:17:43 +00:00
Matthias Clasen
79dc1a2a4d Merge branch 'randr' into 'master'
x11: Fix handling of `RRScreenChangeNotify`/`RRNotify` events

See merge request GNOME/gtk!3894
2021-08-28 17:17:00 +00:00
Ian Douglas Scott
9e5a501c73 x11: Fix handling of RRScreenChangeNotify/RRNotify events
It seems these are sent with `xwindow` set to the root window, so this
was failing to find a surface and get the screen from that.

I'm not sure if there's a reason not to get the screen this way
elsewhere in the function, but it seems this should be correct.

This fixes the behavior of `gdk_x11_display_get_monitors()`, which
wasn't correctly changing when monitors were added or removed. For
instance, this python code was always showing the same number of
monitors when one was turned off and on, but updates correctly with this
change applied:

```python
import gi
gi.require_version("GLib", "2.0")
gi.require_version("Gdk", "4.0")
gi.require_version("Gtk", "4.0")
from gi.repository import GLib, Gdk, Gtk

def f():
    print(len(Gdk.Display.get_default().get_monitors()))
    return True
GLib.timeout_add_seconds(1, f)

GLib.MainLoop().run()
```
2021-08-27 18:07:39 -07:00
Enrico Nicoletto
2db8d7f6a2 Update Brazilian Portuguese translation
(cherry picked from commit cd9735eab1)
2021-08-28 01:00:39 +00:00
Enrico Nicoletto
94c3f4d4fc Update Brazilian Portuguese translation
(cherry picked from commit 8f5a1471cc)
2021-08-28 00:44:38 +00:00
Florian Müllner
3fa26861cd window: Prefer menu bars when handling F10
Focusing the first widget in the titlebar is a good fallback,
but a "real" menubar or :primary menu button should take
precedence.
2021-08-28 00:19:23 +02:00
Matthias Clasen
ef2b0ccd6c Draw negatively sloped carets correctly
The code for handling negatively sloped caret
positions was not tested, and naturally did
not work. Fix it.
2021-08-27 17:20:35 -04:00
Matthias Clasen
f1702d24e7 Merge branch 'css-line-decoration' into 'master'
textview: Apply line decoration from css

See merge request GNOME/gtk!3890
2021-08-27 01:37:57 +00:00
Matthias Clasen
3a50ff57d8 Clean up the code
Avoid compiler warnings, and make the code
a bit more compact.
2021-08-26 20:45:17 -04:00
Matthias Clasen
8ac2e8d495 textview: Apply font features from css
We were forgetting to propagate these values from
CSS to the default attributes. Share the code for
getting these values out of a GtkCssStyle.
2021-08-26 20:21:23 -04:00
Matthias Clasen
e56cf85fd1 textview: Apply text transform from css
We were forgetting to propagate these values from
CSS to the default attributes.
2021-08-26 18:53:30 -04:00
Matthias Clasen
1c6aef1862 textview: Apply line decoration from css
We were forgetting to propagate these values from
CSS to the default attributes.
2021-08-26 18:46:57 -04:00
Matthias Clasen
4653cbe56e Merge branch 'css-letter-spacing' into 'master'
textview: Don't forget to collect css letterspacing

Closes #4207

See merge request GNOME/gtk!3889
2021-08-26 19:37:55 +00:00
Matthias Clasen
93148d302f textview: Don't forget to collect css letterspacing
There are more properties missing, this is just
the start.

Fixes: #4207
2021-08-26 15:01:23 -04:00
Andika Triwidada
a443ba91ce Update Indonesian translation
(cherry picked from commit 85ad1db8ef)
2021-08-26 14:40:16 +00:00
Hugo Carvalho
d8daef2de7 Update Portuguese translation
(cherry picked from commit 6e467de751)
2021-08-26 14:30:04 +00:00
Hugo Carvalho
845d43c77a Update Portuguese translation
(cherry picked from commit ae5b6395ad)
2021-08-26 14:12:59 +00:00
Andika Triwidada
f8dbcc472b Update Indonesian translation
(cherry picked from commit dcc899b621)
2021-08-26 13:39:00 +00:00
Marek Černocký
0f0f75f6b1 Updated Czech translation 2021-08-26 15:07:54 +02:00
Timm Bäder
4e2e2d84fc Merge branch 'clang-fallthrough' into 'master'
build: fix implicit-fallthrough errors with clang

See merge request GNOME/gtk!3887
2021-08-26 11:15:28 +00:00
Jordi Mas
887885b6e0 Update Catalan translation 2021-08-26 13:10:32 +02:00
Matthias Clasen
a868a5c434 Merge branch 'matthiasc/for-master' into 'master'
More markup demos

See merge request GNOME/gtk!3888
2021-08-26 06:15:06 +00:00
Christoph Reiter
a0c147d246 build: fix implicit-fallthrough errors with clang
The clang build fails due to -Werror=implicit-fallthrough being
on by default and some fallthrough cases not being marked as such.

Use G_GNUC_FALLTHROUGH or duplicate the code in those cases.
2021-08-26 07:08:38 +02:00
Matthias Clasen
38040c96d6 More markup demos
Showing more of what Pango can do.
2021-08-25 19:38:59 -04:00
Matthias Clasen
989ecf69ad Merge branch 'new-pango-attributes' into 'master'
Support new pango attributes

See merge request GNOME/gtk!3878
2021-08-25 20:11:45 +00:00
Matthias Clasen
9d26399143 Merge branch 'treeexpander-claim-gestures' into 'master'
GtkTreeExpander: claim expander-icon gestures

Closes #4199

See merge request GNOME/gtk!3886
2021-08-25 19:11:23 +00:00
Matthias Clasen
b9cad7cc54 textview: Implement new pango attributes
Implement the word and sentence attributes.
2021-08-25 14:57:13 -04:00
Matthias Clasen
357b97b5a6 Support new pango attributes
Support new Pango attributes for segmentation
in GtkBuilder.
2021-08-25 14:57:13 -04:00
Jan Lukas Gernert
318160d836 GtkTreeExpander: claim expander-icon gestures
Claim the pressed, released and canceled gestures
meant for the expander-icon of the TreeExpander.
This avoids selecting the row when expanding or collapsing it.

Closes #4199
2021-08-25 17:50:12 +02:00
Matthias Clasen
5e90d63373 Merge branch 'sloped-caret' into 'master'
Implement sloped caret drawing

See merge request GNOME/gtk!3880
2021-08-25 13:43:24 +00:00
Jordi Mas
0b7f1e488f Update Catalan translation 2021-08-25 15:17:27 +02:00
Matthias Clasen
e281bbbb55 Merge branch 'treeexpander-indent-property' into 'master'
GtkTreeExpander: indent-without-expander property

Closes #4175

See merge request GNOME/gtk!3858
2021-08-25 11:48:59 +00:00
Matthias Clasen
892bacee52 Merge branch '4195-include-gdkprivate-h' into 'master'
gdkmacoseventsource: Include "gdk-private.h"

Closes #4195

See merge request GNOME/gtk!3882
2021-08-25 11:48:11 +00:00
Matthias Clasen
2f1db4b77c Use pango_layout_get_caret_pos
This new api returns rectangles for sloped carets,
in contrast to pango_layout_get_cursor_pos, which
always returns a rectangle with a width of zero.
2021-08-25 06:53:53 -04:00
Matthias Clasen
f90e9b26f8 Implement sloped caret drawing
Prepare for Pango to return slope information for carets.

While we are at it, improve the shape of our carets a bit.
2021-08-25 06:53:53 -04:00
Matthias Clasen
ad71f454bd Merge branch 'fix-gettext' into 'master'
Fix wrong gettext ITS target

See merge request GNOME/gtk!3881
2021-08-25 10:51:57 +00:00
Rafał Dzięgiel
c66bd791c4 Fix wrong gettext ITS target
The installed ITS rule filename is "gtk4builder.its". The .loc file
is wrongly pointing to old "gtkbuilder.its" which makes gettext fail
on systems without GTK3 installed.
2021-08-25 10:08:50 +02:00
Fran Dieguez
40bceef3ca Update Galician translation
(cherry picked from commit 410ea1049b)
2021-08-25 07:26:55 +00:00
Fran Dieguez
9d79f55dc9 Update Galician translation
(cherry picked from commit 09a5828ccf)
2021-08-25 06:12:43 +00:00
Matthias Clasen
c5c1cf6d55 Merge branch 'wip/otte/for-master' into 'master'
wayland: Better error on eglGetDisplay() failure

See merge request GNOME/gtk!3879
2021-08-25 04:24:55 +00:00
Peter Bloomfield
9eb8aa9a32 gdkmacoseventsource: Include "gdk-private.h"
_gdk_macos_event_source_new() calls g_source_set_static_name(), which
for GLib versions before 2.69.1 is a macro defined in gdk-private.h.

Fixes #4195

modified:   gdk/macos/gdkmacoseventsource.c
2021-08-24 16:55:52 -04:00
Benjamin Otte
95931a7e6c wayland: Better error on eglGetDisplay() failure
Goals:

1. Provide as much information as possible in the error message, so
   users can try to fix their system themselves.
2. Try to formulate the error message in a way that explains that this
   is not something GTK can fix, but a lower layer problem.

Related: #4193
2021-08-24 19:55:22 +00:00
Yuri Chornoivan
d9daaed43f Update Ukrainian translation
(cherry picked from commit 6377b23bd6)
2021-08-24 17:29:02 +00:00
Matthias Clasen
b9c51e65c1 Merge branch 'wip/carlosg/gesture-state-in-entries' into 'master'
Claim clicks further at GtkEntry/GtkSearchEntry

See merge request GNOME/gtk!3873
2021-08-24 17:20:20 +00:00
Matthias Clasen
da5eb92f47 Merge branch 'matthiasc/for-master' into 'master'
Revert "imwayland: Tweak preedit text"

Closes #4123

See merge request GNOME/gtk!3877
2021-08-24 17:19:42 +00:00
Matthias Clasen
03ed585f6f tests: Rewrite PangoAttribute introspection
We can use the new binding helpers to make this
a little less bothersome. That way, it will need
tweaks less often (only when new fundamental types
are introduced).
2021-08-24 12:53:48 -04:00
Matthias Clasen
51f953a1aa Revert "imwayland: Tweak preedit text"
This reverts commit d84a028455.

IBus uses the same character now.

Fixes: #4123
2021-08-24 12:53:48 -04:00
Emmanuele Bassi
93fb07d808 Merge branch 'expander-ref-leak' into 'master'
Release an extra reference on GtkExpander:child

See merge request GNOME/gtk!3876
2021-08-24 13:20:48 +00:00
Emmanuele Bassi
5e311d4d85 Release an extra reference on GtkExpander:child
The child of a GtkExpander is owned directly by the expander whenever
the "expanded" flag is unset.

We are adding an additional reference to the child of an expander when
expander is not expanded.

Additionally, if a GtkExpander is disposed while not expanded, we need
to explicitly release the reference on the child widget that we own.

This reference leak was masked in GTK3 by GtkContainer removing each
child from the parent container by recursively calling
gtk_widget_destroy().
2021-08-24 11:28:59 +01:00
Carlos Garnacho
e652054d50 gtkpasswordentry: Add a "catchall" click gesture handler
This gesture is set on the whole widget surface, since there's
multiple input targets inside an entry (icons, the GtkText itself)
it makes sense to consider the full entry an area handling clicks.
Ensure these events don't propagate further up, and result in other
actions.
2021-08-24 01:13:29 +02:00
Carlos Garnacho
58c31b8627 gtkpasswordentry: Consume click events after pressing on icon
This may result in user actions, so the gesture should consume the
events.
2021-08-24 01:12:31 +02:00
Matthias Clasen
b945033b41 Merge branch 'fix-docs' into 'master'
Fix documentation for gtk_icon_view_create_drag_icon

Closes #4190

See merge request GNOME/gtk!3875
2021-08-23 20:40:17 +00:00
Qiu Wenbo
92d2867170 docs: Fix documentation for gtk_icon_view_create_drag_icon
Fixes: #4190
2021-08-24 00:24:08 +08:00
Matthias Clasen
5d38c8c558 Merge branch 'wip/kalev/pre-compiled-css-detection' into 'master'
build: Fix detection for pre-compiled css files

See merge request GNOME/gtk!3874
2021-08-23 14:57:19 +00:00
Kalev Lember
552a4b2c82 build: Fix detection for pre-compiled css files
The default theme changed from Adwaita to Default and this tripped up
the logic to detect if the tarball builds contain pre-built css files or
not. Fix this by looking at pre-compiled css files in themes/Default/
instead of themes/Adwaita/.
2021-08-23 16:15:48 +02:00
Carlos Garnacho
7a4afb08bd gtksearchentry: Add a "catchall" click gesture handler
This gesture is set on the whole widget surface, since there's
multiple input targets inside an entry (icons, the GtkText itself)
it makes sense to consider the full entry an area handling clicks.
Ensure these events don't propagate further up, and result in other
actions.
2021-08-23 15:38:33 +02:00
Carlos Garnacho
c5cd1ac630 gtkentry: Add a "catchall" click gesture handler
This gesture is set on the whole widget surface, since there's
multiple input targets inside an entry (icons, the GtkText itself)
it makes sense to consider the full entry an area handling clicks.
Ensure these events don't propagate further up, and result in other
actions.
2021-08-23 15:38:33 +02:00
Carlos Garnacho
90edb76415 gtksearchentry: Consume click events after pressing on icon
This results in user actions, so the gesture should consume the
events.
2021-08-23 15:38:33 +02:00
Carlos Garnacho
8f397502df gtktext: Set claimed state on button1 click gesture
All possible ramifications after button1 press (move cursor,
begin drag, begin dnd, select word/line, ...) result in user
actions. The right thing after that is consuming the events,
set the gesture state for that.
2021-08-23 15:28:27 +02:00
Jan Lukas Gernert
1cb17d8613 GtkTreeExpander: indent-for-icon property
A property to determine if the TreeExpander should indent the child
by the width of an expander-icon when it is not expandable.

Closes #4175
2021-08-23 07:54:45 +02:00
Jan Lukas Gernert
88e796bb45 Add 4.6 version macros 2021-08-23 07:54:27 +02:00
Matthias Clasen
1310bf0218 Merge branch 'matthiasc/for-master' into 'master'
NEWS: Updates

See merge request GNOME/gtk!3872
2021-08-22 21:16:02 +00:00
Matthias Clasen
4efeaa844f NEWS: Updates 2021-08-22 16:41:22 -04:00
Matthias Clasen
24685612a9 Merge branch 'css-text-transform' into 'master'
css: Implement text-transform

See merge request GNOME/gtk!3862
2021-08-22 20:40:50 +00:00
Matthias Clasen
bf21df4195 Add an example for text transforms
Show some transformed text in the markup demo.
2021-08-22 15:57:50 -04:00
Matthias Clasen
417b3f9c6b Implement text transforms for GtkTextView
Add a property to GtkTextTag and do all the
legwork to translate it to the Pango attribute.
2021-08-22 15:57:50 -04:00
Matthias Clasen
2d84a1c63e css: Implement text-transform
Implement the text-transform property from
https://www.w3.org/TR/css-text-3/#text-transform-property
using a new Pango attribute.
2021-08-22 15:57:50 -04:00
Matthias Clasen
5ab9a29bc4 Merge branch 'bump-pango' into 'master'
Bump the pango dependency

See merge request GNOME/gtk!3871
2021-08-22 19:57:21 +00:00
Matthias Clasen
383ea0d86a Merge branch 'css-line-height2' into 'master'
Implement CSS line-height

See merge request GNOME/gtk!3833
2021-08-22 19:47:10 +00:00
Matthias Clasen
df8f75bb8f Merge branch 'matthiasc/for-master' into 'master'
windowhandle: Quiet a compiler warning

See merge request GNOME/gtk!3868
2021-08-22 19:28:01 +00:00
Matthias Clasen
ba95ef63da Drop PANGO_VERSION_CHECK checks
We require Pango 1.49 now.
2021-08-22 15:26:23 -04:00
Matthias Clasen
bea4aa31ea Bump Pango dep
Require Pango 1.49, so we can support new attributes
without too much ifdeffery.
2021-08-22 15:23:42 -04:00
Matthias Clasen
6c19a8352f gtk-demo: Add a line height demo
Add an example for widely spaced text to the markup demo.
2021-08-22 15:15:50 -04:00
Matthias Clasen
e3df89b524 gtk-demo: Improve markup demo
The font sizes demo had the space between the font-size spans,
causing us to have a run with just a default sized space between
the words, which in turn leads to wobbly cursor sizes. Avoid that
by including the space in the preceding span.

Also, make it bigger.
2021-08-22 15:15:50 -04:00
Matthias Clasen
a9003f7ac0 gtk-demo: Set line height from markup
Keep the import-markup code in sync with GtkTextBuffer.
2021-08-22 15:15:50 -04:00
Matthias Clasen
2cc06f60c5 textview: Set line height from markup
Note that we ignore the absolute-line-height
attribute for now.
2021-08-22 15:15:50 -04:00
Matthias Clasen
4759afcc3e textview: Set line height from css
Set the line height in the default attributes from
the CSS style. This makes line height work in
GtkTextView.
2021-08-22 15:15:50 -04:00
Matthias Clasen
66b297e408 textview: Add line height plumbing
This adds a line-height property to GtkTexttag and a
line_height field to GtkTextAttributes, and translates
it to a pango attribute.
2021-08-22 15:15:50 -04:00
Matthias Clasen
bbd2b255a3 css: Document line-height as supported 2021-08-22 15:15:50 -04:00
Matthias Clasen
8b2f28eee3 css: Add parser tests for line-height 2021-08-22 15:15:50 -04:00
Matthias Clasen
ae767dc5bf css: Translate line-height to a Pango
Pango 1.50 introduces a line-height attribute. Use it.
This is enough to make line-height work for labels
and entries.
2021-08-22 15:15:50 -04:00
Matthias Clasen
d923402934 css: Add line-height property
This adds the plumbing to parse the line-height
property from CSS. Widgets are not picking it
up yet.
2021-08-22 15:15:50 -04:00
Benjamin Otte
9667f889b3 Merge branch 'clipboard-set-docs' into 'master'
docs: Expand gdk_clipboard_set() docs

See merge request GNOME/gtk!3865
2021-08-22 18:49:13 +00:00
Benjamin Otte
d2ea7af335 docs: Expand gdk_clipboard_set() docs 2021-08-22 18:49:12 +00:00
Matthias Clasen
f95e9407a9 Fix up test output for new version
Annoying that this test has to be touched every time
we bump the version.
2021-08-22 14:48:12 -04:00
Matthias Clasen
7be3c2974a windowhandle: Quiet a compiler warning
gesture was left uninitialized in the default case.
2021-08-22 14:19:14 -04:00
Matthias Clasen
5444f604cb Merge branch 'matthiasc/for-master' into 'master'
Bump version to 4.5

See merge request GNOME/gtk!3867
2021-08-22 17:53:34 +00:00
Matthias Clasen
94e68b0e36 Bump version to 4.5
4.4 has been branched off.
2021-08-22 13:06:29 -04:00
Matthias Clasen
910d86f958 NEWS: Updates 2021-08-22 13:06:15 -04:00
Matthias Clasen
fd885e42b0 Merge branch 'drop-gl' into 'master'
gsk: Drop the gl renderer

See merge request GNOME/gtk!3863
2021-08-22 17:04:25 +00:00
Matthias Clasen
ea31ae31c1 tests: Remove an outdated comment
It refers to a file that no longer exists.
2021-08-22 12:29:59 -04:00
Matthias Clasen
00763e5af3 media: Don't include gsk/gl/gskglrenderer.h 2021-08-22 12:29:40 -04:00
Matthias Clasen
1bf5aab18c testutils: Don't include gsk/gl/gskglrenderer.h 2021-08-22 12:29:13 -04:00
Matthias Clasen
e9e373913e gsk: Drop the gl renderer
ngl supports all the same platforms as gl
now, and has seen more improvements in the
last cycle.
2021-08-20 22:58:30 -04:00
278 changed files with 70798 additions and 81468 deletions

View File

@@ -40,7 +40,7 @@ export CCACHE_DIR="${CCACHE_BASEDIR}/_ccache"
# https://gitlab.gnome.org/GNOME/gtk/-/issues/2243
# https://gitlab.gnome.org/GNOME/gtk/-/issues/3002
if ! pkg-config --atleast-version=2.65.0 glib-2.0; then
if ! pkg-config --atleast-version=2.66.0 glib-2.0; then
git clone https://gitlab.gnome.org/GNOME/glib.git _glib
meson setup _glib_build _glib
meson compile -C _glib_build
@@ -48,7 +48,7 @@ if ! pkg-config --atleast-version=2.65.0 glib-2.0; then
fi
pkg-config --modversion glib-2.0
if ! pkg-config --atleast-version=1.47.0 pango; then
if ! pkg-config --atleast-version=1.49.1 pango; then
git clone https://gitlab.gnome.org/GNOME/pango.git _pango
meson setup _pango_build _pango
meson compile -C _pango_build

92
NEWS
View File

@@ -1,89 +1,19 @@
Overview of Changes in 4.4.1
============================
Overview of Changes
===================
* input:
- Fix compose data on bigendian systems
- Accept long replacement strings for compose sequences
* gsk:
- Drop the GL renderer in favor of NGL
* a11y:
- Fix accessible relations for menu buttons
- Fix accessible descriptions in some cases
- Simplify atspi context a bit
* Theme:
- Improve styling for toggle buttons in toolbars
* gsk:
- Avoid coordinate overflow with big text selections
* css:
- Add support for line-height
- Add support for text-transform
* GtkTextView:
- Fix applying css properties such as
letterspacing, line decoration, font
features, etc
- Improve grouping for undo
- Invalidate Pango contexts when font settings change
- Fix a crash
- Add support for line height
- Add support for text transforms
* GtkFileChooser:
- Fix handling of choices
* GtkStackSwitcher:
- Expand child buttons
* GtkCalendar:
- Fix day names for weeks that start on monday
* GtkWindow:
- Prefer menubars when handling F10
* GtkFontButton:
- Make sure the dialog is on the same display
* X11:
- Fix handling of randr events
* Windows:
- Fix DND coordinates
- Fix build
* macOS:
- Fix handling of scale on macOS 12
* build:
- Fix detection of pre-compiled css files
* Translation updates
Basque
Brazilian Portuguese
Catalan
Chinese (China)
Croatian
Czech
Danish
Finnish
French
Galician
German
Hebrew
Hungarian
Icelandic
Indonesian
Kazakh
Korean
Latvian
Lithuanian
Persian
Polish
Portuguese
Romanian
Russian
Serbian
Slovak
Slovenian
Spanish
Swedish
Turkish
Ukrainian
* Build:
- Require Pango 1.49
Overview of Changes in 4.4.0

View File

@@ -10,12 +10,10 @@ GTK is a multi-platform toolkit for creating graphical user interfaces.
Offering a complete set of widgets, GTK is suitable for projects ranging
from small one-off projects to complete application suites.
GTK is a free and open-source software project. However, the licensing terms
for GTK, the GNU LGPL, allow it to be used by all developers, including those
developing proprietary software, without any license fees or royalties.
GTK is hosted by the GNOME project (thanks!) and used by a wide variety
of applications and projects.
GTK is free software and part of the GNU Project. However, the
licensing terms for GTK, the GNU LGPL, allow it to be used by all
developers, including those developing proprietary software, without any
license fees or royalties.
The official download location
@@ -153,9 +151,6 @@ Contributing to GTK
Please, follow the [contribution guide](./CONTRIBUTING.md) to know how to
start contributing to GTK.
If you want to support GTK financially, please consider donating to
the GNOME project, which runs the infrastructure hosting GTK.
Release notes
-------------

View File

@@ -321,6 +321,7 @@
<file>paintable_emblem.c</file>
<file>paintable_mediastream.c</file>
<file>paintable_svg.c</file>
<file>paintable_symbolic.c</file>
<file>panes.c</file>
<file>password_entry.c</file>
<file>peg_solitaire.c</file>

View File

@@ -131,6 +131,19 @@ insert_tags_for_attributes (GtkTextBuffer *buffer,
gtk_text_buffer_apply_tag (buffer, tag, start, end); \
}
#define VOID_ATTR(attr_name) \
{ \
tag = gtk_text_tag_table_lookup (table, #attr_name); \
if (!tag) \
{ \
tag = gtk_text_tag_new (#attr_name); \
g_object_set (tag, #attr_name, TRUE, NULL); \
gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \
} \
gtk_text_buffer_apply_tag (buffer, tag, start, end); \
}
fg_alpha = bg_alpha = 1.;
attrs = pango_attr_iterator_get_attrs (iter);
@@ -255,11 +268,28 @@ insert_tags_for_attributes (GtkTextBuffer *buffer,
INT_ATTR (insert_hyphens);
break;
#if PANGO_VERSION_CHECK(1,49,0)
case PANGO_ATTR_LINE_HEIGHT:
FLOAT_ATTR (line_height);
break;
case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
break;
#endif
case PANGO_ATTR_WORD:
VOID_ATTR (word);
break;
case PANGO_ATTR_SENTENCE:
VOID_ATTR (sentence);
break;
case PANGO_ATTR_BASELINE_SHIFT:
INT_ATTR (baseline_shift);
break;
case PANGO_ATTR_FONT_SCALE:
INT_ATTR (font_scale);
break;
case PANGO_ATTR_SHAPE:
case PANGO_ATTR_ABSOLUTE_SIZE:
@@ -269,16 +299,9 @@ insert_tags_for_attributes (GtkTextBuffer *buffer,
case PANGO_ATTR_BACKGROUND_ALPHA:
break;
#if PANGO_VERSION_CHECK(1,49,0)
case PANGO_ATTR_TEXT_TRANSFORM:
#endif
#if PANGO_VERSION_CHECK(1,49,1)
case PANGO_ATTR_WORD:
case PANGO_ATTR_SENTENCE:
case PANGO_ATTR_BASELINE_SHIFT:
case PANGO_ATTR_FONT_SCALE:
INT_ATTR (text_transform);
break;
#endif
case PANGO_ATTR_INVALID:
default:

View File

@@ -1,6 +1,11 @@
/* Pango/Font Rendering
*
* Demonstrates various aspects of font rendering.
* Demonstrates various aspects of font rendering,
* such as hinting, antialiasing and grid alignment.
*
* The demo lets you explore font rendering options
* interactively to get a feeling for they affect the
* shape and positioning of the glyphs.
*/
#include <gtk/gtk.h>
@@ -10,16 +15,21 @@ static GtkWidget *font_button = NULL;
static GtkWidget *entry = NULL;
static GtkWidget *image = NULL;
static GtkWidget *hinting = NULL;
static GtkWidget *anti_alias = NULL;
static GtkWidget *hint_metrics = NULL;
static GtkWidget *up_button = NULL;
static GtkWidget *down_button = NULL;
static GtkWidget *text_radio = NULL;
static GtkWidget *show_grid = NULL;
static GtkWidget *show_extents = NULL;
static GtkWidget *show_pixels = NULL;
static GtkWidget *show_outlines = NULL;
static PangoContext *context;
static int scale = 9;
static int scale = 7;
static double pixel_alpha = 1.0;
static double outline_alpha = 0.0;
static void
update_image (void)
@@ -37,6 +47,8 @@ update_image (void)
cairo_font_options_t *fopt;
cairo_hint_style_t hintstyle;
cairo_hint_metrics_t hintmetrics;
cairo_antialias_t antialias;
cairo_path_t *path;
if (!context)
context = gtk_widget_create_pango_context (image);
@@ -65,6 +77,13 @@ update_image (void)
hintmetrics = CAIRO_HINT_METRICS_OFF;
cairo_font_options_set_hint_metrics (fopt, hintmetrics);
if (gtk_check_button_get_active (GTK_CHECK_BUTTON (anti_alias)))
antialias = CAIRO_ANTIALIAS_GRAY;
else
antialias = CAIRO_ANTIALIAS_NONE;
cairo_font_options_set_antialias (fopt, antialias);
pango_context_set_round_glyph_positions (context, hintmetrics == CAIRO_HINT_METRICS_ON);
pango_cairo_context_set_font_options (context, fopt);
cairo_font_options_destroy (fopt);
pango_context_changed (context);
@@ -85,10 +104,14 @@ update_image (void)
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_paint (cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_set_source_rgba (cr, 0, 0, 0, pixel_alpha);
cairo_move_to (cr, 10, 10);
pango_cairo_show_layout (cr, layout);
pango_cairo_layout_path (cr, layout);
path = cairo_copy_path (cr);
cairo_destroy (cr);
g_object_unref (layout);
@@ -127,7 +150,7 @@ update_image (void)
if (gtk_check_button_get_active (GTK_CHECK_BUTTON (show_extents)))
{
cairo_set_source_rgba (cr, 0, 0, 1, 1);
cairo_set_source_rgb (cr, 0, 0, 1);
cairo_rectangle (cr,
scale * (10 + pango_units_to_double (logical.x)) - 0.5,
@@ -140,7 +163,7 @@ update_image (void)
cairo_line_to (cr, scale * (10 + pango_units_to_double (logical.x + logical.width)) + 1,
scale * (10 + pango_units_to_double (baseline)) - 0.5);
cairo_stroke (cr);
cairo_set_source_rgba (cr, 1, 0, 0, 1);
cairo_set_source_rgb (cr, 1, 0, 0);
cairo_rectangle (cr,
scale * (10 + pango_units_to_double (pink.x)) + 0.5,
scale * (10 + pango_units_to_double (pink.y)) + 0.5,
@@ -149,8 +172,36 @@ update_image (void)
cairo_stroke (cr);
}
for (int i = 0; i < path->num_data; i += path->data[i].header.length)
{
cairo_path_data_t *data = &path->data[i];
switch (data->header.type)
{
case CAIRO_PATH_CURVE_TO:
data[3].point.x *= scale; data[3].point.y *= scale;
data[2].point.x *= scale; data[2].point.y *= scale;
data[1].point.x *= scale; data[1].point.y *= scale;
break;
case CAIRO_PATH_LINE_TO:
case CAIRO_PATH_MOVE_TO:
data[1].point.x *= scale; data[1].point.y *= scale;
break;
case CAIRO_PATH_CLOSE_PATH:
break;
default:
g_assert_not_reached ();
}
}
cairo_set_source_rgba (cr, 0, 0, 1, outline_alpha);
cairo_move_to (cr, scale * 20 - 0.5, scale * 20 - 0.5);
cairo_append_path (cr, path);
cairo_stroke (cr);
cairo_surface_destroy (surface);
cairo_destroy (cr);
cairo_path_destroy (path);
}
else
{
@@ -158,10 +209,26 @@ update_image (void)
PangoLayoutRun *run;
PangoGlyphInfo *g;
int i, j;
GString *str;
gunichar ch;
if (*text == '\0')
text = " ";
ch = g_utf8_get_char (text);
str = g_string_new ("");
for (i = 0; i < 4; i++)
{
g_string_append_unichar (str, ch);
g_string_append_unichar (str, 0x200c);
}
layout = pango_layout_new (context);
pango_layout_set_font_description (layout, desc);
pango_layout_set_text (layout, "aaaa", -1);
pango_layout_set_text (layout, str->str, -1);
g_string_free (str, TRUE);
pango_layout_get_extents (layout, &ink, &logical);
pango_extents_to_pixels (&logical, NULL);
@@ -176,7 +243,7 @@ update_image (void)
cairo_set_source_rgb (cr, 0, 0, 0);
for (i = 0; i < 4; i++)
{
g = &(run->glyphs->glyphs[i]);
g = &(run->glyphs->glyphs[2*i]);
g->geometry.width = PANGO_UNITS_ROUND (g->geometry.width * 3 / 2);
}
@@ -184,7 +251,7 @@ update_image (void)
{
for (i = 0; i < 4; i++)
{
g = &(run->glyphs->glyphs[i]);
g = &(run->glyphs->glyphs[2*i]);
g->geometry.x_offset = i * (PANGO_SCALE / 4);
g->geometry.y_offset = j * (PANGO_SCALE / 4);
}
@@ -203,7 +270,6 @@ update_image (void)
cairo_surface_destroy (surface);
}
gtk_picture_set_pixbuf (GTK_PICTURE (image), pixbuf2);
g_object_unref (pixbuf2);
@@ -211,6 +277,78 @@ update_image (void)
pango_font_description_free (desc);
}
static gboolean fading = FALSE;
static double start_pixel_alpha;
static double end_pixel_alpha;
static double start_outline_alpha;
static double end_outline_alpha;
static gint64 start_time;
static gint64 end_time;
static double
ease_out_cubic (double t)
{
double p = t - 1;
return p * p * p + 1;
}
static gboolean
change_alpha (GtkWidget *widget,
GdkFrameClock *clock,
gpointer user_data)
{
gint64 now = g_get_monotonic_time ();
double t;
t = ease_out_cubic ((now - start_time) / (double) (end_time - start_time));
pixel_alpha = start_pixel_alpha + (end_pixel_alpha - start_pixel_alpha) * t;
outline_alpha = start_outline_alpha + (end_outline_alpha - start_outline_alpha) * t;
update_image ();
if (now >= end_time)
{
fading = FALSE;
return G_SOURCE_REMOVE;
}
return G_SOURCE_CONTINUE;
}
static void
start_alpha_fade (void)
{
gboolean pixels;
gboolean outlines;
if (fading)
return;
pixels = gtk_check_button_get_active (GTK_CHECK_BUTTON (show_pixels));
outlines = gtk_check_button_get_active (GTK_CHECK_BUTTON (show_outlines));
start_pixel_alpha = pixel_alpha;
if (pixels && outlines)
end_pixel_alpha = 0.5;
else if (pixels)
end_pixel_alpha = 1;
else
end_pixel_alpha = 0;
start_outline_alpha = outline_alpha;
if (outlines)
end_outline_alpha = 1.0;
else
end_outline_alpha = 0.0;
start_time = g_get_monotonic_time ();
end_time = start_time + G_TIME_SPAN_SECOND / 2;
fading = TRUE;
gtk_widget_add_tick_callback (window, change_alpha, NULL, NULL);
}
static void
update_buttons (void)
{
@@ -218,20 +356,26 @@ update_buttons (void)
gtk_widget_set_sensitive (down_button, scale > 1);
}
static void
scale_up (void)
static gboolean
scale_up (GtkWidget *widget,
GVariant *args,
gpointer user_data)
{
scale += 1;
update_buttons ();
update_image ();
return TRUE;
}
static void
scale_down (void)
static gboolean
scale_down (GtkWidget *widget,
GVariant *args,
gpointer user_data)
{
scale -= 1;
update_buttons ();
update_image ();
return TRUE;
}
GtkWidget *
@@ -252,20 +396,26 @@ do_fontrendering (GtkWidget *do_widget)
entry = GTK_WIDGET (gtk_builder_get_object (builder, "entry"));
image = GTK_WIDGET (gtk_builder_get_object (builder, "image"));
hinting = GTK_WIDGET (gtk_builder_get_object (builder, "hinting"));
anti_alias = GTK_WIDGET (gtk_builder_get_object (builder, "antialias"));
hint_metrics = GTK_WIDGET (gtk_builder_get_object (builder, "hint_metrics"));
text_radio = GTK_WIDGET (gtk_builder_get_object (builder, "text_radio"));
show_grid = GTK_WIDGET (gtk_builder_get_object (builder, "show_grid"));
show_extents = GTK_WIDGET (gtk_builder_get_object (builder, "show_extents"));
show_pixels = GTK_WIDGET (gtk_builder_get_object (builder, "show_pixels"));
show_outlines = GTK_WIDGET (gtk_builder_get_object (builder, "show_outlines"));
g_signal_connect (up_button, "clicked", G_CALLBACK (scale_up), NULL);
g_signal_connect (down_button, "clicked", G_CALLBACK (scale_down), NULL);
g_signal_connect (entry, "notify::text", G_CALLBACK (update_image), NULL);
g_signal_connect (font_button, "notify::font-desc", G_CALLBACK (update_image), NULL);
g_signal_connect (hinting, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (anti_alias, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (hint_metrics, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (text_radio, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (show_grid, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (show_extents, "notify::active", G_CALLBACK (update_image), NULL);
g_signal_connect (show_pixels, "notify::active", G_CALLBACK (start_alpha_fade), NULL);
g_signal_connect (show_outlines, "notify::active", G_CALLBACK (start_alpha_fade), NULL);
update_image ();

View File

@@ -1,13 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkAdjustment" id="scale_adj">
<property name="upper">24</property>
<property name="step-increment">1</property>
<property name="page-increment">4</property>
</object>
<object class="GtkWindow" id="window">
<property name="default-width">1080</property>
<property name="default-height">430</property>
<property name="default-width">1024</property>
<property name="default-height">768</property>
<child type="titlebar">
<object class="GtkHeaderBar">
<child type="title">
@@ -87,35 +82,62 @@
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Hinting</property>
<property name="xalign">1</property>
<style>
<class name="dim-label"/>
</style>
<object class="GtkCheckButton" id="show_pixels">
<property name="label">Show _Pixels</property>
<property name="use-underline">1</property>
<property name="active">1</property>
<layout>
<property name="column">3</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="hinting">
<property name="active">0</property>
<property name="valign">center</property>
<items>
<item translatable="yes" id="none">None</item>
<item translatable="yes" id="slight">Slight</item>
<item translatable="yes" id="medium">Medium</item>
<item translatable="yes" id="full">Full</item>
</items>
<object class="GtkCheckButton" id="show_outlines">
<property name="label">Show _Outline</property>
<property name="use-underline">1</property>
<layout>
<property name="column">3</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkBox">
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="label">_Hinting</property>
<property name="use-underline">1</property>
<property name="mnemonic-widget">hinting</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="hinting">
<property name="active">0</property>
<property name="valign">center</property>
<items>
<item translatable="yes" id="none">None</item>
<item translatable="yes" id="slight">Slight</item>
<item translatable="yes" id="medium">Medium</item>
<item translatable="yes" id="full">Full</item>
</items>
</object>
</child>
<layout>
<property name="column">4</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkCheckButton" id="hint_metrics">
<property name="label">Hint Metrics</property>
<object class="GtkCheckButton" id="antialias">
<property name="label">_Antialias</property>
<property name="use-underline">1</property>
<property name="active">1</property>
<layout>
<property name="column">4</property>
<property name="row">1</property>
@@ -123,21 +145,31 @@
</object>
</child>
<child>
<object class="GtkCheckButton" id="show_extents">
<property name="label">Show Extents</property>
<property name="active">1</property>
<object class="GtkCheckButton" id="hint_metrics">
<property name="label">Hint _Metrics</property>
<property name="use-underline">1</property>
<layout>
<property name="column">5</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkCheckButton" id="show_extents">
<property name="label">Show _Extents</property>
<property name="use-underline">1</property>
<layout>
<property name="column">6</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkCheckButton" id="show_grid">
<property name="active">1</property>
<property name="label">Show Grid</property>
<property name="label">Show _Grid</property>
<property name="use-underline">1</property>
<layout>
<property name="column">5</property>
<property name="column">6</property>
<property name="row">1</property>
</layout>
</object>
@@ -148,8 +180,19 @@
<style>
<class name="circular"/>
</style>
<child>
<object class="GtkShortcutController">
<property name="scope">managed</property>
<child>
<object class="GtkShortcut">
<property name="trigger">&lt;Control&gt;plus</property>
<property name="action">activate</property>
</object>
</child>
</object>
</child>
<layout>
<property name="column">6</property>
<property name="column">7</property>
<property name="row">0</property>
</layout>
</object>
@@ -160,8 +203,19 @@
<style>
<class name="circular"/>
</style>
<child>
<object class="GtkShortcutController">
<property name="scope">managed</property>
<child>
<object class="GtkShortcut">
<property name="trigger">&lt;Control&gt;minus</property>
<property name="action">activate</property>
</object>
</child>
</object>
</child>
<layout>
<property name="column">6</property>
<property name="column">7</property>
<property name="row">1</property>
</layout>
</object>
@@ -170,7 +224,7 @@
<object class="GtkLabel">
<property name="hexpand">1</property>
<layout>
<property name="column">7</property>
<property name="column">8</property>
</layout>
</object>
</child>

View File

@@ -58,7 +58,7 @@ do_markup (GtkWidget *do_widget)
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_default_size (GTK_WINDOW (window), 450, 450);
gtk_window_set_default_size (GTK_WINDOW (window), 600, 680);
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
stack = gtk_stack_new ();

View File

@@ -1,5 +1,9 @@
Text sizes: <span size="xx-small">tiny</span> <span size="x-small">very small</span> <span size="small">small</span> <span size="medium">normal</span> <span size="large">large</span> <span size="x-large">very large</span> <span size="xx-large">huge</span>
Text sizes: <span size="xx-small">tiny </span><span size="x-small">very small </span><span size="small">small </span><span size="medium">normal </span><span size="large">large </span><span size="x-large">very large </span><span size="xx-large">huge</span>
Text styles: <span style="normal">Normal</span> <span style="italic">Italic</span> <span style="oblique">Olique</span>
Text weights: <span weight="thin">thin</span> <span weight="light">light</span> <span weight="normal">normal</span> <span weight="bold">bold</span> <span weight="ultraheavy">ultraheavy</span>
Text <span color="gray">c<span color="green">o</span>l<span color="tomato">o</span>rs</span> and <span background="pink">backgrounds</span>
@@ -15,6 +19,12 @@ OpenType font features: <span font_desc="sans regular" font_features="dlig=0">fe
Shortcuts: <tt>Monospace</tt> <b>Bold</b> <i>Italic</i> <big>Big</big> <small>Small</small> <u>Underlined</u> <s>Strikethrough</s> Super<sup>script</sup> Sub<sub>script</sub>
<span allow_breaks="false">A</span> hy­phen­ation al­go­rithm is a set of rules, espe­ci­ally one co­di­fied for im­ple­men­tation in a com­pu­ter pro­gram, that de­ci­des at which points a word can be bro­ken over two lines with a hy­phen. For ex­am­ple, a hy­phen­ation al­go­rithm might de­cide that im­peach­ment can be broken as <span allow_breaks="false">impeach‧ment</span> or <span allow_breaks="false">im‧peachment</span> but not <span allow_breaks="false">impe‧achment.</span>
hy­phen­ation al­go­rithm is a <span allow_breaks="false" style="italic">set of rules</span>, espe­ci­ally one co­di­fied for im­ple­men­tation in a com­pu­ter pro­gram, that de­ci­des at which points a word can be bro­ken over two lines with a hy­phen. For ex­am­ple, a hy­phen­ation al­go­rithm might de­cide that im­peach­ment can be broken as impeach‧ment or im‧peachment but not impe‧achment.
<span insert_hyphens="false">one/two three/four five/six seven/eight nine/ten</span>
<span line_height='1.33'>Line height: This is an example of widely spaced text. It was achieved by setting the line-height factor to 1.33. You can set the line-height factor to any value between 0 and 10.
Note that the line height affects the spacing between paragraphs as well as between the wrapped lines inside a paragraph.</span>
Transforms: <span text_transform='uppercase'>straße</span> <span text_transform='capitalize'>up, up and away</span>

View File

@@ -67,6 +67,7 @@ demos = files([
'paintable_animated.c',
'paintable_emblem.c',
'paintable_mediastream.c',
'paintable_symbolic.c',
'panes.c',
'password_entry.c',
'peg_solitaire.c',

View File

@@ -33,7 +33,7 @@
<developer_name>Matthias Clasen and others</developer_name>
<content_rating type="oars-1.1"/>
<releases>
<release version="@BUILD_VERSION@">
<release version="@BUILD_VERSION@" date="@BUILD_DATE@">
<description>
<p>A new build of GTK.</p>
</description>

View File

@@ -45,26 +45,27 @@ struct _GtkNuclearIconClass
* so that it can be called from all the other demos, too.
*/
void
gtk_nuclear_snapshot (GtkSnapshot *snapshot,
double width,
double height,
double rotation,
gboolean draw_background)
gtk_nuclear_snapshot (GtkSnapshot *snapshot,
const GdkRGBA *foreground,
const GdkRGBA *background,
double width,
double height,
double rotation)
{
#define RADIUS 0.3
cairo_t *cr;
double size;
if (draw_background)
gtk_snapshot_append_color (snapshot,
&(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 },
&GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_append_color (snapshot,
background,
&GRAPHENE_RECT_INIT (0, 0, width, height));
size = MIN (width, height);
cr = gtk_snapshot_append_cairo (snapshot,
&GRAPHENE_RECT_INIT ((width - size) / 2.0,
(height - size) / 2.0,
size, size));
gdk_cairo_set_source_rgba (cr, foreground);
cairo_translate (cr, width / 2.0, height / 2.0);
cairo_scale (cr, size, size);
cairo_rotate (cr, rotation);
@@ -94,9 +95,10 @@ gtk_nuclear_icon_snapshot (GdkPaintable *paintable,
*/
gtk_nuclear_snapshot (snapshot,
&(GdkRGBA) { 0, 0, 0, 1 }, /* black */
&(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 }, /* yellow */
width, height,
nuclear->rotation,
TRUE);
nuclear->rotation);
}
static GdkPaintableFlags

View File

@@ -4,10 +4,11 @@
#include <gtk/gtk.h>
void gtk_nuclear_snapshot (GtkSnapshot *snapshot,
const GdkRGBA *foreground,
const GdkRGBA *background,
double width,
double height,
double rotation,
gboolean draw_background);
double rotation);
GdkPaintable * gtk_nuclear_icon_new (double rotation);
GdkPaintable * gtk_nuclear_animation_new (gboolean draw_background);

View File

@@ -65,9 +65,12 @@ gtk_nuclear_animation_snapshot (GdkPaintable *paintable,
/* We call the function from the previous example here. */
gtk_nuclear_snapshot (snapshot,
&(GdkRGBA) { 0, 0, 0, 1 }, /* black */
nuclear->draw_background
? &(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 } /* yellow */
: &(GdkRGBA) { 0, 0, 0, 0 }, /* transparent */
width, height,
2 * G_PI * nuclear->progress / MAX_PROGRESS,
nuclear->draw_background);
2 * G_PI * nuclear->progress / MAX_PROGRESS);
}
static GdkPaintable *

View File

@@ -73,9 +73,10 @@ gtk_nuclear_media_stream_snapshot (GdkPaintable *paintable,
/* We call the function from the previous example here. */
gtk_nuclear_snapshot (snapshot,
&(GdkRGBA) { 0, 0, 0, 1 }, /* black */
&(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 }, /* yellow */
width, height,
2 * G_PI * nuclear->progress / DURATION,
TRUE);
2 * G_PI * nuclear->progress / DURATION);
}
static GdkPaintable *

View File

@@ -0,0 +1,208 @@
/* Paintable/Symbolic Paintable
*
* GdkPaintables can be made to follow the theme's colors. GTK calls
* icons that do this symbolic icons, paintables that want to have
* the same effect can implement the GtkSymbolicPaintable interface.
*
* We will adapt the original paintable example by adding the ability
* to recolor the paintable based on the symbolic colors.
*/
#include <gtk/gtk.h>
#include "paintable.h"
static GtkWidget *window = NULL;
/* First, add the boilerplate for the object itself.
* This part would normally go in the header.
*/
#define GTK_TYPE_NUCLEAR_SYMBOLIC (gtk_nuclear_symbolic_get_type ())
G_DECLARE_FINAL_TYPE (GtkNuclearSymbolic, gtk_nuclear_symbolic, GTK, NUCLEAR_SYMBOLIC, GObject)
/* Declare a few warning levels, so we can pick colors based on them */
typedef enum
{
WARNING_NONE,
WARNING_ALERT,
WARNING_EMERGENCY
} WarningLevel;
/* Declare the struct. */
struct _GtkNuclearSymbolic
{
GObject parent_instance;
WarningLevel warning_level;
};
struct _GtkNuclearSymbolicClass
{
GObjectClass parent_class;
};
/* Add a function to draw the nuclear icon in the given colors */
static void
gtk_nuclear_symbolic_snapshot_symbolic (GtkSymbolicPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height,
const GdkRGBA *colors,
gsize n_colors)
{
GtkNuclearSymbolic *self = GTK_NUCLEAR_SYMBOLIC (paintable);
static const GdkRGBA transparent = { 0, };
const GdkRGBA *bg_color;
/* select the right background color from the warning level */
switch (self->warning_level)
{
case WARNING_NONE:
bg_color = &transparent;
break;
case WARNING_ALERT:
bg_color = &colors[GTK_SYMBOLIC_COLOR_WARNING];
break;
case WARNING_EMERGENCY:
bg_color = &colors[GTK_SYMBOLIC_COLOR_ERROR];
break;
default:
/* This should never happen, but we better do defensive coding
* with this critical icon */
g_assert_not_reached ();
bg_color = &transparent;
break;
}
/* Draw the icon with the selected warning color */
gtk_nuclear_snapshot (snapshot,
&colors[GTK_SYMBOLIC_COLOR_FOREGROUND],
bg_color,
width, height,
0);
}
static void
gtk_nuclear_symbolic_symbolic_paintable_init (GtkSymbolicPaintableInterface *iface)
{
iface->snapshot_symbolic = gtk_nuclear_symbolic_snapshot_symbolic;
}
/* We need to implement the functionality required by the GdkPaintable interface */
static void
gtk_nuclear_symbolic_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
/* Calling this function without passing a color is a neat trick
* to make GTK use default colors and otherwise forward the call
* to the snapshotting function above.
*/
gtk_symbolic_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (paintable),
snapshot,
width, height,
NULL, 0);
}
static GdkPaintableFlags
gtk_nuclear_symbolic_get_flags (GdkPaintable *paintable)
{
/* This image has a static size, but the contents may change:
* We draw different things when the warning level changes.
*/
return GDK_PAINTABLE_STATIC_SIZE;
}
static void
gtk_nuclear_symbolic_paintable_init (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_nuclear_symbolic_snapshot;
iface->get_flags = gtk_nuclear_symbolic_get_flags;
}
/* When defining the GType, we need to implement bot the GdkPaintable
* and the GtkSymbolicPaintable interface */
G_DEFINE_TYPE_WITH_CODE (GtkNuclearSymbolic, gtk_nuclear_symbolic, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_nuclear_symbolic_paintable_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_SYMBOLIC_PAINTABLE,
gtk_nuclear_symbolic_symbolic_paintable_init))
static void
gtk_nuclear_symbolic_class_init (GtkNuclearSymbolicClass *klass)
{
}
static void
gtk_nuclear_symbolic_init (GtkNuclearSymbolic *nuclear)
{
}
/* And finally, we add the simple constructor we declared in the header. */
GdkPaintable *
gtk_nuclear_symbolic_new (void)
{
return g_object_new (GTK_TYPE_NUCLEAR_SYMBOLIC, NULL);
}
/* Add some fun feature to the button */
static void
nuclear_button_clicked (GtkButton *button,
GtkNuclearSymbolic *nuclear)
{
if (nuclear->warning_level >= WARNING_EMERGENCY)
{
/* On maximum warning level, reset the warning */
nuclear->warning_level = WARNING_NONE;
/* And sometimes (but not always to confuse people)
* close the window.
*/
if (g_random_boolean ())
gtk_window_close (GTK_WINDOW (window));
}
else
{
/* Otherwise just increase the warning level */
nuclear->warning_level++;
}
/* Don't forget to emit the signal causing the paintable to redraw.
* Changing the warning level changes the background color after all.
*/
gdk_paintable_invalidate_contents (GDK_PAINTABLE (nuclear));
}
GtkWidget *
do_paintable_symbolic (GtkWidget *do_widget)
{
GdkPaintable *nuclear;
GtkWidget *image, *button;
if (!window)
{
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Don't click!");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
button = gtk_button_new ();
gtk_window_set_child (GTK_WINDOW (window), button);
nuclear = gtk_nuclear_symbolic_new ();
image = gtk_image_new_from_paintable (nuclear);
gtk_button_set_child (GTK_BUTTON (button), image);
g_signal_connect (button, "clicked", G_CALLBACK (nuclear_button_clicked), nuclear);
g_object_unref (nuclear);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}

View File

@@ -32,7 +32,7 @@
<developer_name>Matthias Clasen and others</developer_name>
<content_rating type="oars-1.1"/>
<releases>
<release version="@BUILD_VERSION@">
<release version="@BUILD_VERSION@" date="@BUILD_DATE@">
<description>
<p>A new build of GTK.</p>
</description>

View File

@@ -12,9 +12,19 @@ demo_conf_h = declare_dependency(
)
# appdata
today = 'unknown'
date = find_program('date',
required: false)
if date.found()
r = run_command(date, '-I')
if r.returncode() == 0
today = r.stdout().strip()
endif
endif
appdata_config = configuration_data()
appdata_config.set('BUILD_VERSION', meson.project_version())
appdata_config.set('BUILD_DATE', today)
subdir('constraint-editor')
subdir('gtk-demo')

View File

@@ -24,7 +24,6 @@
#include "gtkrendererpaintableprivate.h"
#include "gsk/gskrendernodeparserprivate.h"
#include "gsk/gl/gskglrenderer.h"
#include "gsk/ngl/gsknglrenderer.h"
#ifdef GDK_WINDOWING_BROADWAY
#include "gsk/broadway/gskbroadwayrenderer.h"
@@ -61,7 +60,7 @@ struct _NodeEditorWindow
GtkWidget *renderer_listbox;
GListStore *renderers;
GdkPaintable *paintable;
GskRenderNode *node;
GFileMonitor *file_monitor;
@@ -168,7 +167,6 @@ static void
text_changed (GtkTextBuffer *buffer,
NodeEditorWindow *self)
{
GskRenderNode *node;
char *text;
GBytes *bytes;
GtkTextIter iter;
@@ -179,10 +177,12 @@ text_changed (GtkTextBuffer *buffer,
text_buffer_remove_all_tags (self->text_buffer);
bytes = g_bytes_new_take (text, strlen (text));
g_clear_pointer (&self->node, gsk_render_node_unref);
/* If this is too slow, go fix the parser performance */
node = gsk_render_node_deserialize (bytes, deserialize_error_func, self);
self->node = gsk_render_node_deserialize (bytes, deserialize_error_func, self);
g_bytes_unref (bytes);
if (node)
if (self->node)
{
/* XXX: Is this code necessary or can we have API to turn nodes into paintables? */
GtkSnapshot *snapshot;
@@ -191,10 +191,9 @@ text_changed (GtkTextBuffer *buffer,
guint i;
snapshot = gtk_snapshot_new ();
gsk_render_node_get_bounds (node, &bounds);
gsk_render_node_get_bounds (self->node, &bounds);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- bounds.origin.x, - bounds.origin.y));
gtk_snapshot_append_node (snapshot, node);
gsk_render_node_unref (node);
gtk_snapshot_append_node (snapshot, self->node);
paintable = gtk_snapshot_free_to_paintable (snapshot, &bounds.size);
gtk_picture_set_paintable (GTK_PICTURE (self->picture), paintable);
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->renderers)); i++)
@@ -338,15 +337,9 @@ text_view_query_tooltip_cb (GtkWidget *widget,
}
static gboolean
load_file_contents (NodeEditorWindow *self,
GFile *file)
load_bytes (NodeEditorWindow *self,
GBytes *bytes)
{
GBytes *bytes;
bytes = g_file_load_bytes (file, NULL, NULL, NULL);
if (bytes == NULL)
return FALSE;
if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL))
{
g_bytes_unref (bytes);
@@ -362,6 +355,105 @@ load_file_contents (NodeEditorWindow *self,
return TRUE;
}
static gboolean
load_file_contents (NodeEditorWindow *self,
GFile *file)
{
GBytes *bytes;
bytes = g_file_load_bytes (file, NULL, NULL, NULL);
if (bytes == NULL)
return FALSE;
return load_bytes (self, bytes);
}
static GdkContentProvider *
on_picture_drag_prepare_cb (GtkDragSource *source,
double x,
double y,
NodeEditorWindow *self)
{
if (self->node == NULL)
return NULL;
return gdk_content_provider_new_typed (GSK_TYPE_RENDER_NODE, self->node);
}
static void
on_picture_drop_read_done_cb (GObject *source,
GAsyncResult *res,
gpointer data)
{
NodeEditorWindow *self = data;
GOutputStream *stream = G_OUTPUT_STREAM (source);
GdkDrop *drop = g_object_get_data (source, "drop");
GdkDragAction action = 0;
GBytes *bytes;
if (g_output_stream_splice_finish (stream, res, NULL) >= 0)
{
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
if (load_bytes (self, bytes))
action = GDK_ACTION_COPY;
}
g_object_unref (self);
gdk_drop_finish (drop, action);
g_object_unref (drop);
return;
}
static void
on_picture_drop_read_cb (GObject *source,
GAsyncResult *res,
gpointer data)
{
NodeEditorWindow *self = data;
GdkDrop *drop = GDK_DROP (source);
GInputStream *input;
GOutputStream *output;
input = gdk_drop_read_finish (drop, res, NULL, NULL);
if (input == NULL)
{
g_object_unref (self);
gdk_drop_finish (drop, 0);
return;
}
output = g_memory_output_stream_new_resizable ();
g_object_set_data (G_OBJECT (output), "drop", drop);
g_object_ref (drop);
g_output_stream_splice_async (output,
input,
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
G_PRIORITY_DEFAULT,
NULL,
on_picture_drop_read_done_cb,
self);
g_object_unref (output);
g_object_unref (input);
}
static gboolean
on_picture_drop_cb (GtkDropTargetAsync *dest,
GdkDrop *drop,
double x,
double y,
NodeEditorWindow *self)
{
gdk_drop_read_async (drop,
(const char *[2]) { "application/x-gtk-render-node", NULL },
G_PRIORITY_DEFAULT,
NULL,
on_picture_drop_read_cb,
g_object_ref (self));
return TRUE;
}
static void
file_changed_cb (GFileMonitor *monitor,
GFile *file,
@@ -736,6 +828,7 @@ node_editor_window_finalize (GObject *object)
g_array_free (self->errors, TRUE);
g_clear_pointer (&self->node, gsk_render_node_unref);
g_clear_object (&self->renderers);
G_OBJECT_CLASS (node_editor_window_parent_class)->finalize (object);
@@ -778,12 +871,9 @@ node_editor_window_realize (GtkWidget *widget)
NULL,
"Default");
#endif
node_editor_window_add_renderer (self,
gsk_gl_renderer_new (),
"OpenGL");
node_editor_window_add_renderer (self,
gsk_ngl_renderer_new (),
"NGL");
"OpenGL");
#ifdef GDK_RENDERING_VULKAN
node_editor_window_add_renderer (self,
gsk_vulkan_renderer_new (),
@@ -848,6 +938,8 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
gtk_widget_class_bind_template_callback (widget_class, testcase_save_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, testcase_name_entry_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, dark_mode_cb);
gtk_widget_class_bind_template_callback (widget_class, on_picture_drag_prepare_cb);
gtk_widget_class_bind_template_callback (widget_class, on_picture_drop_cb);
}
static GtkWidget *

View File

@@ -200,6 +200,19 @@
<property name="can-shrink">0</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkDragSource">
<property name="actions">copy</property>
<signal name="prepare" handler="on_picture_drag_prepare_cb" swapped="no"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkDropTargetAsync">
<property name="actions">copy</property>
<property name="formats">application/x-gtk-render-node</property>
<signal name="drop" handler="on_picture_drop_cb" swapped="no"/>
</object>
</child>
</object>

View File

@@ -34,7 +34,7 @@
<developer_name>Matthias Clasen and others</developer_name>
<content_rating type="oars-1.1"/>
<releases>
<release version="@BUILD_VERSION@">
<release version="@BUILD_VERSION@" date="@BUILD_DATE@">
<description>
<p>A new build of GTK.</p>
</description>

View File

@@ -162,6 +162,8 @@ done with
|caret-color|[CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#caret-color) | CSS allows an auto value |
|-gtk-secondary-caret-color|[Color](https://www.w3.org/TR/css-color-3/#valuea-def-color) | used for the secondary caret in bidirectional text |
|letter-spacing| [CSS Text Level 3](https://www.w3.org/TR/css3-text/#letter-spacing) | |
|text-transform| [CSS Text Level 3](https://www.w3.org/TR/css-text-3/#text-transform-property) | CSS allows full-width and full-size-kana. Since 4.6 |
|line-height| [CSS Inline Layout Level 3](https://www.w3.org/TR/css-inline-3/#line-height-property) | Since 4.6 |
|text-decoration-line| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-line-property) | |
|text-decoration-color| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-color-property) | |
|text-decoration-style| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-style-property) | CSS allows dashed and dotted |

View File

@@ -1332,18 +1332,6 @@ pointer coordinates as inout arguments any more, but as normal in ones.
See: [method@Gtk.TreeView.get_tooltip_context], [method@Gtk.IconView.get_tooltip_context]
### Adapt to GtkPopover changes
In GTK 3, a `GtkPopover` could be attached to any widget, using the `relative-to`
property. This is no longer possible in GTK 4. The parent widget has to be aware
of its popover children, and manage their size allocation. Therefore, only widgets
with dedicated popover support can have them, such as [class@Gtk.MenuButton] or
[class@Gtk.PopoverMenuBar].
If you want to make a custom widget that has an attached popover, you need to call
[method@Gtk.Popover.present] in your [vfunc@Gtk.Widget.size_allocate] vfunc, in order
to update the positioning of the popover.
### Stop using GtkFileChooserButton
The `GtkFileChooserButton` widget was removed, due to its shortcomings in

View File

@@ -7,10 +7,6 @@
struct _ExampleAppPrefs
{
GtkDialog parent;
GSettings *settings;
GtkWidget *font;
GtkWidget *transition;
};
G_DEFINE_TYPE (ExampleAppPrefs, example_app_prefs, GTK_TYPE_DIALOG)
@@ -19,41 +15,20 @@ static void
example_app_prefs_init (ExampleAppPrefs *prefs)
{
gtk_widget_init_template (GTK_WIDGET (prefs));
prefs->settings = g_settings_new ("org.gtk.exampleapp");
g_settings_bind (prefs->settings, "font",
prefs->font, "font",
G_SETTINGS_BIND_DEFAULT);
g_settings_bind (prefs->settings, "transition",
prefs->transition, "active-id",
G_SETTINGS_BIND_DEFAULT);
}
static void
example_app_prefs_dispose (GObject *object)
{
ExampleAppPrefs *prefs;
prefs = EXAMPLE_APP_PREFS (object);
g_clear_object (&prefs->settings);
G_OBJECT_CLASS (example_app_prefs_parent_class)->dispose (object);
}
static void
example_app_prefs_class_init (ExampleAppPrefsClass *class)
{
G_OBJECT_CLASS (class)->dispose = example_app_prefs_dispose;
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/org/gtk/exampleapp/prefs.ui");
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppPrefs, font);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppPrefs, transition);
}
ExampleAppPrefs *
example_app_prefs_new (ExampleAppWindow *win)
{
return g_object_new (EXAMPLE_APP_PREFS_TYPE, "transient-for", win, "use-header-bar", TRUE, NULL);
return g_object_new (EXAMPLE_APP_PREFS_TYPE,
"transient-for", win,
"use-header-bar", TRUE,
NULL);
}

View File

@@ -28,6 +28,7 @@
</child>
<child>
<object class="GtkFontButton" id="font">
<property name="font" bind-settings-schema="org.gtk.exampleapp" bind-settings-key="font"/>
<layout>
<property name="column">1</property>
<property name="row">0</property>
@@ -48,6 +49,7 @@
</child>
<child>
<object class="GtkComboBoxText" id="transition">
<property name="active-id" bind-settings-schema="org.gtk.exampleapp" bind-settings-key="transition"/>
<items>
<item translatable="yes" id="none">None</item>
<item translatable="yes" id="crossfade">Fade</item>

View File

@@ -214,7 +214,7 @@ _gdk_broadway_display_open (const char *display_name)
broadway_display->server = _gdk_broadway_server_new (display, display_name, &error);
if (broadway_display->server == NULL)
{
GDK_NOTE (MISC, g_message ("Unable to init Broadway server: %s\n", error->message));
g_printerr ("Unable to init Broadway server: %s\n", error->message);
g_error_free (error);
return NULL;
}

View File

@@ -553,6 +553,8 @@ gdk_content_deserialize_async (GInputStream *stream,
Deserializer *deserializer;
g_return_if_fail (G_IS_INPUT_STREAM (stream));
g_return_if_fail (mime_type != NULL);
g_return_if_fail (G_TYPE_IS_VALUE_TYPE (type));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback != NULL);
@@ -591,7 +593,10 @@ gdk_content_deserialize_finish (GAsyncResult *result,
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (result), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
deserializer = GDK_CONTENT_DESERIALIZER (result);
g_return_val_if_fail (G_VALUE_HOLDS (value, G_VALUE_TYPE (&deserializer->value)), FALSE);
if (G_VALUE_TYPE (value) == 0)
g_value_init (value, G_VALUE_TYPE (&deserializer->value));
else
g_return_val_if_fail (G_VALUE_HOLDS (value, G_VALUE_TYPE (&deserializer->value)), FALSE);
if (deserializer->error)
{
@@ -928,13 +933,11 @@ init (void)
if (!g_get_charset (&charset))
{
char *mime = g_strdup_printf ("text/plain;charset=%s", charset);
gdk_content_register_deserializer (mime,
G_TYPE_STRING,
string_deserializer,
(gpointer) charset,
g_free);
g_free (mime);
}
gdk_content_register_deserializer ("text/plain",
G_TYPE_STRING,

View File

@@ -1233,8 +1233,6 @@ gdk_display_init_gl (GdkDisplay *self)
*/
priv->gl_context = context;
gdk_gl_backend_use (GDK_GL_CONTEXT_GET_CLASS (context)->backend_type);
gdk_profiler_end_mark (before, "initialize OpenGL", NULL);
}

View File

@@ -75,13 +75,11 @@
#include "config.h"
#include "gdkglcontextprivate.h"
#include "gdkdebug.h"
#include "gdkdisplayprivate.h"
#include "gdkinternals.h"
#include "gdkintl.h"
#include "gdkmemorytextureprivate.h"
#include "gdkinternals.h"
#include "gdkintl.h"
#include "gdk-private.h"
#ifdef GDK_WINDOWING_WIN32
@@ -1337,68 +1335,3 @@ gdk_gl_context_use_es_bgra (GdkGLContext *context)
return FALSE;
}
static GdkGLBackend the_gl_backend_type = GDK_GL_NONE;
static const char *gl_backend_names[] = {
[GDK_GL_NONE] = "No GL (You should never read this)",
[GDK_GL_EGL] = "EGL",
[GDK_GL_GLX] = "X11 GLX",
[GDK_GL_WGL] = "Windows WGL",
[GDK_GL_CGL] = "Apple CGL"
};
/*<private>
* gdk_gl_backend_can_be_used:
* @backend_type: Type of backend to check
* @error: Return location for an error
*
* Checks if this backend type can be used. When multiple displays
* are opened that use different GL backends, conflicts can arise,
* so this function checks that all displays use compatible GL
* backends.
*
* Returns: %TRUE if the backend can still be used
*/
gboolean
gdk_gl_backend_can_be_used (GdkGLBackend backend_type,
GError **error)
{
if (the_gl_backend_type == GDK_GL_NONE ||
the_gl_backend_type == backend_type)
return TRUE;
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
/* translators: This is about OpenGL backend names, like
* "Trying to use X11 GLX, but EGL is already in use" */
_("Trying to use %s, but %s is already in use"),
gl_backend_names[backend_type],
gl_backend_names[the_gl_backend_type]);
return FALSE;
}
/*<private>
* gdk_gl_backend_use:
* @backend_type: Type of backend
*
* Ensures that the backend in use is the given one. If another backend
* is already in use, this function will abort the program. It should
* have previously checked via gdk_gl_backend_can_be_used().
**/
void
gdk_gl_backend_use (GdkGLBackend backend_type)
{
/* Check that the context class is properly initializing its backend type */
g_assert (backend_type != GDK_GL_NONE);
if (the_gl_backend_type == GDK_GL_NONE)
{
the_gl_backend_type = backend_type;
/* This is important!!!11eleven
* (But really: How do I print a message in 2 categories?) */
GDK_NOTE (OPENGL, g_print ("Using OpenGL backend %s\n", gl_backend_names[the_gl_backend_type]));
GDK_NOTE (MISC, g_message ("Using Opengl backend %s", gl_backend_names[the_gl_backend_type]));
}
g_assert (the_gl_backend_type == backend_type);
}

View File

@@ -34,14 +34,6 @@ G_BEGIN_DECLS
#define GDK_EGL_MIN_VERSION_MAJOR (1)
#define GDK_EGL_MIN_VERSION_MINOR (4)
typedef enum {
GDK_GL_NONE = 0,
GDK_GL_EGL,
GDK_GL_GLX,
GDK_GL_WGL,
GDK_GL_CGL
} GdkGLBackend;
#define GDK_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_GL_CONTEXT, GdkGLContextClass))
#define GDK_IS_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_GL_CONTEXT))
#define GDK_GL_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_GL_CONTEXT, GdkGLContextClass))
@@ -60,8 +52,6 @@ struct _GdkGLContextClass
{
GdkDrawContextClass parent_class;
GdkGLBackend backend_type;
gboolean (* realize) (GdkGLContext *context,
GError **error);
@@ -96,10 +86,6 @@ typedef struct {
guint use_es : 1;
} GdkGLContextPaintData;
gboolean gdk_gl_backend_can_be_used (GdkGLBackend backend_type,
GError **error);
void gdk_gl_backend_use (GdkGLBackend backend_type);
GdkGLContext * gdk_gl_context_new_for_surface (GdkSurface *surface);
void gdk_gl_context_set_is_legacy (GdkGLContext *context,

View File

@@ -94,6 +94,14 @@
*/
#define GDK_VERSION_4_4 (G_ENCODE_VERSION (4, 4))
/**
* GDK_VERSION_4_6:
*
* A macro that evaluates to the 4.6 version of GDK, in a format
* that can be used by the C pre-processor.
*/
#define GDK_VERSION_4_6 (G_ENCODE_VERSION (4, 6))
/* evaluates to the current stable version; for development cycles,
* this means the next stable target, with a hard backstop to the
@@ -221,4 +229,18 @@
# define GDK_DEPRECATED_IN_4_4_FOR(f) _GDK_EXTERN
#endif
#if GDK_VERSION_MAX_ALLOWED < GDK_VERSION_4_6
# define GDK_AVAILABLE_IN_4_6 GDK_UNAVAILABLE(4, 6)
#else
# define GDK_AVAILABLE_IN_4_6 _GDK_EXTERN
#endif
#if GDK_VERSION_MIN_REQUIRED >= GDK_VERSION_4_6
# define GDK_DEPRECATED_IN_4_6 GDK_DEPRECATED
# define GDK_DEPRECATED_IN_4_6_FOR(f) GDK_DEPRECATED_FOR(f)
#else
# define GDK_DEPRECATED_IN_4_6 _GDK_EXTERN
# define GDK_DEPRECATED_IN_4_6_FOR(f) _GDK_EXTERN
#endif
#endif /* __GDK_VERSION_MACROS_H__ */

View File

@@ -641,9 +641,6 @@ static GdkGLContext *
gdk_macos_display_init_gl (GdkDisplay *display,
GError **error)
{
if (!gdk_gl_backend_can_be_used (GDK_GL_CGL, error))
return FALSE;
return g_object_new (GDK_TYPE_MACOS_GL_CONTEXT,
"display", display,
NULL);

View File

@@ -514,11 +514,7 @@ gdk_macos_gl_context_class_init (GdkMacosGLContextClass *klass)
draw_context_class->surface_resized = gdk_macos_gl_context_surface_resized;
gl_class->get_damage = gdk_macos_gl_context_get_damage;
gl_class->clear_current = gdk_macos_gl_context_clear_current;
gl_class->make_current = gdk_macos_gl_context_make_current;
gl_class->realize = gdk_macos_gl_context_real_realize;
gl_class->backend_type = GDK_GL_CGL;
}
static void

View File

@@ -807,7 +807,7 @@ _gdk_macos_surface_acquire_context (GdkMacosSurface *self,
scale = CGSizeMake (1.0, 1.0);
scale = CGContextConvertSizeToDeviceSpace (cg_context, scale);
CGContextScaleCTM (cg_context, 1.0 / fabs (scale.width), 1.0 / fabs (scale.height));
CGContextScaleCTM (cg_context, 1.0 / scale.width, 1.0 / scale.height);
}
return cg_context;

View File

@@ -358,8 +358,6 @@ gdk_wayland_gl_context_class_init (GdkWaylandGLContextClass *klass)
context_class->make_current = gdk_wayland_gl_context_make_current;
context_class->clear_current = gdk_wayland_gl_context_clear_current;
context_class->get_damage = gdk_wayland_gl_context_get_damage;
context_class->backend_type = GDK_GL_EGL;
}
static void
@@ -477,9 +475,6 @@ gdk_wayland_display_init_gl (GdkDisplay *display,
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
G_GNUC_UNUSED gint64 start_time2;
if (!gdk_gl_backend_can_be_used (GDK_GL_EGL, error))
return FALSE;
if (!epoxy_has_egl ())
{
gboolean sandboxed = gdk_running_in_sandbox ();

View File

@@ -3009,7 +3009,6 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
impl->has_uncommitted_ack_configure = FALSE;
impl->input_region_dirty = TRUE;
impl->opaque_region_dirty = TRUE;
unset_transient_for_exported (surface);

View File

@@ -1150,8 +1150,6 @@ static gboolean
gdk_win32_display_init_gl_backend (GdkDisplay *display,
GError **error)
{
gboolean result = FALSE;
#ifdef GDK_WIN32_ENABLE_EGL
if (GDK_DISPLAY_DEBUG_CHECK (display, GL_EGL))
return gdk_win32_display_init_egl (display, error);
@@ -1163,17 +1161,17 @@ gdk_win32_display_init_gl_backend (GdkDisplay *display,
* as WGL is the more tried-and-tested configuration.
*/
result = gdk_win32_display_init_wgl (display, error);
if (gdk_win32_display_init_wgl (display, error))
return TRUE;
g_clear_error (error);
#ifdef GDK_WIN32_ENABLE_EGL
if (!result)
{
g_clear_error (error);
result = gdk_win32_display_init_egl (display, error);
}
if (gdk_win32_display_init_egl (display, error))
return TRUE;
#endif
g_clear_error (error);
return result;
return gdk_win32_display_init_wgl (display, error);
}
static GdkGLContext *

View File

@@ -2081,8 +2081,8 @@ gdk_dnd_handle_motion_event (GdkDrag *drag,
API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id,
WM_MOUSEMOVE,
key_state,
MAKELPARAM (x_root * drag_win32->scale - _gdk_offset_x,
y_root * drag_win32->scale - _gdk_offset_y)));
MAKELPARAM (x * drag_win32->scale,
y * drag_win32->scale)));
return TRUE;
}

View File

@@ -426,31 +426,6 @@ set_source_actions_helper (GdkDrop *drop,
return actions;
}
/* Utility function to translate win32 screen coordinates to
* client coordinates (i.e. relative to the surface origin)
*
* Note that input is expected to be:
* a) NOT scaled by dpi_scale
* b) NOT translated by the GDK screen offset (gdk_offset_x / y)
*
* This utility function preserves subpixel precision
*/
static void
unscaled_screen_to_client (GdkSurface* surface,
double screen_x,
double screen_y,
double *client_x,
double *client_y)
{
POINT client_origin;
client_origin.x = 0;
client_origin.y = 0;
ClientToScreen (GDK_SURFACE_HWND (surface), &client_origin);
*client_x = screen_x - client_origin.x;
*client_y = screen_y - client_origin.y;
}
/* The pdwEffect here initially points
* to a DWORD that contains the value of dwOKEffects argument in DoDragDrop,
* i.e. the drag action that the drag source deems acceptable.
@@ -471,8 +446,6 @@ idroptarget_dragenter (LPDROPTARGET This,
GdkDisplay *display;
int pt_x;
int pt_y;
double x = 0.0;
double y = 0.0;
GdkDrag *drag;
GdkDragAction source_actions;
GdkDragAction dest_actions;
@@ -517,12 +490,7 @@ idroptarget_dragenter (LPDROPTARGET This,
set_data_object (&ctx->data_object, pDataObj);
pt_x = pt.x / drop_win32->scale + _gdk_offset_x;
pt_y = pt.y / drop_win32->scale + _gdk_offset_y;
unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
x /= drop_win32->scale;
y /= drop_win32->scale;
gdk_drop_emit_enter_event (drop, TRUE, x, y, GDK_CURRENT_TIME);
gdk_drop_emit_enter_event (drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
drop_win32->last_key_state = grfKeyState;
drop_win32->last_x = pt_x;
drop_win32->last_y = pt_y;
@@ -577,14 +545,7 @@ idroptarget_dragover (LPDROPTARGET This,
pt_y != drop_win32->last_y ||
grfKeyState != drop_win32->last_key_state)
{
double x = 0.0;
double y = 0.0;
unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
x /= drop_win32->scale;
y /= drop_win32->scale;
gdk_drop_emit_motion_event (ctx->drop, TRUE, x, y, GDK_CURRENT_TIME);
gdk_drop_emit_motion_event (ctx->drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
drop_win32->last_key_state = grfKeyState;
drop_win32->last_x = pt_x;
drop_win32->last_y = pt_y;
@@ -627,8 +588,6 @@ idroptarget_drop (LPDROPTARGET This,
GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (ctx->drop);
int pt_x = pt.x / drop_win32->scale + _gdk_offset_x;
int pt_y = pt.y / drop_win32->scale + _gdk_offset_y;
double x = 0.0;
double y = 0.0;
GdkDragAction dest_action;
GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
@@ -648,12 +607,7 @@ idroptarget_drop (LPDROPTARGET This,
grfKeyState);
drop_win32->drop_finished = FALSE;
unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
x /= drop_win32->scale;
y /= drop_win32->scale;
gdk_drop_emit_drop_event (ctx->drop, TRUE, x, y, GDK_CURRENT_TIME);
gdk_drop_emit_drop_event (ctx->drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
while (!drop_win32->drop_finished)
g_main_context_iteration (NULL, FALSE);

View File

@@ -256,9 +256,6 @@ gdk_win32_display_init_egl (GdkDisplay *display,
int best_idx = 0;
EGLDisplay egl_disp;
if (!gdk_gl_backend_can_be_used (GDK_GL_EGL, error))
return FALSE;
if (display_win32->egl_disp != EGL_NO_DISPLAY)
return TRUE;
@@ -491,8 +488,6 @@ gdk_win32_gl_context_egl_class_init (GdkWin32GLContextClass *klass)
GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS(klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
context_class->backend_type = GDK_GL_EGL;
context_class->realize = gdk_win32_gl_context_egl_realize;
context_class->make_current = gdk_win32_gl_context_egl_make_current;
context_class->clear_current = gdk_win32_gl_context_egl_clear_current;

View File

@@ -265,9 +265,6 @@ gdk_win32_display_init_wgl (GdkDisplay *display,
GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
HDC hdc;
if (!gdk_gl_backend_can_be_used (GDK_GL_WGL, error))
return FALSE;
if (display_win32->wgl_pixel_format != 0)
return TRUE;
@@ -291,6 +288,7 @@ gdk_win32_display_init_wgl (GdkDisplay *display,
return FALSE;
}
display_win32->wgl_pixel_format = best_idx;
display_win32->gl_version = epoxy_gl_version ();
/* We must have OpenGL/WGL 2.0 or later, or have the GL_ARB_shader_objects extension */
@@ -309,8 +307,6 @@ gdk_win32_display_init_wgl (GdkDisplay *display,
}
}
display_win32->wgl_pixel_format = best_idx;
display_win32->hasWglARBCreateContext =
epoxy_has_wgl_extension (hdc, "WGL_ARB_create_context");
display_win32->hasWglEXTSwapControl =
@@ -701,8 +697,6 @@ gdk_win32_gl_context_wgl_class_init (GdkWin32GLContextWGLClass *klass)
GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
context_class->backend_type = GDK_GL_WGL;
context_class->realize = gdk_win32_gl_context_wgl_realize;
context_class->make_current = gdk_win32_gl_context_wgl_make_current;
context_class->clear_current = gdk_win32_gl_context_wgl_clear_current;

View File

@@ -583,6 +583,12 @@ _gdk_win32_display_create_surface (GdkDisplay *display,
if (!title || !*title)
title = "";
/* WS_EX_TRANSPARENT means "try draw this window last, and ignore input".
* It's the last part we're after. We don't want DND indicator to accept
* input, because that will make it a potential drop target, and if it's
* under the mouse cursor, this will kill any DND.
*/
klass = RegisterGdkClass (surface_type);
wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL);
@@ -645,7 +651,6 @@ _gdk_win32_display_create_surface (GdkDisplay *display,
_gdk_win32_surface_enable_transparency (surface);
_gdk_win32_surface_register_dnd (surface);
_gdk_win32_surface_update_style_bits (surface);
g_signal_connect (frame_clock,
"after-paint",
@@ -1836,8 +1841,6 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
RECT rect, before, after;
gboolean was_topmost;
gboolean will_be_topmost;
gboolean was_layered;
gboolean will_be_layered;
HWND insert_after;
UINT flags;
@@ -1852,9 +1855,7 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
AdjustWindowRectEx (&before, old_style, FALSE, old_exstyle);
was_topmost = (old_exstyle & WS_EX_TOPMOST) ? TRUE : FALSE;
was_layered = (old_exstyle & WS_EX_LAYERED) ? TRUE : FALSE;
will_be_topmost = was_topmost;
will_be_layered = was_layered;
old_exstyle &= ~WS_EX_TOPMOST;
@@ -1864,14 +1865,7 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
if (GDK_IS_DRAG_SURFACE (window))
{
new_exstyle |= WS_EX_TOOLWINDOW;
/* WS_EX_LAYERED | WS_EX_TRANSPARENT makes the drag surface behave
* in pointer input passthrough mode, so it doesn't interfere with
* the drag and drop operation.
*/
new_exstyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
will_be_topmost = TRUE;
will_be_layered = TRUE;
}
else
{
@@ -1916,14 +1910,6 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
_gdk_win32_surface_exstyle_to_string (new_exstyle)));
SetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE, new_exstyle);
if (!was_layered && will_be_layered)
{
/* We have to call SetLayeredWindowAttributes when setting the
* WS_EX_LAYERED style anew, otherwise the window won't show up
*/
API_CALL (SetLayeredWindowAttributes, (GDK_SURFACE_HWND (window), 0, 255, LWA_ALPHA));
}
}
AdjustWindowRectEx (&after, new_style, FALSE, new_exstyle);

View File

@@ -642,8 +642,6 @@ gdk_x11_gl_context_egl_class_init (GdkX11GLContextEGLClass *klass)
GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
context_class->backend_type = GDK_GL_EGL;
context_class->realize = gdk_x11_gl_context_egl_realize;
context_class->make_current = gdk_x11_gl_context_egl_make_current;
context_class->clear_current = gdk_x11_gl_context_egl_clear_current;
@@ -672,9 +670,6 @@ gdk_x11_display_init_egl (GdkX11Display *self,
Display *dpy;
int major, minor;
if (!gdk_gl_backend_can_be_used (GDK_GL_EGL, error))
return FALSE;
dpy = gdk_x11_display_get_xdisplay (display);
if (!epoxy_has_egl ())

View File

@@ -692,8 +692,6 @@ gdk_x11_gl_context_glx_class_init (GdkX11GLContextGLXClass *klass)
GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
context_class->backend_type = GDK_GL_GLX;
context_class->realize = gdk_x11_gl_context_glx_realize;
context_class->make_current = gdk_x11_gl_context_glx_make_current;
context_class->clear_current = gdk_x11_gl_context_glx_clear_current;
@@ -907,9 +905,6 @@ gdk_x11_display_init_glx (GdkX11Display *display_x11,
Display *dpy;
int screen_num;
if (!gdk_gl_backend_can_be_used (GDK_GL_GLX, error))
return FALSE;
dpy = gdk_x11_display_get_xdisplay (display);
if (!epoxy_has_glx (dpy))

View File

@@ -1923,13 +1923,8 @@ gdk_x11_surface_update_popups (GdkSurface *parent)
{
GdkX11Surface *popup_impl = l->data;
GdkSurface *popup = GDK_SURFACE (popup_impl);
int new_x, new_y;
if (GDK_SURFACE_DESTROYED (popup))
continue;
new_x = GDK_X11_SURFACE (parent)->abs_x + popup->x;
new_y = GDK_X11_SURFACE (parent)->abs_y + popup->y;
int new_x = GDK_X11_SURFACE (parent)->abs_x + popup->x;
int new_y = GDK_X11_SURFACE (parent)->abs_y + popup->y;
if (new_x != popup_impl->abs_x || new_y != popup_impl->abs_y)
x11_surface_move (popup, new_x, new_y);

View File

@@ -1,317 +0,0 @@
#pragma once
#define SANITY_CHECKS 0
#define rounded_rect_top_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
r->bounds.origin.y, \
r->corner[0].width, r->corner[0].height))
#define rounded_rect_top_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[1].width, \
r->bounds.origin.y, \
r->corner[1].width, r->corner[1].height))
#define rounded_rect_bottom_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[2].width, \
r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
r->corner[2].width, r->corner[2].height))
#define rounded_rect_bottom_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
r->corner[3].width, r->corner[3].height))
#define rounded_rect_corner0(r) rounded_rect_top_left(r)
#define rounded_rect_corner1(r) rounded_rect_top_right(r)
#define rounded_rect_corner2(r) rounded_rect_bottom_right(r)
#define rounded_rect_corner3(r) rounded_rect_bottom_left(r)
#define rounded_rect_corner(r, i) (rounded_rect_corner ##i(r))
#define graphene_size_non_zero(s) (s->width > 0 && s->height > 0)
#define rounded_rect_has_corner(r, i) (r->corner[i].width > 0 && r->corner[i].height > 0)
#define rect_contains_point(r, _x, _y) (_x >= (r)->origin.x && _x <= (r)->origin.x + (r)->size.width && \
_y >= (r)->origin.y && _y <= (r)->origin.y + (r)->size.height)
enum {
NINE_SLICE_TOP_LEFT = 0,
NINE_SLICE_TOP_CENTER = 1,
NINE_SLICE_TOP_RIGHT = 2,
NINE_SLICE_LEFT_CENTER = 3,
NINE_SLICE_CENTER = 4,
NINE_SLICE_RIGHT_CENTER = 5,
NINE_SLICE_BOTTOM_LEFT = 6,
NINE_SLICE_BOTTOM_CENTER = 7,
NINE_SLICE_BOTTOM_RIGHT = 8,
};
#define NINE_SLICE_SIZE 9 /* Hah. */
typedef struct
{
int texture_id;
float x;
float y;
float x2;
float y2;
} TextureRegion;
static inline bool G_GNUC_PURE
slice_is_visible (const cairo_rectangle_int_t *r)
{
return (r->width > 0 && r->height > 0);
}
static inline void
nine_slice_rounded_rect (const GskRoundedRect *rect,
cairo_rectangle_int_t *out_rects)
{
const graphene_point_t *origin = &rect->bounds.origin;
const graphene_size_t *size = &rect->bounds.size;
const int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height,
rect->corner[GSK_CORNER_TOP_RIGHT].height));
const int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
rect->corner[GSK_CORNER_BOTTOM_RIGHT].height));
const int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width,
rect->corner[GSK_CORNER_BOTTOM_RIGHT].width));
const int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->corner[GSK_CORNER_BOTTOM_LEFT].width));
/* Top left */
out_rects[0] = (cairo_rectangle_int_t) {
origin->x, origin->y,
left_width, top_height,
};
/* Top center */
out_rects[1] = (cairo_rectangle_int_t) {
origin->x + size->width / 2.0 - 0.5, origin->y,
1, top_height,
};
/* Top right */
out_rects[2] = (cairo_rectangle_int_t) {
origin->x + size->width - right_width, origin->y,
right_width, top_height
};
/* Left center */
out_rects[3] = (cairo_rectangle_int_t) {
origin->x, origin->y + size->height / 2,
left_width, 1,
};
/* center */
out_rects[4] = (cairo_rectangle_int_t) {
origin->x + size->width / 2.0 - 0.5,
origin->y + size->height / 2.0 - 0.5,
1, 1
};
/* Right center */
out_rects[5] = (cairo_rectangle_int_t) {
origin->x + size->width - right_width,
origin->y + (size->height / 2.0) - 0.5,
right_width,
1,
};
/* Bottom Left */
out_rects[6] = (cairo_rectangle_int_t) {
origin->x, origin->y + size->height - bottom_height,
left_width, bottom_height,
};
/* Bottom center */
out_rects[7] = (cairo_rectangle_int_t) {
origin->x + (size->width / 2.0) - 0.5,
origin->y + size->height - bottom_height,
1, bottom_height,
};
/* Bottom right */
out_rects[8] = (cairo_rectangle_int_t) {
origin->x + size->width - right_width,
origin->y + size->height - bottom_height,
right_width, bottom_height,
};
#if SANITY_CHECKS
g_assert_cmpfloat (size->width, >=, left_width + right_width);
g_assert_cmpfloat (size->height, >=, top_height + bottom_height);
#endif
}
static inline void
nine_slice_grow (cairo_rectangle_int_t *slices,
const int amount)
{
/* top left */
slices[0].x -= amount;
slices[0].y -= amount;
if (amount > slices[0].width)
slices[0].width += amount * 2;
else
slices[0].width += amount;
if (amount > slices[0].height)
slices[0].height += amount * 2;
else
slices[0].height += amount;
/* Top center */
slices[1].y -= amount;
if (amount > slices[1].height)
slices[1].height += amount * 2;
else
slices[1].height += amount;
/* top right */
slices[2].y -= amount;
if (amount > slices[2].width)
{
slices[2].x -= amount;
slices[2].width += amount * 2;
}
else
{
slices[2].width += amount;
}
if (amount > slices[2].height)
slices[2].height += amount * 2;
else
slices[2].height += amount;
slices[3].x -= amount;
if (amount > slices[3].width)
slices[3].width += amount * 2;
else
slices[3].width += amount;
/* Leave Britney^Wcenter alone */
if (amount > slices[5].width)
{
slices[5].x -= amount;
slices[5].width += amount * 2;
}
else
{
slices[5].width += amount;
}
/* Bottom left */
slices[6].x -= amount;
if (amount > slices[6].width)
{
slices[6].width += amount * 2;
}
else
{
slices[6].width += amount;
}
if (amount > slices[6].height)
{
slices[6].y -= amount;
slices[6].height += amount * 2;
}
else
{
slices[6].height += amount;
}
/* Bottom center */
if (amount > slices[7].height)
{
slices[7].y -= amount;
slices[7].height += amount * 2;
}
else
{
slices[7].height += amount;
}
if (amount > slices[8].width)
{
slices[8].x -= amount;
slices[8].width += amount * 2;
}
else
{
slices[8].width += amount;
}
if (amount > slices[8].height)
{
slices[8].y -= amount;
slices[8].height += amount * 2;
}
else
{
slices[8].height += amount;
}
#if SANITY_CHECKS
{
for (int i = 0; i < 9; i ++)
{
g_assert_cmpint (slices[i].x, >=, 0);
g_assert_cmpint (slices[i].y, >=, 0);
g_assert_cmpint (slices[i].width, >=, 0);
g_assert_cmpint (slices[i].height, >=, 0);
}
/* Rows don't overlap */
for (int i = 0; i < 3; i++)
{
g_assert_cmpint (slices[i * 3 + 0].x + slices[i * 3 + 0].width, <, slices[i * 3 + 1].x);
}
}
#endif
}
static inline void
nine_slice_to_texture_coords (const cairo_rectangle_int_t *slices,
const int texture_width,
const int texture_height,
TextureRegion *out_regions)
{
const float fw = (float)texture_width;
const float fh = (float)texture_height;
int i;
for (i = 0; i < 9; i++)
{
out_regions[i] = (TextureRegion) {
0, /* Texture id */
slices[i].x / fw,
1.0 - ((slices[i].y + slices[i].height) / fh),
(slices[i].x + slices[i].width) / fw,
1.0 - (slices[i].y / fh),
};
}
#if SANITY_CHECKS
{
for (i = 0; i < 9; i++)
{
const TextureRegion *r = &out_regions[i];
g_assert_cmpfloat (r->x, >=, 0);
g_assert_cmpfloat (r->x, <=, 1);
g_assert_cmpfloat (r->y, >=, 0);
g_assert_cmpfloat (r->y, <=, 1);
g_assert_cmpfloat (r->x, <, r->x2);
g_assert_cmpfloat (r->y, <, r->y2);
}
}
#endif
}

View File

@@ -1,849 +0,0 @@
#include "config.h"
#include "gskgldriverprivate.h"
#include "gskdebugprivate.h"
#include "gskprofilerprivate.h"
#include "gdk/gdkglcontextprivate.h"
#include "gdk/gdktextureprivate.h"
#include "gdk/gdkgltextureprivate.h"
#include "gdkmemorytextureprivate.h"
#include <gdk/gdk.h>
#include <epoxy/gl.h>
typedef struct {
GLuint fbo_id;
GLuint depth_stencil_id;
} Fbo;
typedef struct {
GLuint texture_id;
int width;
int height;
GLuint min_filter;
GLuint mag_filter;
Fbo fbo;
GdkTexture *user;
guint in_use : 1;
guint permanent : 1;
/* TODO: Make this optional and not for every texture... */
TextureSlice *slices;
guint n_slices;
} Texture;
struct _GskGLDriver
{
GObject parent_instance;
GdkGLContext *gl_context;
GskProfiler *profiler;
struct {
GQuark created_textures;
GQuark reused_textures;
GQuark surface_uploads;
} counters;
Fbo default_fbo;
GHashTable *textures; /* texture_id -> Texture */
GHashTable *pointer_textures; /* pointer -> texture_id */
const Texture *bound_source_texture;
int max_texture_size;
gboolean in_frame : 1;
};
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
static void
upload_gdk_texture (GdkTexture *source_texture,
int target,
int x_offset,
int y_offset,
int width,
int height)
{
cairo_surface_t *surface = NULL;
GdkMemoryFormat data_format;
const guchar *data;
gsize data_stride;
gsize bpp;
g_return_if_fail (source_texture != NULL);
g_return_if_fail (x_offset + width <= gdk_texture_get_width (source_texture));
g_return_if_fail (y_offset + height <= gdk_texture_get_height (source_texture));
/* Note: GdkGLTextures are already handled before we reach this and reused as-is */
if (GDK_IS_MEMORY_TEXTURE (source_texture))
{
GdkMemoryTexture *memory_texture = GDK_MEMORY_TEXTURE (source_texture);
data = gdk_memory_texture_get_data (memory_texture);
data_format = gdk_memory_texture_get_format (memory_texture);
data_stride = gdk_memory_texture_get_stride (memory_texture);
}
else
{
/* Fall back to downloading to a surface */
surface = gdk_texture_download_surface (source_texture);
cairo_surface_flush (surface);
data = cairo_image_surface_get_data (surface);
data_format = GDK_MEMORY_DEFAULT;
data_stride = cairo_image_surface_get_stride (surface);
}
bpp = gdk_memory_format_bytes_per_pixel (data_format);
gdk_gl_context_upload_texture (gdk_gl_context_get_current (),
data + x_offset * bpp + y_offset * data_stride,
width, height, data_stride,
data_format, target);
if (surface)
cairo_surface_destroy (surface);
}
static Texture *
texture_new (void)
{
return g_slice_new0 (Texture);
}
static inline void
fbo_clear (const Fbo *f)
{
if (f->depth_stencil_id != 0)
glDeleteRenderbuffers (1, &f->depth_stencil_id);
glDeleteFramebuffers (1, &f->fbo_id);
}
static void
texture_free (gpointer data)
{
Texture *t = data;
guint i;
if (t->user)
gdk_texture_clear_render_data (t->user);
if (t->fbo.fbo_id != 0)
fbo_clear (&t->fbo);
if (t->texture_id != 0)
{
glDeleteTextures (1, &t->texture_id);
}
else
{
g_assert_cmpint (t->n_slices, >, 0);
for (i = 0; i < t->n_slices; i ++)
glDeleteTextures (1, &t->slices[i].texture_id);
}
g_slice_free (Texture, t);
}
static void
gsk_gl_driver_set_texture_parameters (GskGLDriver *self,
int min_filter,
int mag_filter)
{
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
static void
gsk_gl_driver_finalize (GObject *gobject)
{
GskGLDriver *self = GSK_GL_DRIVER (gobject);
gdk_gl_context_make_current (self->gl_context);
g_clear_pointer (&self->textures, g_hash_table_unref);
g_clear_pointer (&self->pointer_textures, g_hash_table_unref);
g_clear_object (&self->profiler);
if (self->gl_context == gdk_gl_context_get_current ())
gdk_gl_context_clear_current ();
G_OBJECT_CLASS (gsk_gl_driver_parent_class)->finalize (gobject);
}
static void
gsk_gl_driver_class_init (GskGLDriverClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gsk_gl_driver_finalize;
}
static void
gsk_gl_driver_init (GskGLDriver *self)
{
self->textures = g_hash_table_new_full (NULL, NULL, NULL, texture_free);
self->max_texture_size = -1;
#ifdef G_ENABLE_DEBUG
self->profiler = gsk_profiler_new ();
self->counters.created_textures = gsk_profiler_add_counter (self->profiler,
"created_textures",
"Textures created this frame",
TRUE);
self->counters.reused_textures = gsk_profiler_add_counter (self->profiler,
"reused_textures",
"Textures reused this frame",
TRUE);
self->counters.surface_uploads = gsk_profiler_add_counter (self->profiler,
"surface_uploads",
"Texture uploads from surfaces this frame",
TRUE);
#endif
}
GskGLDriver *
gsk_gl_driver_new (GdkGLContext *context)
{
GskGLDriver *self;
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
self = (GskGLDriver *) g_object_new (GSK_TYPE_GL_DRIVER, NULL);
self->gl_context = context;
return self;
}
void
gsk_gl_driver_begin_frame (GskGLDriver *self)
{
g_return_if_fail (GSK_IS_GL_DRIVER (self));
g_return_if_fail (!self->in_frame);
self->in_frame = TRUE;
if (self->max_texture_size < 0)
{
glGetIntegerv (GL_MAX_TEXTURE_SIZE, (GLint *) &self->max_texture_size);
GSK_NOTE (OPENGL, g_message ("GL max texture size: %d", self->max_texture_size));
}
glBindFramebuffer (GL_FRAMEBUFFER, 0);
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, 0);
glActiveTexture (GL_TEXTURE0 + 1);
glBindTexture (GL_TEXTURE_2D, 0);
glBindVertexArray (0);
glUseProgram (0);
glActiveTexture (GL_TEXTURE0);
#ifdef G_ENABLE_DEBUG
gsk_profiler_reset (self->profiler);
#endif
}
gboolean
gsk_gl_driver_in_frame (GskGLDriver *self)
{
return self->in_frame;
}
void
gsk_gl_driver_end_frame (GskGLDriver *self)
{
g_return_if_fail (GSK_IS_GL_DRIVER (self));
g_return_if_fail (self->in_frame);
self->bound_source_texture = NULL;
self->default_fbo.fbo_id = 0;
#ifdef G_ENABLE_DEBUG
GSK_NOTE (OPENGL,
g_message ("Textures created: %" G_GINT64_FORMAT "\n"
" Textures reused: %" G_GINT64_FORMAT "\n"
" Surface uploads: %" G_GINT64_FORMAT,
gsk_profiler_counter_get (self->profiler, self->counters.created_textures),
gsk_profiler_counter_get (self->profiler, self->counters.reused_textures),
gsk_profiler_counter_get (self->profiler, self->counters.surface_uploads)));
#endif
GSK_NOTE (OPENGL,
g_message ("*** Frame end: textures=%d",
g_hash_table_size (self->textures)));
self->in_frame = FALSE;
}
int
gsk_gl_driver_collect_textures (GskGLDriver *self)
{
GHashTableIter iter;
gpointer value_p = NULL;
int old_size;
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
g_return_val_if_fail (!self->in_frame, 0);
old_size = g_hash_table_size (self->textures);
g_hash_table_iter_init (&iter, self->textures);
while (g_hash_table_iter_next (&iter, NULL, &value_p))
{
Texture *t = value_p;
if (t->user || t->permanent)
continue;
if (t->in_use)
{
t->in_use = FALSE;
if (t->fbo.fbo_id != 0)
{
fbo_clear (&t->fbo);
t->fbo.fbo_id = 0;
}
}
else
{
/* Remove from self->pointer_textures. */
/* TODO: Is there a better way for this? */
if (self->pointer_textures)
{
GHashTableIter pointer_iter;
gpointer value;
gpointer p;
g_hash_table_iter_init (&pointer_iter, self->pointer_textures);
while (g_hash_table_iter_next (&pointer_iter, &p, &value))
{
if (GPOINTER_TO_INT (value) == t->texture_id)
{
g_hash_table_iter_remove (&pointer_iter);
break;
}
}
}
g_hash_table_iter_remove (&iter);
}
}
return old_size - g_hash_table_size (self->textures);
}
GdkGLContext *
gsk_gl_driver_get_gl_context (GskGLDriver *self)
{
return self->gl_context;
}
int
gsk_gl_driver_get_max_texture_size (GskGLDriver *self)
{
if (self->max_texture_size < 0)
{
if (gdk_gl_context_get_use_es (self->gl_context))
return 2048;
return 1024;
}
return self->max_texture_size;
}
static Texture *
gsk_gl_driver_get_texture (GskGLDriver *self,
int texture_id)
{
Texture *t;
if (g_hash_table_lookup_extended (self->textures, GINT_TO_POINTER (texture_id), NULL, (gpointer *) &t))
return t;
return NULL;
}
static Texture *
create_texture (GskGLDriver *self,
float fwidth,
float fheight)
{
guint texture_id;
Texture *t;
int width = ceilf (fwidth);
int height = ceilf (fheight);
g_assert (width > 0);
g_assert (height > 0);
if (width > self->max_texture_size ||
height > self->max_texture_size)
{
g_critical ("Texture %d x %d is bigger than supported texture limit of %d; clipping...",
width, height,
self->max_texture_size);
width = MIN (width, self->max_texture_size);
height = MIN (height, self->max_texture_size);
}
glGenTextures (1, &texture_id);
t = texture_new ();
t->texture_id = texture_id;
t->width = width;
t->height = height;
t->min_filter = GL_NEAREST;
t->mag_filter = GL_NEAREST;
t->in_use = TRUE;
g_hash_table_insert (self->textures, GINT_TO_POINTER (texture_id), t);
#ifdef G_ENABLE_DEBUG
gsk_profiler_counter_inc (self->profiler, self->counters.created_textures);
#endif
return t;
}
static void
gsk_gl_driver_release_texture (gpointer data)
{
Texture *t = data;
t->user = NULL;
}
void
gsk_gl_driver_slice_texture (GskGLDriver *self,
GdkTexture *texture,
TextureSlice **out_slices,
guint *out_n_slices)
{
const int max_texture_size = gsk_gl_driver_get_max_texture_size (self) / 4; // XXX Too much?
const int cols = (texture->width / max_texture_size) + 1;
const int rows = (texture->height / max_texture_size) + 1;
int col, row;
int x = 0, y = 0; /* Position in the texture */
TextureSlice *slices;
Texture *tex;
g_assert (texture->width > max_texture_size || texture->height > max_texture_size);
tex = gdk_texture_get_render_data (texture, self);
if (tex != NULL)
{
g_assert (tex->n_slices > 0);
*out_slices = tex->slices;
*out_n_slices = tex->n_slices;
return;
}
slices = g_new0 (TextureSlice, cols * rows);
for (col = 0; col < cols; col ++)
{
const int slice_width = MIN (max_texture_size, texture->width - x);
for (row = 0; row < rows; row ++)
{
const int slice_height = MIN (max_texture_size, texture->height - y);
const int slice_index = (col * rows) + row;
guint texture_id;
glGenTextures (1, &texture_id);
#ifdef G_ENABLE_DEBUG
gsk_profiler_counter_inc (self->profiler, self->counters.created_textures);
#endif
glBindTexture (GL_TEXTURE_2D, texture_id);
gsk_gl_driver_set_texture_parameters (self, GL_NEAREST, GL_NEAREST);
upload_gdk_texture (texture, GL_TEXTURE_2D, x, y, slice_width, slice_height);
#ifdef G_ENABLE_DEBUG
gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
#endif
slices[slice_index].rect = (GdkRectangle){x, y, slice_width, slice_height};
slices[slice_index].texture_id = texture_id;
y += slice_height;
}
y = 0;
x += slice_width;
}
/* Allocate one Texture for the entire thing. */
tex = texture_new ();
tex->width = texture->width;
tex->height = texture->height;
tex->min_filter = GL_NEAREST;
tex->mag_filter = GL_NEAREST;
tex->in_use = TRUE;
tex->slices = slices;
tex->n_slices = cols * rows;
/* Use texture_free as destroy notify here since we are not inserting this Texture
* into self->textures! */
gdk_texture_set_render_data (texture, self, tex, texture_free);
*out_slices = slices;
*out_n_slices = cols * rows;
}
int
gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
GdkTexture *texture,
int min_filter,
int mag_filter)
{
Texture *t;
GdkTexture *downloaded_texture = NULL;
GdkTexture *source_texture;
if (GDK_IS_GL_TEXTURE (texture))
{
GdkGLTexture *gl_texture = (GdkGLTexture *) texture;
GdkGLContext *texture_context = gdk_gl_texture_get_context (gl_texture);
if (gdk_gl_context_is_shared (self->gl_context, texture_context))
{
/* A GL texture from the same GL context is a simple task... */
return gdk_gl_texture_get_id (gl_texture);
}
else
{
cairo_surface_t *surface;
/* In this case, we have to temporarily make the texture's context the current one,
* download its data into our context and then create a texture from it. */
if (texture_context)
gdk_gl_context_make_current (texture_context);
surface = gdk_texture_download_surface (texture);
downloaded_texture = gdk_texture_new_for_surface (surface);
cairo_surface_destroy (surface);
gdk_gl_context_make_current (self->gl_context);
source_texture = downloaded_texture;
}
}
else
{
t = gdk_texture_get_render_data (texture, self);
if (t)
{
if (t->min_filter == min_filter && t->mag_filter == mag_filter)
return t->texture_id;
}
source_texture = texture;
}
t = create_texture (self, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_driver_release_texture))
t->user = texture;
gsk_gl_driver_bind_source_texture (self, t->texture_id);
gsk_gl_driver_init_texture (self,
t->texture_id,
source_texture,
min_filter,
mag_filter);
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, t->texture_id,
"GdkTexture<%p> %d", texture, t->texture_id);
if (downloaded_texture)
g_object_unref (downloaded_texture);
return t->texture_id;
}
static guint
texture_key_hash (gconstpointer v)
{
const GskTextureKey *k = (GskTextureKey *)v;
return GPOINTER_TO_UINT (k->pointer)
+ (guint)(k->scale_x * 100)
+ (guint)(k->scale_y * 100)
+ (guint)k->filter * 2 +
+ (guint)k->pointer_is_child;
}
static gboolean
texture_key_equal (gconstpointer v1, gconstpointer v2)
{
const GskTextureKey *k1 = (GskTextureKey *)v1;
const GskTextureKey *k2 = (GskTextureKey *)v2;
return k1->pointer == k2->pointer &&
k1->scale_x == k2->scale_x &&
k1->scale_y == k2->scale_y &&
k1->filter == k2->filter &&
k1->pointer_is_child == k2->pointer_is_child &&
(!k1->pointer_is_child || graphene_rect_equal (&k1->parent_rect, &k2->parent_rect));
}
int
gsk_gl_driver_get_texture_for_key (GskGLDriver *self,
GskTextureKey *key)
{
int id = 0;
if (G_UNLIKELY (self->pointer_textures == NULL))
self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL);
id = GPOINTER_TO_INT (g_hash_table_lookup (self->pointer_textures, key));
if (id != 0)
{
Texture *t;
t = g_hash_table_lookup (self->textures, GINT_TO_POINTER (id));
if (t != NULL)
t->in_use = TRUE;
}
return id;
}
void
gsk_gl_driver_set_texture_for_key (GskGLDriver *self,
GskTextureKey *key,
int texture_id)
{
GskTextureKey *k;
if (G_UNLIKELY (self->pointer_textures == NULL))
self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL);
k = g_new (GskTextureKey, 1);
*k = *key;
g_hash_table_insert (self->pointer_textures, k, GINT_TO_POINTER (texture_id));
}
int
gsk_gl_driver_create_texture (GskGLDriver *self,
float width,
float height)
{
Texture *t;
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), -1);
t = create_texture (self, width, height);
return t->texture_id;
}
void
gsk_gl_driver_create_render_target (GskGLDriver *self,
int width,
int height,
int min_filter,
int mag_filter,
int *out_texture_id,
int *out_render_target_id)
{
GLuint fbo_id;
Texture *texture;
g_return_if_fail (self->in_frame);
texture = create_texture (self, width, height);
gsk_gl_driver_bind_source_texture (self, texture->texture_id);
gsk_gl_driver_init_texture_empty (self, texture->texture_id, min_filter, mag_filter);
glGenFramebuffers (1, &fbo_id);
glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture_id, 0);
#if 0
if (add_depth_buffer || add_stencil_buffer)
{
glGenRenderbuffersEXT (1, &depth_stencil_buffer_id);
gdk_gl_context_label_object_printf (self->gl_context, GL_RENDERBUFFER, depth_stencil_buffer_id,
"%s buffer for %d", add_depth_buffer ? "Depth" : "Stencil", texture_id);
}
else
depth_stencil_buffer_id = 0;
glBindRenderbuffer (GL_RENDERBUFFER, depth_stencil_buffer_id);
if (add_depth_buffer || add_stencil_buffer)
{
if (add_stencil_buffer)
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, t->width, t->height);
else
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, t->width, t->height);
if (add_depth_buffer)
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depth_stencil_buffer_id);
if (add_stencil_buffer)
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, depth_stencil_buffer_id);
texture->fbo.depth_stencil_id = depth_stencil_buffer_id;
}
#endif
texture->fbo.fbo_id = fbo_id;
g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
glBindFramebuffer (GL_FRAMEBUFFER, self->default_fbo.fbo_id);
*out_texture_id = texture->texture_id;
*out_render_target_id = fbo_id;
}
/* Mark the texture permanent, meaning it won'e be reused by the GLDriver.
* E.g. to store it in some other cache. */
void
gsk_gl_driver_mark_texture_permanent (GskGLDriver *self,
int texture_id)
{
Texture *t = gsk_gl_driver_get_texture (self, texture_id);
g_assert (t != NULL);
t->permanent = TRUE;
}
void
gsk_gl_driver_bind_source_texture (GskGLDriver *self,
int texture_id)
{
Texture *t;
g_return_if_fail (GSK_IS_GL_DRIVER (self));
g_return_if_fail (self->in_frame);
t = gsk_gl_driver_get_texture (self, texture_id);
if (t == NULL)
{
g_critical ("No texture %d found.", texture_id);
return;
}
if (self->bound_source_texture != t)
{
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, t->texture_id);
self->bound_source_texture = t;
}
}
void
gsk_gl_driver_destroy_texture (GskGLDriver *self,
int texture_id)
{
g_return_if_fail (GSK_IS_GL_DRIVER (self));
g_hash_table_remove (self->textures, GINT_TO_POINTER (texture_id));
}
void
gsk_gl_driver_init_texture_empty (GskGLDriver *self,
int texture_id,
int min_filter,
int mag_filter)
{
Texture *t;
g_return_if_fail (GSK_IS_GL_DRIVER (self));
t = gsk_gl_driver_get_texture (self, texture_id);
if (t == NULL)
{
g_critical ("No texture %d found.", texture_id);
return;
}
if (self->bound_source_texture != t)
{
g_critical ("You must bind the texture before initializing it.");
return;
}
t->min_filter = min_filter;
t->mag_filter = mag_filter;
gsk_gl_driver_set_texture_parameters (self, t->min_filter, t->mag_filter);
if (gdk_gl_context_get_use_es (self->gl_context))
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
else
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glBindTexture (GL_TEXTURE_2D, 0);
}
static gboolean
filter_uses_mipmaps (int filter)
{
return filter != GL_NEAREST && filter != GL_LINEAR;
}
void
gsk_gl_driver_init_texture (GskGLDriver *self,
int texture_id,
GdkTexture *texture,
int min_filter,
int mag_filter)
{
Texture *t;
g_return_if_fail (GSK_IS_GL_DRIVER (self));
t = gsk_gl_driver_get_texture (self, texture_id);
if (t == NULL)
{
g_critical ("No texture %d found.", texture_id);
return;
}
if (self->bound_source_texture != t)
{
g_critical ("You must bind the texture before initializing it.");
return;
}
gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter);
upload_gdk_texture (texture, GL_TEXTURE_2D, 0, 0, t->width, t->height);
#ifdef G_ENABLE_DEBUG
gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
#endif
t->min_filter = min_filter;
t->mag_filter = mag_filter;
if (filter_uses_mipmaps (t->min_filter))
glGenerateMipmap (GL_TEXTURE_2D);
}

View File

@@ -1,86 +0,0 @@
#ifndef __GSK_GL_DRIVER_PRIVATE_H__
#define __GSK_GL_DRIVER_PRIVATE_H__
#include <cairo.h>
#include <gdk/gdk.h>
#include <graphene.h>
G_BEGIN_DECLS
#define GSK_TYPE_GL_DRIVER (gsk_gl_driver_get_type ())
G_DECLARE_FINAL_TYPE (GskGLDriver, gsk_gl_driver, GSK, GL_DRIVER, GObject)
typedef struct {
float position[2];
float uv[2];
} GskQuadVertex;
typedef struct {
cairo_rectangle_int_t rect;
guint texture_id;
} TextureSlice;
typedef struct {
gpointer pointer;
float scale_x;
float scale_y;
int filter;
int pointer_is_child;
graphene_rect_t parent_rect; /* Only set if pointer_is_child */
} GskTextureKey;
GskGLDriver * gsk_gl_driver_new (GdkGLContext *context);
GdkGLContext *gsk_gl_driver_get_gl_context (GskGLDriver *driver);
int gsk_gl_driver_get_max_texture_size (GskGLDriver *driver);
void gsk_gl_driver_begin_frame (GskGLDriver *driver);
void gsk_gl_driver_end_frame (GskGLDriver *driver);
gboolean gsk_gl_driver_in_frame (GskGLDriver *driver);
int gsk_gl_driver_get_texture_for_texture (GskGLDriver *driver,
GdkTexture *texture,
int min_filter,
int mag_filter);
int gsk_gl_driver_get_texture_for_key (GskGLDriver *driver,
GskTextureKey *key);
void gsk_gl_driver_set_texture_for_key (GskGLDriver *driver,
GskTextureKey *key,
int texture_id);
int gsk_gl_driver_create_texture (GskGLDriver *driver,
float width,
float height);
void gsk_gl_driver_create_render_target (GskGLDriver *driver,
int width,
int height,
int min_filter,
int mag_filter,
int *out_texture_id,
int *out_render_target_id);
void gsk_gl_driver_mark_texture_permanent (GskGLDriver *self,
int texture_id);
void gsk_gl_driver_bind_source_texture (GskGLDriver *driver,
int texture_id);
void gsk_gl_driver_init_texture_empty (GskGLDriver *driver,
int texture_id,
int min_filter,
int max_filter);
void gsk_gl_driver_init_texture (GskGLDriver *driver,
int texture_id,
GdkTexture *texture,
int min_filter,
int mag_filter);
void gsk_gl_driver_destroy_texture (GskGLDriver *driver,
int texture_id);
int gsk_gl_driver_collect_textures (GskGLDriver *driver);
void gsk_gl_driver_slice_texture (GskGLDriver *self,
GdkTexture *texture,
TextureSlice **out_slices,
guint *out_n_slices);
G_END_DECLS
#endif /* __GSK_GL_DRIVER_PRIVATE_H__ */

View File

@@ -1,397 +0,0 @@
#include "config.h"
#include "gskglglyphcacheprivate.h"
#include "gskgldriverprivate.h"
#include "gskdebugprivate.h"
#include "gskprivate.h"
#include "gskgltextureatlasprivate.h"
#include "gdk/gdkglcontextprivate.h"
#include "gdk/gdkmemorytextureprivate.h"
#include <graphene.h>
#include <cairo.h>
#include <epoxy/gl.h>
#include <string.h>
/* Cache eviction strategy
*
* We mark glyphs as accessed every time we use them.
* Every few frames, we mark glyphs that haven't been
* accessed since the last check as old.
*
* We keep count of the pixels of each atlas that are
* taken up by old data. When the fraction of old pixels
* gets too high, we drop the atlas and all the items it
* contained.
*
* Big glyphs are not stored in the atlas, they get their
* own texture, but they are still cached.
*/
#define MAX_FRAME_AGE (60)
#define MAX_GLYPH_SIZE 128 /* Will get its own texture if bigger */
static guint glyph_cache_hash (gconstpointer v);
static gboolean glyph_cache_equal (gconstpointer v1,
gconstpointer v2);
static void glyph_cache_key_free (gpointer v);
static void glyph_cache_value_free (gpointer v);
GskGLGlyphCache *
gsk_gl_glyph_cache_new (GdkDisplay *display,
GskGLTextureAtlases *atlases)
{
GskGLGlyphCache *glyph_cache;
glyph_cache = g_new0 (GskGLGlyphCache, 1);
glyph_cache->display = display;
glyph_cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
glyph_cache_key_free, glyph_cache_value_free);
glyph_cache->atlases = gsk_gl_texture_atlases_ref (atlases);
glyph_cache->ref_count = 1;
return glyph_cache;
}
GskGLGlyphCache *
gsk_gl_glyph_cache_ref (GskGLGlyphCache *self)
{
self->ref_count++;
return self;
}
void
gsk_gl_glyph_cache_unref (GskGLGlyphCache *self)
{
g_assert (self->ref_count > 0);
if (self->ref_count == 1)
{
gsk_gl_texture_atlases_unref (self->atlases);
g_hash_table_unref (self->hash_table);
g_free (self);
return;
}
self->ref_count--;
}
static gboolean
glyph_cache_equal (gconstpointer v1, gconstpointer v2)
{
return memcmp (v1, v2, sizeof (CacheKeyData)) == 0;
}
static guint
glyph_cache_hash (gconstpointer v)
{
const GlyphCacheKey *key = v;
return key->hash;
}
static void
glyph_cache_key_free (gpointer v)
{
GlyphCacheKey *f = v;
g_object_unref (f->data.font);
g_free (f);
}
static void
glyph_cache_value_free (gpointer v)
{
g_free (v);
}
static gboolean
render_glyph (GlyphCacheKey *key,
GskGLCachedGlyph *value,
GskImageRegion *region)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_scaled_font_t *scaled_font;
PangoGlyphString glyph_string;
PangoGlyphInfo glyph_info;
int surface_width, surface_height;
int stride;
unsigned char *data;
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)key->data.font);
if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
{
g_warning ("Failed to get a font");
return FALSE;
}
surface_width = value->draw_width * key->data.scale / 1024;
surface_height = value->draw_height * key->data.scale / 1024;
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, surface_width);
data = g_malloc0 (stride * surface_height);
surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32,
surface_width, surface_height,
stride);
cairo_surface_set_device_scale (surface, key->data.scale / 1024.0, key->data.scale / 1024.0);
cr = cairo_create (surface);
cairo_set_scaled_font (cr, scaled_font);
cairo_set_source_rgba (cr, 1, 1, 1, 1);
glyph_info.glyph = key->data.glyph;
glyph_info.geometry.width = value->draw_width * 1024;
if (glyph_info.glyph & PANGO_GLYPH_UNKNOWN_FLAG)
glyph_info.geometry.x_offset = 256 * key->data.xshift;
else
glyph_info.geometry.x_offset = 256 * key->data.xshift - value->draw_x * 1024;
glyph_info.geometry.y_offset = 256 * key->data.yshift - value->draw_y * 1024;
glyph_string.num_glyphs = 1;
glyph_string.glyphs = &glyph_info;
pango_cairo_show_glyph_string (cr, key->data.font, &glyph_string);
cairo_destroy (cr);
cairo_surface_flush (surface);
region->width = cairo_image_surface_get_width (surface);
region->height = cairo_image_surface_get_height (surface);
region->stride = cairo_image_surface_get_stride (surface);
region->data = data;
if (value->atlas)
{
region->x = (gsize)(value->tx * value->atlas->width);
region->y = (gsize)(value->ty * value->atlas->height);
}
else
{
region->x = 0;
region->y = 0;
}
cairo_surface_destroy (surface);
return TRUE;
}
static void
upload_glyph (GlyphCacheKey *key,
GskGLCachedGlyph *value)
{
GskImageRegion r;
guchar *pixel_data;
guchar *free_data = NULL;
guint gl_format;
guint gl_type;
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
"Uploading glyph %d",
key->data.glyph);
if (render_glyph (key, value, &r))
{
glPixelStorei (GL_UNPACK_ROW_LENGTH, r.stride / 4);
glBindTexture (GL_TEXTURE_2D, value->texture_id);
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
{
pixel_data = free_data = g_malloc (r.width * r.height * 4);
gdk_memory_convert (pixel_data, r.width * 4,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
r.data, r.width * 4,
GDK_MEMORY_DEFAULT, r.width, r.height);
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
}
else
{
pixel_data = r.data;
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height,
gl_format, gl_type, pixel_data);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
g_free (r.data);
g_free (free_data);
}
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
}
static void
add_to_cache (GskGLGlyphCache *self,
GlyphCacheKey *key,
GskGLDriver *driver,
GskGLCachedGlyph *value)
{
const int width = value->draw_width * key->data.scale / 1024;
const int height = value->draw_height * key->data.scale / 1024;
if (width < MAX_GLYPH_SIZE && height < MAX_GLYPH_SIZE)
{
GskGLTextureAtlas *atlas = NULL;
int packed_x = 0;
int packed_y = 0;
gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
value->tx = (float)(packed_x + 1) / atlas->width;
value->ty = (float)(packed_y + 1) / atlas->height;
value->tw = (float)width / atlas->width;
value->th = (float)height / atlas->height;
value->used = TRUE;
value->atlas = atlas;
value->texture_id = atlas->texture_id;
}
else
{
value->atlas = NULL;
value->texture_id = gsk_gl_driver_create_texture (driver, width, height);
gsk_gl_driver_mark_texture_permanent (driver, value->texture_id);
gsk_gl_driver_bind_source_texture (driver, value->texture_id);
gsk_gl_driver_init_texture_empty (driver, value->texture_id, GL_LINEAR, GL_LINEAR);
value->tx = 0.0f;
value->ty = 0.0f;
value->tw = 1.0f;
value->th = 1.0f;
}
upload_glyph (key, value);
}
void
gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache *cache,
GlyphCacheKey *lookup,
GskGLDriver *driver,
const GskGLCachedGlyph **cached_glyph_out)
{
GskGLCachedGlyph *value;
value = g_hash_table_lookup (cache->hash_table, lookup);
if (value)
{
if (value->atlas && !value->used)
{
gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height);
value->used = TRUE;
}
value->accessed = TRUE;
*cached_glyph_out = value;
return;
}
{
GlyphCacheKey *key;
PangoRectangle ink_rect;
pango_font_get_glyph_extents (lookup->data.font, lookup->data.glyph, &ink_rect, NULL);
pango_extents_to_pixels (&ink_rect, NULL);
if (lookup->data.xshift != 0)
ink_rect.width += 1;
if (lookup->data.yshift != 0)
ink_rect.height += 1;
value = g_new0 (GskGLCachedGlyph, 1);
value->draw_x = ink_rect.x;
value->draw_y = ink_rect.y;
value->draw_width = ink_rect.width;
value->draw_height = ink_rect.height;
value->accessed = TRUE;
value->atlas = NULL; /* For now */
key = g_new0 (GlyphCacheKey, 1);
key->data.font = g_object_ref (lookup->data.font);
key->data.glyph = lookup->data.glyph;
key->data.xshift = lookup->data.xshift;
key->data.yshift = lookup->data.yshift;
key->data.scale = lookup->data.scale;
key->hash = lookup->hash;
if (key->data.scale > 0 &&
value->draw_width * key->data.scale / 1024 > 0 &&
value->draw_height * key->data.scale / 1024 > 0)
add_to_cache (cache, key, driver, value);
*cached_glyph_out = value;
g_hash_table_insert (cache->hash_table, key, value);
}
}
void
gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
GskGLDriver *driver,
GPtrArray *removed_atlases)
{
GHashTableIter iter;
GlyphCacheKey *key;
GskGLCachedGlyph *value;
guint dropped = 0;
self->timestamp++;
if (removed_atlases->len > 0)
{
g_hash_table_iter_init (&iter, self->hash_table);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
{
if (g_ptr_array_find (removed_atlases, value->atlas, NULL))
{
g_hash_table_iter_remove (&iter);
dropped++;
}
}
}
if (self->timestamp % MAX_FRAME_AGE == 30)
{
g_hash_table_iter_init (&iter, self->hash_table);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
{
if (!value->accessed)
{
if (value->atlas)
{
if (value->used)
{
gsk_gl_texture_atlas_mark_unused (value->atlas, value->draw_width, value->draw_height);
value->used = FALSE;
}
}
else
{
gsk_gl_driver_destroy_texture (driver, value->texture_id);
g_hash_table_iter_remove (&iter);
/* Sadly, if we drop an atlas-less cached glyph, we
* have to treat it like a dropped atlas and purge
* text node render data.
*/
dropped++;
}
}
else
value->accessed = FALSE;
}
GSK_NOTE(GLYPH_CACHE, g_message ("%d glyphs cached", g_hash_table_size (self->hash_table)));
}
GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d glyphs", dropped));
}

View File

@@ -1,92 +0,0 @@
#ifndef __GSK_GL_GLYPH_CACHE_PRIVATE_H__
#define __GSK_GL_GLYPH_CACHE_PRIVATE_H__
#include "gskgldriverprivate.h"
#include "gskglimageprivate.h"
#include "gskgltextureatlasprivate.h"
#include <pango/pango.h>
#include <gdk/gdk.h>
typedef struct
{
int ref_count;
GdkDisplay *display;
GHashTable *hash_table;
GskGLTextureAtlases *atlases;
int timestamp;
} GskGLGlyphCache;
struct _CacheKeyData
{
PangoFont *font;
PangoGlyph glyph;
guint xshift : 3;
guint yshift : 3;
guint scale : 26; /* times 1024 */
};
typedef struct _CacheKeyData CacheKeyData;
struct _GlyphCacheKey
{
CacheKeyData data;
guint hash;
};
typedef struct _GlyphCacheKey GlyphCacheKey;
#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125)))
static inline void
glyph_cache_key_set_glyph_and_shift (GlyphCacheKey *key,
PangoGlyph glyph,
float x,
float y)
{
key->data.glyph = glyph;
key->data.xshift = PHASE (x);
key->data.yshift = PHASE (y);
key->hash = GPOINTER_TO_UINT (key->data.font) ^
key->data.glyph ^
(key->data.xshift << 24) ^
(key->data.yshift << 26) ^
key->data.scale;
}
typedef struct _GskGLCachedGlyph GskGLCachedGlyph;
struct _GskGLCachedGlyph
{
GskGLTextureAtlas *atlas;
guint texture_id;
float tx;
float ty;
float tw;
float th;
int draw_x;
int draw_y;
int draw_width;
int draw_height;
guint accessed : 1; /* accessed since last check */
guint used : 1; /* accounted as used in the atlas */
};
GskGLGlyphCache * gsk_gl_glyph_cache_new (GdkDisplay *display,
GskGLTextureAtlases *atlases);
GskGLGlyphCache * gsk_gl_glyph_cache_ref (GskGLGlyphCache *self);
void gsk_gl_glyph_cache_unref (GskGLGlyphCache *self);
void gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
GskGLDriver *driver,
GPtrArray *removed_atlases);
void gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache *self,
GlyphCacheKey *lookup,
GskGLDriver *driver,
const GskGLCachedGlyph **cached_glyph_out);
#endif

View File

@@ -1,275 +0,0 @@
#include "gskgliconcacheprivate.h"
#include "gskgltextureatlasprivate.h"
#include "gdk/gdktextureprivate.h"
#include "gdk/gdkmemorytextureprivate.h"
#include "gdk/gdkglcontextprivate.h"
#include <epoxy/gl.h>
#define MAX_FRAME_AGE 60
static void
icon_data_free (gpointer p)
{
g_object_unref (((IconData *)p)->source_texture);
g_free (p);
}
GskGLIconCache *
gsk_gl_icon_cache_new (GdkDisplay *display,
GskGLTextureAtlases *atlases)
{
GskGLIconCache *self;
self = g_new0 (GskGLIconCache, 1);
self->display = display;
self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
self->atlases = gsk_gl_texture_atlases_ref (atlases);
self->ref_count = 1;
return self;
}
GskGLIconCache *
gsk_gl_icon_cache_ref (GskGLIconCache *self)
{
self->ref_count++;
return self;
}
void
gsk_gl_icon_cache_unref (GskGLIconCache *self)
{
g_assert (self->ref_count > 0);
if (self->ref_count == 1)
{
gsk_gl_texture_atlases_unref (self->atlases);
g_hash_table_unref (self->icons);
g_free (self);
return;
}
self->ref_count--;
}
void
gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
GPtrArray *removed_atlases)
{
GHashTableIter iter;
GdkTexture *texture;
IconData *icon_data;
self->timestamp++;
/* Drop icons on removed atlases */
if (removed_atlases->len > 0)
{
guint dropped = 0;
g_hash_table_iter_init (&iter, self->icons);
while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
{
if (g_ptr_array_find (removed_atlases, icon_data->atlas, NULL))
{
g_hash_table_iter_remove (&iter);
dropped++;
}
}
GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d icons", dropped));
}
if (self->timestamp % MAX_FRAME_AGE == 0)
{
g_hash_table_iter_init (&iter, self->icons);
while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
{
if (!icon_data->accessed)
{
if (icon_data->used)
{
const int width = icon_data->source_texture->width;
const int height = icon_data->source_texture->height;
gsk_gl_texture_atlas_mark_unused (icon_data->atlas, width + 2, height + 2);
icon_data->used = FALSE;
}
}
icon_data->accessed = FALSE;
}
GSK_NOTE(GLYPH_CACHE, g_message ("%d icons cached", g_hash_table_size (self->icons)));
}
}
void
gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
GdkTexture *texture,
const IconData **out_icon_data)
{
IconData *icon_data = g_hash_table_lookup (self->icons, texture);
if (icon_data)
{
if (!icon_data->used)
{
gsk_gl_texture_atlas_mark_used (icon_data->atlas, texture->width + 2, texture->height + 2);
icon_data->used = TRUE;
}
icon_data->accessed = TRUE;
*out_icon_data = icon_data;
return;
}
/* texture not on any atlas yet. Find a suitable one. */
{
const int width = texture->width;
const int height = texture->height;
GskGLTextureAtlas *atlas = NULL;
int packed_x = 0;
int packed_y = 0;
cairo_surface_t *surface;
unsigned char *surface_data;
unsigned char *pixel_data;
guchar *free_data = NULL;
guint gl_format;
guint gl_type;
gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
icon_data = g_new0 (IconData, 1);
icon_data->atlas = atlas;
icon_data->accessed = TRUE;
icon_data->used = TRUE;
icon_data->texture_id = atlas->texture_id;
icon_data->source_texture = g_object_ref (texture);
icon_data->x = (float)(packed_x + 1) / atlas->width;
icon_data->y = (float)(packed_y + 1) / atlas->width;
icon_data->x2 = icon_data->x + (float)width / atlas->width;
icon_data->y2 = icon_data->y + (float)height / atlas->height;
g_hash_table_insert (self->icons, texture, icon_data);
/* actually upload the texture */
surface = gdk_texture_download_surface (texture);
surface_data = cairo_image_surface_get_data (surface);
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
"Uploading texture");
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
{
pixel_data = free_data = g_malloc (width * height * 4);
gdk_memory_convert (pixel_data, width * 4,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
surface_data, cairo_image_surface_get_stride (surface),
GDK_MEMORY_DEFAULT, width, height);
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
}
else
{
pixel_data = surface_data;
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y + 1,
width, height,
gl_format, gl_type,
pixel_data);
/* Padding top */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y,
width, 1,
gl_format, gl_type,
pixel_data);
/* Padding left */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y + 1,
1, height,
gl_format, gl_type,
pixel_data);
/* Padding top left */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y,
1, 1,
gl_format, gl_type,
pixel_data);
/* Padding right */
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + width + 1, packed_y + 1,
1, height,
gl_format, gl_type,
pixel_data);
/* Padding top right */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + width + 1, packed_y,
1, 1,
gl_format, gl_type,
pixel_data);
/* Padding bottom */
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1);
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y + 1 + height,
width, 1,
gl_format, gl_type,
pixel_data);
/* Padding bottom left */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y + 1 + height,
1, 1,
gl_format, gl_type,
pixel_data);
/* Padding bottom right */
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1 + width, packed_y + 1 + height,
1, 1,
gl_format, gl_type,
pixel_data);
/* Reset this */
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
*out_icon_data = icon_data;
cairo_surface_destroy (surface);
g_free (free_data);
#if 0
{
static int k;
const int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
guchar *data = g_malloc (atlas->height * stride);
cairo_surface_t *s;
char *filename = g_strdup_printf ("atlas_%u_%d.png", atlas->texture_id, k++);
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride);
cairo_surface_write_to_png (s, filename);
cairo_surface_destroy (s);
g_free (data);
g_free (filename);
}
#endif
}
}

View File

@@ -1,44 +0,0 @@
#ifndef __GSK_GL_ICON_CACHE_PRIVATE_H__
#define __GSK_GL_ICON_CACHE_PRIVATE_H__
#include "gskgldriverprivate.h"
#include "gskglimageprivate.h"
#include "gskrendererprivate.h"
#include "gskgltextureatlasprivate.h"
#include <pango/pango.h>
#include <gdk/gdk.h>
typedef struct
{
int ref_count;
GdkDisplay *display;
GskGLDriver *gl_driver;
GskGLTextureAtlases *atlases;
GHashTable *icons; /* GdkTexture -> IconData */
int timestamp;
} GskGLIconCache;
typedef struct
{
float x, y, x2, y2;
GskGLTextureAtlas *atlas;
guint used : 1;
guint accessed : 1;
int texture_id;
GdkTexture *source_texture;
} IconData;
GskGLIconCache * gsk_gl_icon_cache_new (GdkDisplay *display,
GskGLTextureAtlases *atlases);
GskGLIconCache * gsk_gl_icon_cache_ref (GskGLIconCache *self);
void gsk_gl_icon_cache_unref (GskGLIconCache *self);
void gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
GPtrArray *removed_atlases);
void gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
GdkTexture *texture,
const IconData **out_icon_data);
#endif

View File

@@ -1,64 +0,0 @@
#include "gskglimageprivate.h"
#include <epoxy/gl.h>
void
gsk_gl_image_create (GskGLImage *self,
GskGLDriver *gl_driver,
int width,
int height,
int min_filter,
int mag_filter)
{
self->texture_id = gsk_gl_driver_create_texture (gl_driver, width, height);
self->width = width;
self->height = height;
gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
gsk_gl_driver_init_texture_empty (gl_driver, self->texture_id, min_filter, mag_filter);
gsk_gl_driver_mark_texture_permanent (gl_driver, self->texture_id);
}
void
gsk_gl_image_destroy (GskGLImage *self,
GskGLDriver *gl_driver)
{
gsk_gl_driver_destroy_texture (gl_driver, self->texture_id);
self->texture_id = 0;
}
void
gsk_gl_image_write_to_png (const GskGLImage *self,
GskGLDriver *gl_driver,
const char *filename)
{
int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, self->width);
guchar *data = g_malloc (self->height * stride);
cairo_surface_t *s;
gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, self->width, self->height, stride);
cairo_surface_write_to_png (s, filename);
cairo_surface_destroy (s);
g_free (data);
}
void
gsk_gl_image_upload_region (GskGLImage *self,
GskGLDriver *gl_driver,
const GskImageRegion *region)
{
gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
glBindTexture (GL_TEXTURE_2D, self->texture_id);
glTexSubImage2D (GL_TEXTURE_2D, 0, region->x, region->y, region->width, region->height,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, region->data);
#ifdef G_ENABLE_DEBUG
/*gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);*/
/*gsk_gl_image_dump (self, gl_driver, "/home/baedert/atlases/test_dump.png");*/
#endif
}

View File

@@ -1,41 +0,0 @@
#ifndef __GSK_GL_IMAGE_H__
#define __GSK_GL_IMAGE_H__
#include "gskgldriverprivate.h"
#include <cairo.h>
typedef struct
{
guint texture_id;
int width;
int height;
} GskGLImage;
typedef struct
{
guchar *data;
gsize width;
gsize height;
gsize stride;
gsize x;
gsize y;
} GskImageRegion;
void gsk_gl_image_create (GskGLImage *self,
GskGLDriver *gl_driver,
int width,
int height,
int min_filter,
int mag_filter);
void gsk_gl_image_destroy (GskGLImage *self,
GskGLDriver *gl_driver);
void gsk_gl_image_write_to_png (const GskGLImage *self,
GskGLDriver *gl_driver,
const char *filename);
void gsk_gl_image_upload_region (GskGLImage *self,
GskGLDriver *gl_driver,
const GskImageRegion *region);
#endif

View File

@@ -1,51 +0,0 @@
#include <glib/gprintf.h>
#include "gskglnodesampleprivate.h"
#include "gskrendernodeprivate.h"
void
node_sample_init (NodeSample *self)
{
memset (self->nodes, 0, sizeof (self->nodes));
self->count = 0;
}
void
node_sample_reset (NodeSample *self)
{
node_sample_init (self);
}
void
node_sample_add (NodeSample *self,
GskRenderNode *node)
{
const guint node_type = gsk_render_node_get_node_type (node);
g_assert (node_type <= N_NODE_TYPES);
if (self->nodes[node_type].class_name == NULL)
self->nodes[node_type].class_name = g_type_name_from_instance ((GTypeInstance *) node);
self->nodes[node_type].count ++;
self->count ++;
}
void
node_sample_print (const NodeSample *self,
const char *prefix)
{
guint i;
g_printf ("%s:\n", prefix);
for (i = 0; i < N_NODE_TYPES; i ++)
{
if (self->nodes[i].count > 0)
{
double p = (double)self->nodes[i].count / (double)self->count;
g_printf ("%s: %u (%.2f%%)\n", self->nodes[i].class_name, self->nodes[i].count, p * 100.0);
}
}
}

View File

@@ -1,28 +0,0 @@
#ifndef __GSK_GL_NODE_SAMPLE_PRIVATE_H__
#define __GSK_GL_NODE_SAMPLE_PRIVATE_H__
#include <glib.h>
#include "gskenums.h"
#include "gskrendernode.h"
/* TODO: We have no other way for this...? */
#define N_NODE_TYPES (GSK_DEBUG_NODE + 1)
typedef struct
{
struct {
const char *class_name;
guint count;
} nodes[N_NODE_TYPES];
guint count;
} NodeSample;
void node_sample_init (NodeSample *self);
void node_sample_reset (NodeSample *self);
void node_sample_add (NodeSample *self,
GskRenderNode *node);
void node_sample_print (const NodeSample *self,
const char *prefix);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,52 +0,0 @@
/*
* Copyright © 2016 Endless
* 2018 Timm Bäder <mail@baedert.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Timm Bäder <mail@baedert.org>
*/
#ifndef __GSK_GL_RENDERER_H__
#define __GSK_GL_RENDERER_H__
#include <gsk/gskrenderer.h>
G_BEGIN_DECLS
#define GSK_TYPE_GL_RENDERER (gsk_gl_renderer_get_type ())
#define GSK_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_RENDERER, GskGLRenderer))
#define GSK_IS_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_RENDERER))
#define GSK_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_RENDERER, GskGLRendererClass))
#define GSK_IS_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_RENDERER))
#define GSK_GL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_RENDERER, GskGLRendererClass))
/**
* GskGLRenderer:
*
* A GSK renderer that is using OpenGL.
*/
typedef struct _GskGLRenderer GskGLRenderer;
typedef struct _GskGLRendererClass GskGLRendererClass;
GDK_AVAILABLE_IN_ALL
GType gsk_gl_renderer_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderer * gsk_gl_renderer_new (void);
G_END_DECLS
#endif /* __GSK_GL_RENDERER_H__ */

View File

@@ -1,14 +0,0 @@
#ifndef __GSK_GL_RENDERER_PRIVATE_H__
#define __GSK_GL_RENDERER_PRIVATE_H__
#include "gskglrenderer.h"
G_BEGIN_DECLS
gboolean gsk_gl_renderer_try_compile_gl_shader (GskGLRenderer *self,
GskGLShader *shader,
GError **error);
G_END_DECLS
#endif /* __GSK_GL_RENDERER_PRIVATE_H__ */

View File

@@ -1,976 +0,0 @@
#include "gskglrenderopsprivate.h"
#include "gsktransform.h"
typedef struct
{
GskRoundedRect rect;
bool is_rectilinear;
} ClipStackEntry;
static inline gboolean
rect_equal (const graphene_rect_t *a,
const graphene_rect_t *b)
{
return memcmp (a, b, sizeof (graphene_rect_t)) == 0;
}
static inline bool G_GNUC_PURE
rounded_rect_equal (const GskRoundedRect *r1,
const GskRoundedRect *r2)
{
if (r1 == r2)
return true;
if (!r1)
return false;
if (r1->bounds.origin.x != r2->bounds.origin.x ||
r1->bounds.origin.y != r2->bounds.origin.y ||
r1->bounds.size.width != r2->bounds.size.width ||
r1->bounds.size.height != r2->bounds.size.height)
return false;
for (int i = 0; i < 4; i ++)
if (r1->corner[i].width != r2->corner[i].width ||
r1->corner[i].height != r2->corner[i].height)
return false;
return true;
}
static inline gboolean G_GNUC_PURE
rounded_rect_corners_equal (const GskRoundedRect *r1,
const GskRoundedRect *r2)
{
int i;
if (!r1)
return FALSE;
for (i = 0; i < 4; i ++)
if (r1->corner[i].width != r2->corner[i].width ||
r1->corner[i].height != r2->corner[i].height)
return FALSE;
return TRUE;
}
static inline ProgramState *
get_current_program_state (RenderOpBuilder *builder)
{
if (!builder->current_program)
return NULL;
return &builder->current_program->state;
}
void
ops_finish (RenderOpBuilder *builder)
{
if (builder->mv_stack)
g_array_free (builder->mv_stack, TRUE);
builder->mv_stack = NULL;
if (builder->clip_stack)
g_array_free (builder->clip_stack, TRUE);
builder->clip_stack = NULL;
builder->dx = 0;
builder->dy = 0;
builder->scale_x = 1;
builder->scale_y = 1;
builder->current_modelview = NULL;
builder->current_clip = NULL;
builder->clip_is_rectilinear = TRUE;
builder->current_render_target = 0;
builder->current_texture = 0;
builder->current_program = NULL;
graphene_matrix_init_identity (&builder->current_projection);
builder->current_viewport = GRAPHENE_RECT_INIT (0, 0, 0, 0);
}
/* Debugging only! */
void
ops_dump_framebuffer (RenderOpBuilder *builder,
const char *filename,
int width,
int height)
{
OpDumpFrameBuffer *op;
op = ops_begin (builder, OP_DUMP_FRAMEBUFFER);
op->filename = g_strdup (filename);
op->width = width;
op->height = height;
}
void
ops_push_debug_group (RenderOpBuilder *builder,
const char *text)
{
OpDebugGroup *op;
op = ops_begin (builder, OP_PUSH_DEBUG_GROUP);
strncpy (op->text, text, sizeof(op->text) - 1);
op->text[sizeof(op->text) - 1] = 0; /* Ensure zero terminated */
}
void
ops_pop_debug_group (RenderOpBuilder *builder)
{
ops_begin (builder, OP_POP_DEBUG_GROUP);
}
static void
extract_matrix_metadata (GskTransform *transform,
OpsMatrixMetadata *md)
{
float dummy;
switch (gsk_transform_get_category (transform))
{
case GSK_TRANSFORM_CATEGORY_IDENTITY:
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
md->scale_x = 1;
md->scale_y = 1;
break;
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
gsk_transform_to_affine (transform,
&md->scale_x, &md->scale_y,
&dummy, &dummy);
break;
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
case GSK_TRANSFORM_CATEGORY_ANY:
case GSK_TRANSFORM_CATEGORY_3D:
case GSK_TRANSFORM_CATEGORY_2D:
{
graphene_vec3_t col1;
graphene_vec3_t col2;
graphene_matrix_t m;
gsk_transform_to_matrix (transform, &m);
/* TODO: 90% sure this is incorrect. But we should never hit this code
* path anyway. */
graphene_vec3_init (&col1,
graphene_matrix_get_value (&m, 0, 0),
graphene_matrix_get_value (&m, 1, 0),
graphene_matrix_get_value (&m, 2, 0));
graphene_vec3_init (&col2,
graphene_matrix_get_value (&m, 0, 1),
graphene_matrix_get_value (&m, 1, 1),
graphene_matrix_get_value (&m, 2, 1));
md->scale_x = graphene_vec3_length (&col1);
md->scale_y = graphene_vec3_length (&col2);
}
break;
default:
{}
}
}
void
ops_transform_bounds_modelview (const RenderOpBuilder *builder,
const graphene_rect_t *src,
graphene_rect_t *dst)
{
graphene_rect_t r = *src;
g_assert (builder->mv_stack != NULL);
g_assert (builder->mv_stack->len >= 1);
r.origin.x += builder->dx;
r.origin.y += builder->dy;
gsk_transform_transform_bounds (builder->current_modelview, &r, dst);
}
void
ops_init (RenderOpBuilder *builder)
{
memset (builder, 0, sizeof (*builder));
builder->current_opacity = 1.0f;
op_buffer_init (&builder->render_ops);
builder->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex));
}
void
ops_free (RenderOpBuilder *builder)
{
g_array_unref (builder->vertices);
op_buffer_destroy (&builder->render_ops);
}
void
ops_set_program (RenderOpBuilder *builder,
Program *program)
{
OpProgram *op;
if (builder->current_program == program)
return;
op = ops_begin (builder, OP_CHANGE_PROGRAM);
op->program = program;
builder->current_program = program;
}
void
ops_push_clip (RenderOpBuilder *self,
const GskRoundedRect *clip)
{
ClipStackEntry entry;
if (G_UNLIKELY (self->clip_stack == NULL))
self->clip_stack = g_array_new (FALSE, TRUE, sizeof (ClipStackEntry));
g_assert (self->clip_stack != NULL);
entry.rect = *clip;
entry.is_rectilinear = gsk_rounded_rect_is_rectilinear (clip);
g_array_append_val (self->clip_stack, entry);
self->current_clip = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1).rect;
self->clip_is_rectilinear = entry.is_rectilinear;
}
void
ops_pop_clip (RenderOpBuilder *self)
{
const ClipStackEntry *head;
g_assert (self->clip_stack);
g_assert (self->clip_stack->len >= 1);
self->clip_stack->len --;
head = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1);
if (self->clip_stack->len >= 1)
{
self->current_clip = &head->rect;
self->clip_is_rectilinear = head->is_rectilinear;
}
else
{
self->current_clip = NULL;
self->clip_is_rectilinear = TRUE;
}
}
gboolean
ops_has_clip (RenderOpBuilder *self)
{
return self->clip_stack != NULL &&
self->clip_stack->len > 1;
}
/**
* ops_set_modelview:
* @builder
* @transform: (transfer full): The new modelview transform
*
* This sets the modelview to the given one without looking at the
* one that's currently set */
void
ops_set_modelview (RenderOpBuilder *builder,
GskTransform *transform)
{
MatrixStackEntry *entry;
if (G_UNLIKELY (builder->mv_stack == NULL))
builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
g_assert (builder->mv_stack != NULL);
g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
entry->transform = transform;
entry->metadata.dx_before = builder->dx;
entry->metadata.dy_before = builder->dy;
extract_matrix_metadata (entry->transform, &entry->metadata);
builder->dx = 0;
builder->dy = 0;
builder->current_modelview = entry->transform;
builder->scale_x = entry->metadata.scale_x;
builder->scale_y = entry->metadata.scale_y;
}
/* This sets the given modelview to the one we get when multiplying
* the given modelview with the current one. */
void
ops_push_modelview (RenderOpBuilder *builder,
GskTransform *transform)
{
MatrixStackEntry *entry;
if (G_UNLIKELY (builder->mv_stack == NULL))
builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
g_assert (builder->mv_stack != NULL);
g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
if (G_LIKELY (builder->mv_stack->len >= 2))
{
const MatrixStackEntry *cur;
GskTransform *t = NULL;
cur = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 2);
/* Multiply given matrix with current modelview */
t = gsk_transform_translate (gsk_transform_ref (cur->transform),
&(graphene_point_t) { builder->dx, builder->dy});
t = gsk_transform_transform (t, transform);
entry->transform = t;
}
else
{
entry->transform = gsk_transform_ref (transform);
}
entry->metadata.dx_before = builder->dx;
entry->metadata.dy_before = builder->dy;
extract_matrix_metadata (entry->transform, &entry->metadata);
builder->dx = 0;
builder->dy = 0;
builder->scale_x = entry->metadata.scale_x;
builder->scale_y = entry->metadata.scale_y;
builder->current_modelview = entry->transform;
}
void
ops_pop_modelview (RenderOpBuilder *builder)
{
const MatrixStackEntry *head;
g_assert (builder->mv_stack);
g_assert (builder->mv_stack->len >= 1);
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
builder->dx = head->metadata.dx_before;
builder->dy = head->metadata.dy_before;
gsk_transform_unref (head->transform);
builder->mv_stack->len --;
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
if (builder->mv_stack->len >= 1)
{
builder->scale_x = head->metadata.scale_x;
builder->scale_y = head->metadata.scale_y;
builder->current_modelview = head->transform;
}
else
{
builder->current_modelview = NULL;
}
}
graphene_matrix_t
ops_set_projection (RenderOpBuilder *builder,
const graphene_matrix_t *projection)
{
graphene_matrix_t prev_mv;
prev_mv = builder->current_projection;
builder->current_projection = *projection;
return prev_mv;
}
graphene_rect_t
ops_set_viewport (RenderOpBuilder *builder,
const graphene_rect_t *viewport)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpViewport *op;
graphene_rect_t prev_viewport;
if (rect_equal (&builder->current_viewport, viewport))
return *viewport;
op = ops_begin (builder, OP_CHANGE_VIEWPORT);
op->viewport = *viewport;
if (current_program_state != NULL)
current_program_state->viewport = *viewport;
prev_viewport = builder->current_viewport;
builder->current_viewport = *viewport;
return prev_viewport;
}
void
ops_set_texture (RenderOpBuilder *builder,
int texture_id)
{
OpTexture *op;
if (builder->current_texture == texture_id)
return;
op = ops_begin (builder, OP_CHANGE_SOURCE_TEXTURE);
op->texture_id = texture_id;
builder->current_texture = texture_id;
}
void
ops_set_extra_texture (RenderOpBuilder *builder,
int texture_id,
int idx)
{
OpExtraTexture *op;
op = ops_begin (builder, OP_CHANGE_EXTRA_SOURCE_TEXTURE);
op->texture_id = texture_id;
op->idx = idx;
}
int
ops_set_render_target (RenderOpBuilder *builder,
int render_target_id)
{
OpRenderTarget *op;
int prev_render_target;
if (builder->current_render_target == render_target_id)
return render_target_id;
prev_render_target = builder->current_render_target;
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_RENDER_TARGET)))
op = op_buffer_add (&builder->render_ops, OP_CHANGE_RENDER_TARGET);
op->render_target_id = render_target_id;
builder->current_render_target = render_target_id;
return prev_render_target;
}
float
ops_set_opacity (RenderOpBuilder *builder,
float opacity)
{
float prev_opacity;
if (builder->current_opacity == opacity)
return opacity;
prev_opacity = builder->current_opacity;
builder->current_opacity = opacity;
return prev_opacity;
}
void
ops_set_color (RenderOpBuilder *builder,
const GdkRGBA *color)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpColor *op;
if (gdk_rgba_equal (color, &current_program_state->color))
return;
current_program_state->color = *color;
op = ops_begin (builder, OP_CHANGE_COLOR);
op->rgba = color;
}
void
ops_set_gl_shader_args (RenderOpBuilder *builder,
GskGLShader *shader,
float width,
float height,
const guchar *uniform_data)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpGLShader *op;
gsize args_size = gsk_gl_shader_get_args_size (shader);
if (current_program_state)
{
if (current_program_state->gl_shader.width == width &&
current_program_state->gl_shader.height == height &&
current_program_state->gl_shader.uniform_data_len == args_size &&
memcmp (current_program_state->gl_shader.uniform_data, uniform_data, args_size) == 0)
return;
current_program_state->gl_shader.width = width;
current_program_state->gl_shader.height = height;
if (args_size > sizeof (current_program_state->gl_shader.uniform_data))
current_program_state->gl_shader.uniform_data_len = 0;
else
{
current_program_state->gl_shader.uniform_data_len = args_size;
memcpy (current_program_state->gl_shader.uniform_data, uniform_data, args_size);
}
}
op = ops_begin (builder, OP_CHANGE_GL_SHADER_ARGS);
op->shader = shader;
op->size[0] = width;
op->size[1] = height;
op->uniform_data = uniform_data;
}
void
ops_set_color_matrix (RenderOpBuilder *builder,
const graphene_matrix_t *matrix,
const graphene_vec4_t *offset)
{
ProgramState *current_program_state = get_current_program_state (builder);
const bool offset_equal = graphene_vec4_equal (offset, &current_program_state->color_matrix.offset);
const bool matrix_equal = graphene_matrix_equal_fast (matrix,
&current_program_state->color_matrix.matrix);
OpColorMatrix *op;
if (offset_equal && matrix_equal)
return;
op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
if (!matrix_equal)
{
current_program_state->color_matrix.matrix = *matrix;
op->matrix.value = matrix;
op->matrix.send = TRUE;
}
else
op->matrix.send = FALSE;
if (!offset_equal)
{
current_program_state->color_matrix.offset = *offset;
op->offset.value = offset;
op->offset.send = TRUE;
}
else
op->offset.send = FALSE;
}
void
ops_set_border (RenderOpBuilder *builder,
const GskRoundedRect *outline)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpBorder *op;
if (memcmp (&current_program_state->border.outline,
outline, sizeof (GskRoundedRect)) == 0)
return;
current_program_state->border.outline = *outline;
op = ops_begin (builder, OP_CHANGE_BORDER);
op->outline = *outline;
}
void
ops_set_border_width (RenderOpBuilder *builder,
const float *widths)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpBorder *op;
g_assert (current_program_state);
if (memcmp (current_program_state->border.widths,
widths, sizeof (float) * 4) == 0)
return;
memcpy (&current_program_state->border.widths,
widths, sizeof (float) * 4);
op = ops_begin (builder, OP_CHANGE_BORDER_WIDTH);
op->widths[0] = widths[0];
op->widths[1] = widths[1];
op->widths[2] = widths[2];
op->widths[3] = widths[3];
}
void
ops_set_border_color (RenderOpBuilder *builder,
const GdkRGBA *color)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpBorder *op;
if (gdk_rgba_equal (color, &current_program_state->border.color))
return;
op = op_buffer_add (&builder->render_ops, OP_CHANGE_BORDER_COLOR);
op->color = color;
current_program_state->border.color = *color;
}
GskQuadVertex *
ops_draw (RenderOpBuilder *builder,
const GskQuadVertex vertex_data[GL_N_VERTICES])
{
ProgramState *program_state = get_current_program_state (builder);
OpDraw *op;
if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
{
OpMatrix *opm;
opm = ops_begin (builder, OP_CHANGE_PROJECTION);
opm->matrix = builder->current_projection;
program_state->projection = builder->current_projection;
}
if (program_state->modelview == NULL ||
!gsk_transform_equal (builder->current_modelview, program_state->modelview))
{
OpMatrix *opm;
opm = ops_begin (builder, OP_CHANGE_MODELVIEW);
gsk_transform_to_matrix (builder->current_modelview, &opm->matrix);
gsk_transform_unref (program_state->modelview);
program_state->modelview = gsk_transform_ref (builder->current_modelview);
}
if (!rect_equal (&builder->current_viewport, &program_state->viewport))
{
OpViewport *opv;
opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
opv->viewport = builder->current_viewport;
program_state->viewport = builder->current_viewport;
}
if (!rounded_rect_equal (builder->current_clip, &program_state->clip))
{
OpClip *opc;
opc = ops_begin (builder, OP_CHANGE_CLIP);
opc->clip = *builder->current_clip;
opc->send_corners = !rounded_rect_corners_equal (builder->current_clip, &program_state->clip);
program_state->clip = *builder->current_clip;
}
if (program_state->opacity != builder->current_opacity)
{
OpOpacity *opo;
opo = ops_begin (builder, OP_CHANGE_OPACITY);
opo->opacity = builder->current_opacity;
program_state->opacity = builder->current_opacity;
}
/* TODO: Did the additions above break the following optimization? */
if ((op = op_buffer_peek_tail_checked (&builder->render_ops, OP_DRAW)))
{
op->vao_size += GL_N_VERTICES;
}
else
{
op = op_buffer_add (&builder->render_ops, OP_DRAW);
op->vao_offset = builder->vertices->len;
op->vao_size = GL_N_VERTICES;
}
if (vertex_data)
{
g_array_append_vals (builder->vertices, vertex_data, GL_N_VERTICES);
return NULL; /* Better not use this on the caller side */
}
g_array_set_size (builder->vertices, builder->vertices->len + GL_N_VERTICES);
return &g_array_index (builder->vertices, GskQuadVertex, builder->vertices->len - GL_N_VERTICES);
}
/* The offset is only valid for the current modelview.
* Setting a new modelview will add the offset to that matrix
* and reset the internal offset to 0. */
void
ops_offset (RenderOpBuilder *builder,
float x,
float y)
{
builder->dx += x;
builder->dy += y;
}
gpointer
ops_begin (RenderOpBuilder *builder,
OpKind kind)
{
return op_buffer_add (&builder->render_ops, kind);
}
void
ops_reset (RenderOpBuilder *builder)
{
op_buffer_clear (&builder->render_ops);
g_array_set_size (builder->vertices, 0);
}
OpBuffer *
ops_get_buffer (RenderOpBuilder *builder)
{
return &builder->render_ops;
}
void
ops_set_inset_shadow (RenderOpBuilder *self,
const GskRoundedRect outline,
float spread,
const GdkRGBA *color,
float dx,
float dy)
{
ProgramState *current_program_state = get_current_program_state (self);
OpShadow *op;
g_assert (current_program_state);
op = ops_begin (self, OP_CHANGE_INSET_SHADOW);
if (!rounded_rect_equal (&outline, &current_program_state->inset_shadow.outline))
{
op->outline.value = outline;
op->outline.send = TRUE;
op->outline.send_corners = !rounded_rect_corners_equal (&current_program_state->inset_shadow.outline,
&outline);
current_program_state->inset_shadow.outline = outline;
}
else
op->outline.send = FALSE;
if (spread != current_program_state->inset_shadow.spread)
{
op->spread.value = spread;
op->spread.send = TRUE;
current_program_state->inset_shadow.spread = spread;
}
else
op->spread.send = FALSE;
if (!gdk_rgba_equal (color, &current_program_state->inset_shadow.color))
{
op->color.value = color;
op->color.send = TRUE;
current_program_state->inset_shadow.color = *color;
}
else
op->color.send = FALSE;
if (dx != current_program_state->inset_shadow.dx ||
dy != current_program_state->inset_shadow.dy)
{
op->offset.value[0] = dx;
op->offset.value[1] = dy;
op->offset.send = TRUE;
current_program_state->inset_shadow.dx = dx;
current_program_state->inset_shadow.dy = dy;
}
else
op->offset.send = FALSE;
if (!op->outline.send &&
!op->spread.send &&
!op->offset.send &&
!op->color.send)
{
op_buffer_pop_tail (&self->render_ops);
}
}
void
ops_set_unblurred_outset_shadow (RenderOpBuilder *self,
const GskRoundedRect outline,
float spread,
const GdkRGBA *color,
float dx,
float dy)
{
ProgramState *current_program_state = get_current_program_state (self);
OpShadow *op;
g_assert (current_program_state);
op = ops_begin (self, OP_CHANGE_UNBLURRED_OUTSET_SHADOW);
if (!rounded_rect_equal (&outline, &current_program_state->unblurred_outset_shadow.outline))
{
op->outline.value = outline;
op->outline.send = TRUE;
op->outline.send_corners = !rounded_rect_corners_equal (&current_program_state->unblurred_outset_shadow.outline,
&outline);
current_program_state->unblurred_outset_shadow.outline = outline;
}
else
op->outline.send = FALSE;
if (spread != current_program_state->unblurred_outset_shadow.spread)
{
op->spread.value = spread;
op->spread.send = TRUE;
current_program_state->unblurred_outset_shadow.spread = spread;
}
else
op->spread.send = FALSE;
if (!gdk_rgba_equal (color, &current_program_state->unblurred_outset_shadow.color))
{
op->color.value = color;
op->color.send = TRUE;
current_program_state->unblurred_outset_shadow.color = *color;
}
else
op->color.send = FALSE;
if (dx != current_program_state->unblurred_outset_shadow.dx ||
dy != current_program_state->unblurred_outset_shadow.dy)
{
op->offset.value[0] = dx;
op->offset.value[1] = dy;
op->offset.send = TRUE;
current_program_state->unblurred_outset_shadow.dx = dx;
current_program_state->unblurred_outset_shadow.dy = dy;
}
else
op->offset.send = FALSE;
}
void
ops_set_linear_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
gboolean repeat,
float start_x,
float start_y,
float end_x,
float end_y)
{
ProgramState *current_program_state = get_current_program_state (self);
OpLinearGradient *op;
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
g_assert (current_program_state);
op = ops_begin (self, OP_CHANGE_LINEAR_GRADIENT);
/* We always save the n_color_stops value in the op so the renderer can use it in
* cases where we send the color stops, but not n_color_stops */
op->n_color_stops.value = real_n_color_stops;
if (current_program_state->linear_gradient.n_color_stops != real_n_color_stops)
{
op->n_color_stops.send = TRUE;
current_program_state->linear_gradient.n_color_stops = real_n_color_stops;
}
else
op->n_color_stops.send = FALSE;
op->color_stops.send = FALSE;
if (!op->n_color_stops.send)
{
g_assert (current_program_state->linear_gradient.n_color_stops == real_n_color_stops);
for (guint i = 0; i < real_n_color_stops; i ++)
{
const GskColorStop *s1 = &color_stops[i];
const GskColorStop *s2 = &current_program_state->linear_gradient.color_stops[i];
if (s1->offset != s2->offset ||
!gdk_rgba_equal (&s1->color, &s2->color))
{
op->color_stops.send = TRUE;
break;
}
}
}
else
op->color_stops.send = TRUE;
if (op->color_stops.send)
{
op->color_stops.value = color_stops;
memcpy (&current_program_state->linear_gradient.color_stops,
color_stops,
sizeof (GskColorStop) * real_n_color_stops);
}
op->repeat = repeat;
op->start_point[0] = start_x;
op->start_point[1] = start_y;
op->end_point[0] = end_x;
op->end_point[1] = end_y;
}
void
ops_set_radial_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
gboolean repeat,
float center_x,
float center_y,
float start,
float end,
float hradius,
float vradius)
{
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
OpRadialGradient *op;
/* TODO: State tracking? */
op = ops_begin (self, OP_CHANGE_RADIAL_GRADIENT);
op->n_color_stops.value = real_n_color_stops;
op->n_color_stops.send = true;
op->color_stops.value = color_stops;
op->color_stops.send = true;
op->center[0] = center_x;
op->center[1] = center_y;
op->radius[0] = hradius;
op->radius[1] = vradius;
op->start = start;
op->end = end;
op->repeat = repeat;
}
void
ops_set_conic_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
float center_x,
float center_y,
float angle)
{
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
OpConicGradient *op;
/* TODO: State tracking? */
op = ops_begin (self, OP_CHANGE_CONIC_GRADIENT);
op->n_color_stops.value = real_n_color_stops;
op->n_color_stops.send = true;
op->color_stops.value = color_stops;
op->color_stops.send = true;
op->center[0] = center_x;
op->center[1] = center_y;
op->angle = angle;
}

View File

@@ -1,353 +0,0 @@
#ifndef __GSK_GL_RENDER_OPS_H__
#define __GSK_GL_RENDER_OPS_H__
#include <glib.h>
#include <graphene.h>
#include <gdk/gdk.h>
#include "gskgldriverprivate.h"
#include "gskroundedrectprivate.h"
#include "gskglrenderer.h"
#include "gskrendernodeprivate.h"
#include "opbuffer.h"
#define GL_N_VERTICES 6
#define GL_N_PROGRAMS 15
#define GL_MAX_GRADIENT_STOPS 6
typedef struct
{
float scale_x;
float scale_y;
float dx_before;
float dy_before;
} OpsMatrixMetadata;
typedef struct
{
GskTransform *transform;
OpsMatrixMetadata metadata;
} MatrixStackEntry;
typedef struct
{
GskTransform *modelview;
GskRoundedRect clip;
graphene_matrix_t projection;
int source_texture;
graphene_rect_t viewport;
float opacity;
/* Per-program state */
union {
GdkRGBA color;
struct {
graphene_matrix_t matrix;
graphene_vec4_t offset;
} color_matrix;
struct {
float widths[4];
GdkRGBA color;
GskRoundedRect outline;
} border;
struct {
GskRoundedRect outline;
float dx;
float dy;
float spread;
GdkRGBA color;
} inset_shadow;
struct {
GskRoundedRect outline;
float dx;
float dy;
float spread;
GdkRGBA color;
} unblurred_outset_shadow;
struct {
int n_color_stops;
GskColorStop color_stops[GL_MAX_GRADIENT_STOPS];
float start_point[2];
float end_point[2];
} linear_gradient;
struct {
int n_color_stops;
GskColorStop color_stops[GL_MAX_GRADIENT_STOPS];
float center[2];
float start;
float end;
float radius[2]; /* h/v */
} radial_gradient;
struct {
float width;
float height;
int uniform_data_len;
guchar uniform_data[32];
} gl_shader;
};
} ProgramState;
struct _Program
{
const char *name;
int index; /* Into the renderer's program array -1 for custom */
int id;
/* Common locations (gl_common)*/
int source_location;
int position_location;
int uv_location;
int alpha_location;
int viewport_location;
int projection_location;
int modelview_location;
int clip_rect_location;
union {
struct {
int color_location;
} color;
struct {
int color_location;
} coloring;
struct {
int color_matrix_location;
int color_offset_location;
} color_matrix;
struct {
int num_color_stops_location;
int color_stops_location;
int points_location;
int repeat_location;
} linear_gradient;
struct {
int num_color_stops_location;
int color_stops_location;
int geometry_location;
int range_location;
int repeat_location;
} radial_gradient;
struct {
int num_color_stops_location;
int color_stops_location;
int geometry_location;
} conic_gradient;
struct {
int blur_radius_location;
int blur_size_location;
int blur_dir_location;
} blur;
struct {
int color_location;
int spread_location;
int offset_location;
int outline_rect_location;
} inset_shadow;
struct {
int color_location;
int outline_rect_location;
} outset_shadow;
struct {
int outline_rect_location;
int color_location;
int spread_location;
int offset_location;
} unblurred_outset_shadow;
struct {
int color_location;
int widths_location;
int outline_rect_location;
} border;
struct {
int source2_location;
int progress_location;
} cross_fade;
struct {
int source2_location;
int mode_location;
} blend;
struct {
int child_bounds_location;
int texture_rect_location;
} repeat;
struct {
int size_location;
int args_locations[8];
int texture_locations[4];
GError *compile_error;
} glshader;
};
ProgramState state;
};
typedef struct {
int ref_count;
union {
Program programs[GL_N_PROGRAMS];
struct {
Program blend_program;
Program blit_program;
Program blur_program;
Program border_program;
Program color_matrix_program;
Program color_program;
Program coloring_program;
Program cross_fade_program;
Program inset_shadow_program;
Program linear_gradient_program;
Program radial_gradient_program;
Program conic_gradient_program;
Program outset_shadow_program;
Program repeat_program;
Program unblurred_outset_shadow_program;
};
};
GHashTable *custom_programs; /* GskGLShader -> Program* */
} GskGLRendererPrograms;
typedef struct
{
GskGLRendererPrograms *programs;
Program *current_program;
int current_render_target;
int current_texture;
graphene_matrix_t current_projection;
graphene_rect_t current_viewport;
float current_opacity;
float dx, dy;
float scale_x, scale_y;
OpBuffer render_ops;
GArray *vertices;
GskGLRenderer *renderer;
/* Stack of modelview matrices */
GArray *mv_stack;
GskTransform *current_modelview;
/* Same thing */
GArray *clip_stack;
/* Pointer into clip_stack */
const GskRoundedRect *current_clip;
bool clip_is_rectilinear;
} RenderOpBuilder;
void ops_dump_framebuffer (RenderOpBuilder *builder,
const char *filename,
int width,
int height);
void ops_init (RenderOpBuilder *builder);
void ops_free (RenderOpBuilder *builder);
void ops_reset (RenderOpBuilder *builder);
void ops_push_debug_group (RenderOpBuilder *builder,
const char *text);
void ops_pop_debug_group (RenderOpBuilder *builder);
void ops_finish (RenderOpBuilder *builder);
void ops_push_modelview (RenderOpBuilder *builder,
GskTransform *transform);
void ops_set_modelview (RenderOpBuilder *builder,
GskTransform *transform);
void ops_pop_modelview (RenderOpBuilder *builder);
void ops_set_program (RenderOpBuilder *builder,
Program *program);
void ops_push_clip (RenderOpBuilder *builder,
const GskRoundedRect *clip);
void ops_pop_clip (RenderOpBuilder *builder);
gboolean ops_has_clip (RenderOpBuilder *builder);
void ops_transform_bounds_modelview (const RenderOpBuilder *builder,
const graphene_rect_t *src,
graphene_rect_t *dst);
graphene_matrix_t ops_set_projection (RenderOpBuilder *builder,
const graphene_matrix_t *projection);
graphene_rect_t ops_set_viewport (RenderOpBuilder *builder,
const graphene_rect_t *viewport);
void ops_set_texture (RenderOpBuilder *builder,
int texture_id);
void ops_set_extra_texture (RenderOpBuilder *builder,
int texture_id,
int idx);
int ops_set_render_target (RenderOpBuilder *builder,
int render_target_id);
float ops_set_opacity (RenderOpBuilder *builder,
float opacity);
void ops_set_color (RenderOpBuilder *builder,
const GdkRGBA *color);
void ops_set_color_matrix (RenderOpBuilder *builder,
const graphene_matrix_t *matrix,
const graphene_vec4_t *offset);
void ops_set_border (RenderOpBuilder *builder,
const GskRoundedRect *outline);
void ops_set_border_width (RenderOpBuilder *builder,
const float *widths);
void ops_set_border_color (RenderOpBuilder *builder,
const GdkRGBA *color);
void ops_set_inset_shadow (RenderOpBuilder *self,
const GskRoundedRect outline,
float spread,
const GdkRGBA *color,
float dx,
float dy);
void ops_set_gl_shader_args (RenderOpBuilder *builder,
GskGLShader *shader,
float width,
float height,
const guchar *uniform_data);
void ops_set_unblurred_outset_shadow (RenderOpBuilder *self,
const GskRoundedRect outline,
float spread,
const GdkRGBA *color,
float dx,
float dy);
void ops_set_linear_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
gboolean repeat,
float start_x,
float start_y,
float end_x,
float end_y);
void ops_set_radial_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
gboolean repeat,
float center_x,
float center_y,
float start,
float end,
float hradius,
float vradius);
void ops_set_conic_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
float center_x,
float center_y,
float angle);
GskQuadVertex * ops_draw (RenderOpBuilder *builder,
const GskQuadVertex vertex_data[GL_N_VERTICES]);
void ops_offset (RenderOpBuilder *builder,
float x,
float y);
gpointer ops_begin (RenderOpBuilder *builder,
OpKind kind);
OpBuffer *ops_get_buffer (RenderOpBuilder *builder);
#endif

View File

@@ -1,271 +0,0 @@
#include "config.h"
#include "gskglshaderbuilderprivate.h"
#include "gskdebugprivate.h"
#include <gdk/gdk.h>
#include <epoxy/gl.h>
void
gsk_gl_shader_builder_init (GskGLShaderBuilder *self,
const char *common_preamble_resource_path,
const char *vs_preamble_resource_path,
const char *fs_preamble_resource_path)
{
memset (self, 0, sizeof (*self));
self->preamble = g_resources_lookup_data (common_preamble_resource_path, 0, NULL);
self->vs_preamble = g_resources_lookup_data (vs_preamble_resource_path, 0, NULL);
self->fs_preamble = g_resources_lookup_data (fs_preamble_resource_path, 0, NULL);
g_assert (self->preamble);
g_assert (self->vs_preamble);
g_assert (self->fs_preamble);
}
void
gsk_gl_shader_builder_finish (GskGLShaderBuilder *self)
{
g_bytes_unref (self->preamble);
g_bytes_unref (self->vs_preamble);
g_bytes_unref (self->fs_preamble);
}
void
gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self,
int version)
{
self->version = version;
}
static void
prepend_line_numbers (char *code,
GString *s)
{
char *p;
int line;
p = code;
line = 1;
while (*p)
{
char *end = strchr (p, '\n');
if (end)
end = end + 1; /* Include newline */
else
end = p + strlen (p);
g_string_append_printf (s, "%3d| ", line++);
g_string_append_len (s, p, end - p);
p = end;
}
}
static gboolean
check_shader_error (int shader_id,
int shader_type,
const char *resource_path,
GError **error)
{
int status;
int log_len;
char *buffer;
int code_len;
char *code;
GString *s;
glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
if (G_LIKELY (status == GL_TRUE))
return TRUE;
glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len);
buffer = g_malloc0 (log_len + 1);
glGetShaderInfoLog (shader_id, log_len, NULL, buffer);
glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
code = g_malloc0 (code_len + 1);
glGetShaderSource (shader_id, code_len, NULL, code);
s = g_string_new ("");
prepend_line_numbers (code, s);
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
"Compilation failure in %s shader %s.\nSource Code:\n%s\n\nError Message:\n%s\n\n",
(shader_type == GL_FRAGMENT_SHADER ? "fragment" : "vertex"),
resource_path,
s->str,
buffer);
g_string_free (s, TRUE);
g_free (buffer);
g_free (code);
return FALSE;
}
static void
print_shader_info (const char *prefix,
int shader_id,
const char *resource_path)
{
if (GSK_DEBUG_CHECK(SHADERS))
{
int code_len;
char *code;
GString *s;
glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
code = g_malloc0 (code_len + 1);
glGetShaderSource (shader_id, code_len, NULL, code);
s = g_string_new ("");
prepend_line_numbers (code, s);
g_message ("%s %d, %s:\n%s", prefix, shader_id, resource_path, s->str);
g_string_free (s, TRUE);
g_free (code);
}
}
int
gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
const char *resource_path,
const char *extra_fragment_snippet,
gsize extra_fragment_length,
GError **error)
{
GBytes *source_bytes = g_resources_lookup_data (resource_path, 0, NULL);
char version_buffer[64];
const char *source;
const char *vertex_shader_start;
const char *fragment_shader_start;
int vertex_id;
int fragment_id;
int program_id = -1;
int status;
g_assert (source_bytes);
source = g_bytes_get_data (source_bytes, NULL);
vertex_shader_start = strstr (source, "VERTEX_SHADER");
fragment_shader_start = strstr (source, "FRAGMENT_SHADER");
g_assert (vertex_shader_start);
g_assert (fragment_shader_start);
/* They both start at the next newline */
vertex_shader_start = strstr (vertex_shader_start, "\n");
fragment_shader_start = strstr (fragment_shader_start, "\n");
g_snprintf (version_buffer, sizeof (version_buffer),
"#version %d\n", self->version);
vertex_id = glCreateShader (GL_VERTEX_SHADER);
glShaderSource (vertex_id, 8,
(const char *[]) {
version_buffer,
self->debugging ? "#define GSK_DEBUG 1\n" : "",
self->legacy ? "#define GSK_LEGACY 1\n" : "",
self->gl3 ? "#define GSK_GL3 1\n" : "",
self->gles ? "#define GSK_GLES 1\n" : "",
g_bytes_get_data (self->preamble, NULL),
g_bytes_get_data (self->vs_preamble, NULL),
vertex_shader_start
},
(int[]) {
-1,
-1,
-1,
-1,
-1,
-1,
-1,
fragment_shader_start - vertex_shader_start
});
glCompileShader (vertex_id);
if (!check_shader_error (vertex_id, GL_VERTEX_SHADER, resource_path, error))
{
glDeleteShader (vertex_id);
goto out;
}
print_shader_info ("Vertex shader", vertex_id, resource_path);
fragment_id = glCreateShader (GL_FRAGMENT_SHADER);
glShaderSource (fragment_id, 9,
(const char *[]) {
version_buffer,
self->debugging ? "#define GSK_DEBUG 1\n" : "",
self->legacy ? "#define GSK_LEGACY 1\n" : "",
self->gl3 ? "#define GSK_GL3 1\n" : "",
self->gles ? "#define GSK_GLES 1\n" : "",
g_bytes_get_data (self->preamble, NULL),
g_bytes_get_data (self->fs_preamble, NULL),
fragment_shader_start,
extra_fragment_snippet ? extra_fragment_snippet : ""
},
(int[]) {
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
extra_fragment_length,
});
glCompileShader (fragment_id);
if (!check_shader_error (fragment_id, GL_FRAGMENT_SHADER, resource_path, error))
{
glDeleteShader (fragment_id);
goto out;
}
print_shader_info ("Fragment shader", fragment_id, resource_path);
program_id = glCreateProgram ();
glAttachShader (program_id, vertex_id);
glAttachShader (program_id, fragment_id);
glBindAttribLocation (program_id, 0, "aPosition");
glBindAttribLocation (program_id, 1, "aUv");
glLinkProgram (program_id);
glDetachShader (program_id, vertex_id);
glDetachShader (program_id, fragment_id);
glGetProgramiv (program_id, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
char *buffer = NULL;
int log_len = 0;
glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len);
buffer = g_malloc0 (log_len + 1);
glGetProgramInfoLog (program_id, log_len, NULL, buffer);
g_warning ("Linking failure in shader:\n%s", buffer);
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED,
"Linking failure in shader: %s", buffer);
g_free (buffer);
glDeleteProgram (program_id);
program_id = -1;
}
glDeleteShader (vertex_id);
glDeleteShader (fragment_id);
out:
g_bytes_unref (source_bytes);
return program_id;
}

View File

@@ -1,42 +0,0 @@
#ifndef __GSK_SHADER_BUILDER_PRIVATE_H__
#define __GSK_SHADER_BUILDER_PRIVATE_H__
#include <gdk/gdk.h>
#include <graphene.h>
G_BEGIN_DECLS
typedef struct
{
GBytes *preamble;
GBytes *vs_preamble;
GBytes *fs_preamble;
int version;
guint debugging: 1;
guint gles: 1;
guint gl3: 1;
guint legacy: 1;
} GskGLShaderBuilder;
void gsk_gl_shader_builder_init (GskGLShaderBuilder *self,
const char *common_preamble_resource_path,
const char *vs_preamble_resource_path,
const char *fs_preamble_resource_path);
void gsk_gl_shader_builder_finish (GskGLShaderBuilder *self);
void gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self,
int version);
int gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
const char *resource_path,
const char *extra_fragment_snippet,
gsize extra_fragment_length,
GError **error);
G_END_DECLS
#endif /* __GSK_SHADER_BUILDER_PRIVATE_H__ */

View File

@@ -1,141 +0,0 @@
#include "gskglshadowcacheprivate.h"
#define MAX_UNUSED_FRAMES (16 * 5)
typedef struct
{
GskRoundedRect outline;
float blur_radius;
} CacheKey;
typedef struct
{
GskRoundedRect outline;
float blur_radius;
int texture_id;
int unused_frames;
} CacheItem;
static gboolean
key_equal (const void *x,
const void *y)
{
const CacheKey *a = x;
const CacheKey *b = y;
return a->blur_radius == b->blur_radius &&
graphene_size_equal (&a->outline.corner[0], &b->outline.corner[0]) &&
graphene_size_equal (&a->outline.corner[1], &b->outline.corner[1]) &&
graphene_size_equal (&a->outline.corner[2], &b->outline.corner[2]) &&
graphene_size_equal (&a->outline.corner[3], &b->outline.corner[3]) &&
graphene_rect_equal (&a->outline.bounds, &b->outline.bounds);
}
void
gsk_gl_shadow_cache_init (GskGLShadowCache *self)
{
self->textures = g_array_new (FALSE, TRUE, sizeof (CacheItem));
}
void
gsk_gl_shadow_cache_free (GskGLShadowCache *self,
GskGLDriver *gl_driver)
{
guint i, p;
for (i = 0, p = self->textures->len; i < p; i ++)
{
const CacheItem *item = &g_array_index (self->textures, CacheItem, i);
gsk_gl_driver_destroy_texture (gl_driver, item->texture_id);
}
g_array_free (self->textures, TRUE);
self->textures = NULL;
}
void
gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self,
GskGLDriver *gl_driver)
{
guint i, p;
for (i = 0, p = self->textures->len; i < p; i ++)
{
CacheItem *item = &g_array_index (self->textures, CacheItem, i);
if (item->unused_frames > MAX_UNUSED_FRAMES)
{
gsk_gl_driver_destroy_texture (gl_driver, item->texture_id);
g_array_remove_index_fast (self->textures, i);
p --;
i --;
}
else
{
item->unused_frames ++;
}
}
}
/* XXX
* The offset origin should always be at 0/0, or the blur radius should just go
* away since it defines the origin position anyway?
*/
int
gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache *self,
GskGLDriver *gl_driver,
const GskRoundedRect *shadow_rect,
float blur_radius)
{
CacheItem *item= NULL;
guint i;
g_assert (self != NULL);
g_assert (gl_driver != NULL);
g_assert (shadow_rect != NULL);
for (i = 0; i < self->textures->len; i ++)
{
CacheItem *k = &g_array_index (self->textures, CacheItem, i);
if (key_equal (&(CacheKey){*shadow_rect, blur_radius},
&(CacheKey){k->outline, k->blur_radius}))
{
item = k;
break;
}
}
if (item == NULL)
return 0;
item->unused_frames = 0;
g_assert (item->texture_id != 0);
return item->texture_id;
}
void
gsk_gl_shadow_cache_commit (GskGLShadowCache *self,
const GskRoundedRect *shadow_rect,
float blur_radius,
int texture_id)
{
CacheItem *item;
g_assert (self != NULL);
g_assert (shadow_rect != NULL);
g_assert (texture_id > 0);
g_array_set_size (self->textures, self->textures->len + 1);
item = &g_array_index (self->textures, CacheItem, self->textures->len - 1);
item->outline = *shadow_rect;
item->blur_radius = blur_radius;
item->unused_frames = 0;
item->texture_id = texture_id;
}

View File

@@ -1,31 +0,0 @@
#ifndef __GSK_GL_SHADOW_CACHE_H__
#define __GSK_GL_SHADOW_CACHE_H__
#include <glib.h>
#include "gskgldriverprivate.h"
#include "gskroundedrect.h"
typedef struct
{
GArray *textures;
} GskGLShadowCache;
void gsk_gl_shadow_cache_init (GskGLShadowCache *self);
void gsk_gl_shadow_cache_free (GskGLShadowCache *self,
GskGLDriver *gl_driver);
void gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self,
GskGLDriver *gl_driver);
int gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache *self,
GskGLDriver *gl_driver,
const GskRoundedRect *shadow_rect,
float blur_radius);
void gsk_gl_shadow_cache_commit (GskGLShadowCache *self,
const GskRoundedRect *shadow_rect,
float blur_radius,
int texture_id);
#endif

View File

@@ -1,309 +0,0 @@
#include "config.h"
#include "gskgltextureatlasprivate.h"
#include "gskdebugprivate.h"
#include "gdkglcontextprivate.h"
#include <epoxy/gl.h>
#define ATLAS_SIZE (512)
#define MAX_OLD_RATIO 0.5
static void
free_atlas (gpointer v)
{
GskGLTextureAtlas *atlas = v;
gsk_gl_texture_atlas_free (atlas);
g_free (atlas);
}
GskGLTextureAtlases *
gsk_gl_texture_atlases_new (void)
{
GskGLTextureAtlases *self;
self = g_new (GskGLTextureAtlases, 1);
self->atlases = g_ptr_array_new_with_free_func (free_atlas);
self->ref_count = 1;
return self;
}
GskGLTextureAtlases *
gsk_gl_texture_atlases_ref (GskGLTextureAtlases *self)
{
self->ref_count++;
return self;
}
void
gsk_gl_texture_atlases_unref (GskGLTextureAtlases *self)
{
g_assert (self->ref_count > 0);
if (self->ref_count == 1)
{
g_ptr_array_unref (self->atlases);
g_free (self);
return;
}
self->ref_count--;
}
#if 0
static void
write_atlas_to_png (GskGLTextureAtlas *atlas,
const char *filename)
{
int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
guchar *data = g_malloc (atlas->height * stride);
cairo_surface_t *s;
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride);
cairo_surface_write_to_png (s, filename);
cairo_surface_destroy (s);
g_free (data);
}
#endif
void
gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *self,
GPtrArray *removed)
{
int i;
for (i = self->atlases->len - 1; i >= 0; i--)
{
GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
{
GSK_NOTE(GLYPH_CACHE,
g_message ("Dropping atlas %d (%g.2%% old)", i,
100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
if (atlas->texture_id != 0)
{
glDeleteTextures (1, &atlas->texture_id);
atlas->texture_id = 0;
}
g_ptr_array_add (removed, atlas);
g_ptr_array_remove_index (self->atlases, i);
}
}
GSK_NOTE(GLYPH_CACHE, {
static guint timestamp;
if (timestamp++ % 60 == 0)
g_message ("%d atlases", self->atlases->len);
});
#if 0
{
static guint timestamp;
timestamp++;
if (timestamp % 10 == 0)
for (i = 0; i < self->atlases->len; i++)
{
GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
if (atlas->texture_id)
{
char *filename;
filename = g_strdup_printf ("textureatlas%d-%u.png", i, timestamp);
write_atlas_to_png (atlas, filename);
g_free (filename);
}
}
}
#endif
}
gboolean
gsk_gl_texture_atlases_pack (GskGLTextureAtlases *self,
int width,
int height,
GskGLTextureAtlas **atlas_out,
int *out_x,
int *out_y)
{
GskGLTextureAtlas *atlas;
int x, y;
int i;
g_assert (width < ATLAS_SIZE);
g_assert (height < ATLAS_SIZE);
atlas = NULL;
for (i = 0; i < self->atlases->len; i++)
{
atlas = g_ptr_array_index (self->atlases, i);
if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
break;
atlas = NULL;
}
if (atlas == NULL)
{
/* No atlas has enough space, so create a new one... */
atlas = g_malloc (sizeof (GskGLTextureAtlas));
gsk_gl_texture_atlas_init (atlas, ATLAS_SIZE, ATLAS_SIZE);
gsk_gl_texture_atlas_realize (atlas);
g_ptr_array_add (self->atlases, atlas);
/* Pack it onto that one, which surely has enough space... */
if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
g_assert_not_reached ();
GSK_NOTE(GLYPH_CACHE, g_message ("adding new atlas"));
}
*atlas_out = atlas;
*out_x = x;
*out_y = y;
return TRUE;
}
void
gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
int width,
int height)
{
memset (self, 0, sizeof (*self));
self->texture_id = 0;
self->width = width;
self->height = height;
/* TODO: We might want to change the strategy about the amount of
* nodes here? stb_rect_pack.h says with is optimal. */
self->nodes = g_malloc0 (sizeof (struct stbrp_node) * width);
stbrp_init_target (&self->context,
width, height,
self->nodes,
width);
gsk_gl_texture_atlas_realize (self);
}
void
gsk_gl_texture_atlas_free (GskGLTextureAtlas *self)
{
if (self->texture_id != 0)
{
glDeleteTextures (1, &self->texture_id);
self->texture_id = 0;
}
g_clear_pointer (&self->nodes, g_free);
}
void
gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
int width,
int height)
{
self->unused_pixels += (width * height);
}
void
gsk_gl_texture_atlas_mark_used (GskGLTextureAtlas *self,
int width,
int height)
{
self->unused_pixels -= (width * height);
g_assert (self->unused_pixels >= 0);
}
gboolean
gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
int width,
int height,
int *out_x,
int *out_y)
{
stbrp_rect rect;
g_assert (out_x);
g_assert (out_y);
rect.w = width;
rect.h = height;
stbrp_pack_rects (&self->context, &rect, 1);
if (rect.was_packed)
{
*out_x = rect.x;
*out_y = rect.y;
}
return rect.was_packed;
}
double
gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self)
{
if (self->unused_pixels > 0)
return (double)(self->unused_pixels) / (double)(self->width * self->height);
return 0.0;
}
/* Not using gdk_gl_driver_create_texture here, since we want
* this texture to survive the driver and stay around until
* the display gets closed.
*/
static guint
create_shared_texture (int width,
int height)
{
guint texture_id;
glGenTextures (1, &texture_id);
glBindTexture (GL_TEXTURE_2D, texture_id);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
else
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glBindTexture (GL_TEXTURE_2D, 0);
return texture_id;
}
void
gsk_gl_texture_atlas_realize (GskGLTextureAtlas *atlas)
{
if (atlas->texture_id)
return;
atlas->texture_id = create_shared_texture (atlas->width, atlas->height);
gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
GL_TEXTURE, atlas->texture_id,
"Texture atlas %d", atlas->texture_id);
}

View File

@@ -1,72 +0,0 @@
#ifndef __GSK_GL_TEXTURE_ATLAS_H__
#define __GSK_GL_TEXTURE_ATLAS_H__
#include "stb_rect_pack.h"
#include "gskglimageprivate.h"
#include "gskgldriverprivate.h"
struct _GskGLTextureAtlas
{
struct stbrp_context context;
struct stbrp_node *nodes;
int width;
int height;
guint texture_id;
int unused_pixels; /* Pixels of rects that have been used at some point,
But are now unused. */
void *user_data;
};
typedef struct _GskGLTextureAtlas GskGLTextureAtlas;
struct _GskGLTextureAtlases
{
int ref_count;
GPtrArray *atlases;
};
typedef struct _GskGLTextureAtlases GskGLTextureAtlases;
GskGLTextureAtlases *gsk_gl_texture_atlases_new (void);
GskGLTextureAtlases *gsk_gl_texture_atlases_ref (GskGLTextureAtlases *atlases);
void gsk_gl_texture_atlases_unref (GskGLTextureAtlases *atlases);
void gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *atlases,
GPtrArray *removed);
gboolean gsk_gl_texture_atlases_pack (GskGLTextureAtlases *atlases,
int width,
int height,
GskGLTextureAtlas **atlas_out,
int *out_x,
int *out_y);
void gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
int width,
int height);
void gsk_gl_texture_atlas_free (GskGLTextureAtlas *self);
void gsk_gl_texture_atlas_realize (GskGLTextureAtlas *self);
void gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
int width,
int height);
void gsk_gl_texture_atlas_mark_used (GskGLTextureAtlas *self,
int width,
int height);
gboolean gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
int width,
int height,
int *out_x,
int *out_y);
double gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self);
#endif

View File

@@ -1,137 +0,0 @@
#include "opbuffer.h"
#include <string.h>
static guint op_sizes[OP_LAST] = {
0,
sizeof (OpOpacity),
sizeof (OpColor),
sizeof (OpMatrix),
sizeof (OpMatrix),
sizeof (OpProgram),
sizeof (OpRenderTarget),
sizeof (OpClip),
sizeof (OpViewport),
sizeof (OpTexture),
sizeof (OpRepeat),
sizeof (OpLinearGradient),
sizeof (OpRadialGradient),
sizeof (OpColorMatrix),
sizeof (OpBlur),
sizeof (OpShadow),
sizeof (OpOutsetShadow),
sizeof (OpBorder),
sizeof (OpBorder),
sizeof (OpBorder),
sizeof (OpCrossFade),
sizeof (OpShadow),
0,
sizeof (OpDraw),
sizeof (OpDumpFrameBuffer),
sizeof (OpDebugGroup),
0,
sizeof (OpBlend),
sizeof (OpGLShader),
sizeof (OpExtraTexture),
sizeof (OpConicGradient),
};
void
op_buffer_init (OpBuffer *buffer)
{
static gsize initialized = FALSE;
if (g_once_init_enter (&initialized))
{
guint i;
for (i = 0; i < G_N_ELEMENTS (op_sizes); i++)
{
guint size = op_sizes[i];
if (size > 0)
{
/* Round all op entry sizes to the nearest 16 to ensure
* that we guarantee proper alignments for all op entries.
* This is only done once on first use.
*/
#define CHECK_SIZE(s) else if (size < (s)) { size = s; }
if (0) {}
CHECK_SIZE (16)
CHECK_SIZE (32)
CHECK_SIZE (48)
CHECK_SIZE (64)
CHECK_SIZE (80)
CHECK_SIZE (96)
CHECK_SIZE (112)
CHECK_SIZE (128)
CHECK_SIZE (144)
CHECK_SIZE (160)
CHECK_SIZE (176)
CHECK_SIZE (192)
else g_assert_not_reached ();
#undef CHECK_SIZE
op_sizes[i] = size;
}
}
g_once_init_leave (&initialized, TRUE);
}
memset (buffer, 0, sizeof *buffer);
buffer->buflen = 4096;
buffer->bufpos = 0;
buffer->buf = g_malloc (buffer->buflen);
buffer->index = g_array_new (FALSE, FALSE, sizeof (OpBufferEntry));
/* Add dummy entry to guarantee non-empty index */
op_buffer_add (buffer, OP_NONE);
}
void
op_buffer_destroy (OpBuffer *buffer)
{
g_free (buffer->buf);
g_array_unref (buffer->index);
}
void
op_buffer_clear (OpBuffer *buffer)
{
if (buffer->index->len > 1)
g_array_remove_range (buffer->index, 1, buffer->index->len - 1);
buffer->bufpos = 0;
}
static inline void
ensure_buffer_space_for (OpBuffer *buffer,
guint size)
{
if G_UNLIKELY (buffer->bufpos + size >= buffer->buflen)
{
buffer->buflen *= 2;
buffer->buf = g_realloc (buffer->buf, buffer->buflen);
}
}
gpointer
op_buffer_add (OpBuffer *buffer,
OpKind kind)
{
guint size = op_sizes[kind];
OpBufferEntry entry;
entry.pos = buffer->bufpos;
entry.kind = kind;
if (size > 0)
ensure_buffer_space_for (buffer, size);
g_array_append_val (buffer->index, entry);
buffer->bufpos += size;
return &buffer->buf[entry.pos];
}

View File

@@ -1,306 +0,0 @@
#ifndef __OP_BUFFER_H__
#define __OP_BUFFER_H__
#include <gdk/gdk.h>
#include <gsk/gsk.h>
#include <graphene.h>
#include "gskgldriverprivate.h"
typedef struct _Program Program;
typedef enum
{
OP_NONE = 0,
OP_CHANGE_OPACITY = 1,
OP_CHANGE_COLOR = 2,
OP_CHANGE_PROJECTION = 3,
OP_CHANGE_MODELVIEW = 4,
OP_CHANGE_PROGRAM = 5,
OP_CHANGE_RENDER_TARGET = 6,
OP_CHANGE_CLIP = 7,
OP_CHANGE_VIEWPORT = 8,
OP_CHANGE_SOURCE_TEXTURE = 9,
OP_CHANGE_REPEAT = 10,
OP_CHANGE_LINEAR_GRADIENT = 11,
OP_CHANGE_RADIAL_GRADIENT = 12,
OP_CHANGE_COLOR_MATRIX = 13,
OP_CHANGE_BLUR = 14,
OP_CHANGE_INSET_SHADOW = 15,
OP_CHANGE_OUTSET_SHADOW = 16,
OP_CHANGE_BORDER = 17,
OP_CHANGE_BORDER_COLOR = 18,
OP_CHANGE_BORDER_WIDTH = 19,
OP_CHANGE_CROSS_FADE = 20,
OP_CHANGE_UNBLURRED_OUTSET_SHADOW = 21,
OP_CLEAR = 22,
OP_DRAW = 23,
OP_DUMP_FRAMEBUFFER = 24,
OP_PUSH_DEBUG_GROUP = 25,
OP_POP_DEBUG_GROUP = 26,
OP_CHANGE_BLEND = 27,
OP_CHANGE_GL_SHADER_ARGS = 28,
OP_CHANGE_EXTRA_SOURCE_TEXTURE = 29,
OP_CHANGE_CONIC_GRADIENT = 30,
OP_LAST
} OpKind;
typedef struct { int value; guint send: 1; } IntUniformValue;
typedef struct { float value; guint send: 1; } FloatUniformValue;
typedef struct { float value[2]; guint send: 1; } Float2UniformValue;
typedef struct { GskRoundedRect value; guint send: 1; guint send_corners: 1; } RRUniformValue;
typedef struct { const GdkRGBA *value; guint send: 1; } RGBAUniformValue;
typedef struct { const graphene_vec4_t *value; guint send: 1; } Vec4UniformValue;
typedef struct { const GskColorStop *value; guint send: 1; } ColorStopUniformValue;
typedef struct { const graphene_matrix_t *value; guint send: 1; } MatrixUniformValue;
/* OpNode are allocated within OpBuffer.pos, but we keep
* a secondary index into the locations of that buffer
* from OpBuffer.index. This allows peeking at the kind
* and quickly replacing existing entries when necessary.
*/
typedef struct
{
RRUniformValue outline;
FloatUniformValue spread;
Float2UniformValue offset;
RGBAUniformValue color;
} OpShadow;
typedef struct
{
RRUniformValue outline;
} OpOutsetShadow;
typedef struct
{
guint pos;
OpKind kind;
} OpBufferEntry;
typedef struct
{
guint8 *buf;
gsize buflen;
gsize bufpos;
GArray *index;
} OpBuffer;
typedef struct
{
float opacity;
} OpOpacity;
typedef struct
{
graphene_matrix_t matrix;
} OpMatrix;
typedef struct
{
const Program *program;
} OpProgram;
typedef struct
{
const GdkRGBA *rgba;
} OpColor;
typedef struct
{
int render_target_id;
} OpRenderTarget;
typedef struct
{
GskRoundedRect clip;
guint send_corners: 1;
} OpClip;
typedef struct
{
graphene_rect_t viewport;
} OpViewport;
typedef struct
{
int texture_id;
} OpTexture;
typedef struct
{
int texture_id;
int idx;
} OpExtraTexture;
typedef struct
{
gsize vao_offset;
gsize vao_size;
} OpDraw;
typedef struct
{
ColorStopUniformValue color_stops;
IntUniformValue n_color_stops;
float start_point[2];
float end_point[2];
gboolean repeat;
} OpLinearGradient;
typedef struct
{
ColorStopUniformValue color_stops;
IntUniformValue n_color_stops;
float start;
float end;
float radius[2];
float center[2];
gboolean repeat;
} OpRadialGradient;
typedef struct
{
ColorStopUniformValue color_stops;
IntUniformValue n_color_stops;
float center[2];
float angle;
} OpConicGradient;
typedef struct
{
MatrixUniformValue matrix;
Vec4UniformValue offset;
} OpColorMatrix;
typedef struct
{
float radius;
graphene_size_t size;
float dir[2];
} OpBlur;
typedef struct
{
float widths[4];
const GdkRGBA *color;
GskRoundedRect outline;
} OpBorder;
typedef struct
{
float progress;
int source2;
} OpCrossFade;
typedef struct
{
char *filename;
int width;
int height;
} OpDumpFrameBuffer;
typedef struct
{
char text[64];
} OpDebugGroup;
typedef struct
{
int source2;
int mode;
} OpBlend;
typedef struct
{
float child_bounds[4];
float texture_rect[4];
} OpRepeat;
typedef struct
{
float size[2];
GskGLShader *shader;
const guchar *uniform_data;
} OpGLShader;
void op_buffer_init (OpBuffer *buffer);
void op_buffer_destroy (OpBuffer *buffer);
void op_buffer_clear (OpBuffer *buffer);
gpointer op_buffer_add (OpBuffer *buffer,
OpKind kind);
typedef struct
{
GArray *index;
OpBuffer *buffer;
guint pos;
} OpBufferIter;
static inline void
op_buffer_iter_init (OpBufferIter *iter,
OpBuffer *buffer)
{
iter->index = buffer->index;
iter->buffer = buffer;
iter->pos = 1; /* Skip first OP_NONE */
}
static inline gpointer
op_buffer_iter_next (OpBufferIter *iter,
OpKind *kind)
{
const OpBufferEntry *entry;
if (iter->pos == iter->index->len)
return NULL;
entry = &g_array_index (iter->index, OpBufferEntry, iter->pos);
iter->pos++;
*kind = entry->kind;
return &iter->buffer->buf[entry->pos];
}
static inline void
op_buffer_pop_tail (OpBuffer *buffer)
{
/* Never truncate the first OP_NONE */
if G_LIKELY (buffer->index->len > 0)
buffer->index->len--;
}
static inline gpointer
op_buffer_peek_tail (OpBuffer *buffer,
OpKind *kind)
{
const OpBufferEntry *entry;
entry = &g_array_index (buffer->index, OpBufferEntry, buffer->index->len - 1);
*kind = entry->kind;
return &buffer->buf[entry->pos];
}
static inline gpointer
op_buffer_peek_tail_checked (OpBuffer *buffer,
OpKind kind)
{
const OpBufferEntry *entry;
entry = &g_array_index (buffer->index, OpBufferEntry, buffer->index->len - 1);
if (entry->kind == kind)
return &buffer->buf[entry->pos];
return NULL;
}
static inline guint
op_buffer_n_ops (OpBuffer *buffer)
{
return buffer->index->len - 1;
}
#endif /* __OP_BUFFER_H__ */

View File

@@ -1,310 +0,0 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
uniform int u_mode;
uniform sampler2D u_source2;
float
combine (float source, float backdrop)
{
return source + backdrop * (1.0 - source);
}
vec4
composite (vec4 Cs, vec4 Cb, vec3 B)
{
float ao = Cs.a + Cb.a * (1.0 - Cs.a);
vec3 Co = (Cs.a*(1.0 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1.0 - Cs.a)*Cb.a*Cb.rgb) / ao;
return vec4(Co, ao);
}
vec4
normal (vec4 Cs, vec4 Cb)
{
return composite (Cs, Cb, Cs.rgb);
}
vec4
multiply (vec4 Cs, vec4 Cb)
{
return composite (Cs, Cb, Cs.rgb * Cb.rgb);
}
vec4
difference (vec4 Cs, vec4 Cb)
{
return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb));
}
vec4
screen (vec4 Cs, vec4 Cb)
{
return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb);
}
float
hard_light (float source, float backdrop)
{
if (source <= 0.5)
return 2.0 * backdrop * source;
else
return 2.0 * (backdrop + source - backdrop * source) - 1.0;
}
vec4
hard_light (vec4 Cs, vec4 Cb)
{
vec3 B = vec3 (hard_light (Cs.r, Cb.r),
hard_light (Cs.g, Cb.g),
hard_light (Cs.b, Cb.b));
return composite (Cs, Cb, B);
}
float
soft_light (float source, float backdrop)
{
float db;
if (backdrop <= 0.25)
db = ((16.0 * backdrop - 12.0) * backdrop + 4.0) * backdrop;
else
db = sqrt (backdrop);
if (source <= 0.5)
return backdrop - (1.0 - 2.0 * source) * backdrop * (1.0 - backdrop);
else
return backdrop + (2.0 * source - 1.0) * (db - backdrop);
}
vec4
soft_light (vec4 Cs, vec4 Cb)
{
vec3 B = vec3 (soft_light (Cs.r, Cb.r),
soft_light (Cs.g, Cb.g),
soft_light (Cs.b, Cb.b));
return composite (Cs, Cb, B);
}
vec4
overlay (vec4 Cs, vec4 Cb)
{
vec3 B = vec3 (hard_light (Cb.r, Cs.r),
hard_light (Cb.g, Cs.g),
hard_light (Cb.b, Cs.b));
return composite (Cs, Cb, B);
}
vec4
darken (vec4 Cs, vec4 Cb)
{
vec3 B = min (Cs.rgb, Cb.rgb);
return composite (Cs, Cb, B);
}
vec4
lighten (vec4 Cs, vec4 Cb)
{
vec3 B = max (Cs.rgb, Cb.rgb);
return composite (Cs, Cb, B);
}
float
color_dodge (float source, float backdrop)
{
return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0);
}
vec4
color_dodge (vec4 Cs, vec4 Cb)
{
vec3 B = vec3 (color_dodge (Cs.r, Cb.r),
color_dodge (Cs.g, Cb.g),
color_dodge (Cs.b, Cb.b));
return composite (Cs, Cb, B);
}
float
color_burn (float source, float backdrop)
{
return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0);
}
vec4
color_burn (vec4 Cs, vec4 Cb)
{
vec3 B = vec3 (color_burn (Cs.r, Cb.r),
color_burn (Cs.g, Cb.g),
color_burn (Cs.b, Cb.b));
return composite (Cs, Cb, B);
}
vec4
exclusion (vec4 Cs, vec4 Cb)
{
vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb;
return composite (Cs, Cb, B);
}
float
lum (vec3 c)
{
return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;
}
vec3
clip_color (vec3 c)
{
float l = lum (c);
float n = min (c.r, min (c.g, c.b));
float x = max (c.r, max (c.g, c.b));
if (n < 0.0) c = l + (((c - l) * l) / (l - n));
if (x > 1.0) c = l + (((c - l) * (1.0 - l)) / (x - l));
return c;
}
vec3
set_lum (vec3 c, float l)
{
float d = l - lum (c);
return clip_color (vec3 (c.r + d, c.g + d, c.b + d));
}
float
sat (vec3 c)
{
return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b));
}
vec3
set_sat (vec3 c, float s)
{
float cmin = min (c.r, min (c.g, c.b));
float cmax = max (c.r, max (c.g, c.b));
vec3 res;
if (cmax == cmin)
res = vec3 (0, 0, 0);
else
{
if (c.r == cmax)
{
if (c.g == cmin)
{
res.b = ((c.b - cmin) * s) / (cmax - cmin);
res.g = 0.0;
}
else
{
res.g = ((c.g - cmin) * s) / (cmax - cmin);
res.b = 0.0;
}
res.r = s;
}
else if (c.g == cmax)
{
if (c.r == cmin)
{
res.b = ((c.b - cmin) * s) / (cmax - cmin);
res.r = 0.0;
}
else
{
res.r = ((c.r - cmin) * s) / (cmax - cmin);
res.b = 0.0;
}
res.g = s;
}
else
{
if (c.r == cmin)
{
res.g = ((c.g - cmin) * s) / (cmax - cmin);
res.r = 0.0;
}
else
{
res.r = ((c.r - cmin) * s) / (cmax - cmin);
res.g = 0.0;
}
res.b = s;
}
}
return res;
}
vec4
color (vec4 Cs, vec4 Cb)
{
vec3 B = set_lum (Cs.rgb, lum (Cb.rgb));
return composite (Cs, Cb, B);
}
vec4
hue (vec4 Cs, vec4 Cb)
{
vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb));
return composite (Cs, Cb, B);
}
vec4
saturation (vec4 Cs, vec4 Cb)
{
vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb));
return composite (Cs, Cb, B);
}
vec4
luminosity (vec4 Cs, vec4 Cb)
{
vec3 B = set_lum (Cb.rgb, lum (Cs.rgb));
return composite (Cs, Cb, B);
}
void main() {
vec4 bottom_color = GskTexture(u_source, vUv);
vec4 top_color = GskTexture(u_source2, vUv);
vec4 result;
if (u_mode == 0)
result = normal(top_color, bottom_color);
else if (u_mode == 1)
result = multiply(top_color, bottom_color);
else if (u_mode == 2)
result = screen(top_color, bottom_color);
else if (u_mode == 3)
result = overlay(top_color, bottom_color);
else if (u_mode == 4)
result = darken(top_color, bottom_color);
else if (u_mode == 5)
result = lighten(top_color, bottom_color);
else if (u_mode == 6)
result = color_dodge(top_color, bottom_color);
else if (u_mode == 7)
result = color_burn(top_color, bottom_color);
else if (u_mode == 8)
result = hard_light(top_color, bottom_color);
else if (u_mode == 9)
result = soft_light(top_color, bottom_color);
else if (u_mode == 10)
result = difference(top_color, bottom_color);
else if (u_mode == 11)
result = exclusion(top_color, bottom_color);
else if (u_mode == 12)
result = color(top_color, bottom_color);
else if (u_mode == 13)
result = hue(top_color, bottom_color);
else if (u_mode == 14)
result = saturation(top_color, bottom_color);
else if (u_mode == 15)
result = luminosity(top_color, bottom_color);
else
discard;
gskSetOutputColor(result * u_alpha);
}

View File

@@ -1,13 +0,0 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
void main() {
vec4 diffuse = GskTexture(u_source, vUv);
gskSetOutputColor(diffuse * u_alpha);
}

View File

@@ -1,55 +0,0 @@
// VERTEX_SHADER:
uniform float u_blur_radius;
uniform vec2 u_blur_size;
uniform vec2 u_blur_dir;
_OUT_ vec2 pixel_step;
_OUT_ float pixels_per_side;
_OUT_ vec3 initial_gaussian;
const float PI = 3.14159265;
const float RADIUS_MULTIPLIER = 2.0;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
pixel_step = (vec2(1.0) / u_blur_size) * u_blur_dir;
pixels_per_side = floor(u_blur_radius * RADIUS_MULTIPLIER / 2.0);
float sigma = u_blur_radius / 2.0; // *shrug*
initial_gaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma);
initial_gaussian.y = exp(-0.5 / (sigma * sigma));
initial_gaussian.z = initial_gaussian.y * initial_gaussian.y;
}
// FRAGMENT_SHADER:
_IN_ vec2 pixel_step;
_IN_ float pixels_per_side;
_IN_ vec3 initial_gaussian;
// blur_radius 0 is NOT supported and MUST be caught before.
// Partially from http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html
void main() {
vec3 incrementalGaussian = initial_gaussian;
float coefficientSum = 0.0;
vec4 sum = GskTexture(u_source, vUv) * incrementalGaussian.x;
coefficientSum += incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
vec2 p = pixel_step;
for (int i = 1; i <= int(pixels_per_side); i++) {
sum += GskTexture(u_source, vUv - p) * incrementalGaussian.x;
sum += GskTexture(u_source, vUv + p) * incrementalGaussian.x;
coefficientSum += 2.0 * incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
p += pixel_step;
}
gskSetOutputColor(sum / coefficientSum);
}

View File

@@ -1,40 +0,0 @@
// VERTEX_SHADER:
uniform vec4 u_color;
uniform vec4 u_widths;
uniform vec4[3] u_outline_rect;
_OUT_ vec4 final_color;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
final_color = gsk_premultiply(u_color) * u_alpha;
GskRoundedRect outside = gsk_create_rect(u_outline_rect);
GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}
// FRAGMENT_SHADER:
uniform vec4[3] u_outline_rect;
_IN_ vec4 final_color;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
vec2 frag = gsk_get_frag_coord();
float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
0.0, 1.0);
gskSetOutputColor(final_color * alpha);
}

View File

@@ -1,18 +0,0 @@
// VERTEX_SHADER:
uniform vec4 u_color;
_OUT_ vec4 final_color;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
final_color = gsk_premultiply(u_color) * u_alpha;
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
void main() {
gskSetOutputColor(final_color);
}

View File

@@ -1,25 +0,0 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
uniform mat4 u_color_matrix;
uniform vec4 u_color_offset;
void main() {
vec4 color = GskTexture(u_source, vUv);
// Un-premultilpy
if (color.a != 0.0)
color.rgb /= color.a;
color = u_color_matrix * color + u_color_offset;
color = clamp(color, 0.0, 1.0);
color.rgb *= color.a;
gskSetOutputColor(color * u_alpha);
}

View File

@@ -1,22 +0,0 @@
// VERTEX_SHADER:
uniform vec4 u_color;
_OUT_ vec4 final_color;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
final_color = gsk_premultiply(u_color) * u_alpha;
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
void main() {
vec4 diffuse = GskTexture(u_source, vUv);
gskSetOutputColor(final_color * diffuse.a);
}

View File

@@ -1,73 +0,0 @@
// VERTEX_SHADER
uniform vec4 u_geometry;
_NOPERSPECTIVE_ _OUT_ vec2 coord;
void main() {
gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
vec2 mv0 = u_modelview[0].xy;
vec2 mv1 = u_modelview[1].xy;
vec2 offset = aPosition - u_geometry.xy;
coord = vec2(dot(mv0, offset),
dot(mv1, offset));
}
// FRAGMENT_SHADER:
#ifdef GSK_LEGACY
uniform int u_num_color_stops;
#else
uniform highp int u_num_color_stops; // Why? Because it works like this.
#endif
uniform vec4 u_geometry;
uniform float u_color_stops[6 * 5];
_NOPERSPECTIVE_ _IN_ vec2 coord;
float get_offset(int index) {
return u_color_stops[5 * index];
}
vec4 get_color(int index) {
int base = 5 * index + 1;
return vec4(u_color_stops[base],
u_color_stops[base + 1],
u_color_stops[base + 2],
u_color_stops[base + 3]);
}
void main() {
// direction of point in range [-PI, PI]
vec2 pos = floor(coord);
float angle = atan(pos.y, pos.x);
// fract() does the modulo here, so now we have progress
// into the current conic
float offset = fract(angle * u_geometry.z + u_geometry.w);
if (offset < get_offset(0)) {
gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
return;
}
int n = u_num_color_stops - 1;
for (int i = 0; i < n; i++) {
float curr_offset = get_offset(i);
float next_offset = get_offset(i + 1);
if (offset >= curr_offset && offset < next_offset) {
float f = (offset - curr_offset) / (next_offset - curr_offset);
vec4 curr_color = gsk_premultiply(get_color(i));
vec4 next_color = gsk_premultiply(get_color(i + 1));
vec4 color = mix(curr_color, next_color, f);
gskSetOutputColor(color * u_alpha);
return;
}
}
gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha));
}

View File

@@ -1,20 +0,0 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
uniform float u_progress;
uniform sampler2D u_source2;
void main() {
vec4 source1 = GskTexture(u_source, vUv); // start child
vec4 source2 = GskTexture(u_source2, vUv); // end child
float p_start = (1.0 - u_progress) * u_alpha;
float p_end = u_progress * u_alpha;
vec4 color = (p_start * source1) + (p_end * source2);
gskSetOutputColor(color);
}

View File

@@ -1,21 +0,0 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
// The shader supplies:
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv);
uniform vec2 u_size;
uniform sampler2D u_source2;
uniform sampler2D u_source3;
uniform sampler2D u_source4;
void main() {
vec4 fragColor;
vec2 fragCoord = vec2(vUv.x * u_size.x, (1.0-vUv.y) * u_size.y);
mainImage(fragColor, fragCoord, u_size, vUv);
gskSetOutputColor(fragColor);
}

View File

@@ -1,41 +0,0 @@
// VERTEX_SHADER:
uniform vec4 u_color;
uniform float u_spread;
uniform vec2 u_offset;
uniform vec4[3] u_outline_rect;
_OUT_ vec4 final_color;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
final_color = gsk_premultiply(u_color) * u_alpha;
GskRoundedRect outside = gsk_create_rect(u_outline_rect);
GskRoundedRect inside = gsk_rounded_rect_shrink(outside, vec4(u_spread));
gsk_rounded_rect_offset(inside, u_offset);
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
vec2 frag = gsk_get_frag_coord();
float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
0.0, 1.0);
gskSetOutputColor(final_color * alpha);
}

View File

@@ -1,95 +0,0 @@
// VERTEX_SHADER
uniform vec4 u_points;
_NOPERSPECTIVE_ _OUT_ vec4 info;
void main() {
gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
vec2 mv0 = u_modelview[0].xy;
vec2 mv1 = u_modelview[1].xy;
vec2 offset = aPosition - u_points.xy;
vec2 coord = vec2(dot(mv0, offset),
dot(mv1, offset));
// Original equation:
// VS | maxDist = length(end - start);
// VS | gradient = end - start;
// VS | gradientLength = length(gradient);
// FS | pos = frag_coord - start
// FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient
// FS | offset = length(proj) / maxDist
// Simplified formula derivation:
// 1. Notice that maxDist = gradientLength:
// offset = length(proj) / gradientLength
// 2. Let gnorm = gradient / gradientLength, then:
// proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) =
// = dot(gnorm, pos) * gnorm
// 3. Since gnorm is unit length then:
// length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos)
// 4. We can avoid the FS division by passing a scaled pos from the VS:
// offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength)
// 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL
vec2 gradient = vec2(dot(mv0, u_points.zw),
dot(mv1, u_points.zw));
float rcp_gradient_length = inversesqrt(dot(gradient, gradient));
info = rcp_gradient_length * vec4(coord, gradient);
}
// FRAGMENT_SHADER:
#ifdef GSK_LEGACY
uniform int u_num_color_stops;
#else
uniform highp int u_num_color_stops; // Why? Because it works like this.
#endif
uniform float u_color_stops[6 * 5];
uniform bool u_repeat;
_NOPERSPECTIVE_ _IN_ vec4 info;
float get_offset(int index) {
return u_color_stops[5 * index];
}
vec4 get_color(int index) {
int base = 5 * index + 1;
return vec4(u_color_stops[base],
u_color_stops[base + 1],
u_color_stops[base + 2],
u_color_stops[base + 3]);
}
void main() {
float offset = dot(info.xy, info.zw);
if (u_repeat) {
offset = fract(offset);
}
if (offset < get_offset(0)) {
gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
return;
}
int n = u_num_color_stops - 1;
for (int i = 0; i < n; i++) {
float curr_offset = get_offset(i);
float next_offset = get_offset(i + 1);
if (offset >= curr_offset && offset < next_offset) {
float f = (offset - curr_offset) / (next_offset - curr_offset);
vec4 curr_color = gsk_premultiply(get_color(i));
vec4 next_color = gsk_premultiply(get_color(i + 1));
vec4 color = mix(curr_color, next_color, f);
gskSetOutputColor(color * u_alpha);
return;
}
}
gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha));
}

View File

@@ -1,33 +0,0 @@
// VERTEX_SHADER:
uniform vec4 u_color;
uniform vec4[3] u_outline_rect;
_OUT_ vec4 final_color;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
final_color = gsk_premultiply(u_color) * u_alpha;
GskRoundedRect outline = gsk_create_rect(u_outline_rect);
gsk_rounded_rect_transform(outline, u_modelview);
gsk_rounded_rect_encode(outline, transformed_outline);
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
void main() {
vec2 frag = gsk_get_frag_coord();
float alpha = GskTexture(u_source, vUv).a;
alpha *= (1.0 - clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0));
vec4 color = final_color * alpha;
gskSetOutputColor(color);
}

View File

@@ -1,139 +0,0 @@
uniform sampler2D u_source;
uniform mat4 u_projection;
uniform mat4 u_modelview;
uniform float u_alpha;// = 1.0;
uniform vec4 u_viewport;
uniform vec4[3] u_clip_rect;
#if defined(GSK_LEGACY)
_OUT_ vec4 outputColor;
#elif !defined(GSK_GLES)
_OUT_ vec4 outputColor;
#endif
_IN_ vec2 vUv;
GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
{
#if defined(GSK_GLES) || defined(GSK_LEGACY)
return GskRoundedRect(r[0], r[1], r[2]);
#else
return r;
#endif
}
float
gsk_ellipsis_dist (vec2 p, vec2 radius)
{
if (radius == vec2(0, 0))
return 0.0;
vec2 p0 = p / radius;
vec2 p1 = 2.0 * p0 / radius;
return (dot(p0, p0) - 1.0) / length (p1);
}
float
gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
{
float d = gsk_ellipsis_dist (point - center, radius);
return clamp (0.5 - d, 0.0, 1.0);
}
float
gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
{
if (p.x < r.bounds.x || p.y < r.bounds.y ||
p.x >= r.bounds.z || p.y >= r.bounds.w)
return 0.0;
vec2 ref_tl = r.corner_points1.xy;
vec2 ref_tr = r.corner_points1.zw;
vec2 ref_br = r.corner_points2.xy;
vec2 ref_bl = r.corner_points2.zw;
if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
p.x <= ref_tr.x && p.x <= ref_br.x)
return 1.0;
if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
p.y <= ref_bl.y && p.y <= ref_br.y)
return 1.0;
vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
p.x > ref_tr.x && p.y < ref_tr.y,
p.x > ref_br.x && p.y > ref_br.y,
p.x < ref_bl.x && p.y > ref_bl.y);
return 1.0 - dot(vec4(is_out), corner_coverages);
}
float
gsk_rect_coverage (vec4 r, vec2 p)
{
if (p.x < r.x || p.y < r.y ||
p.x >= r.z || p.y >= r.w)
return 0.0;
return 1.0;
}
vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
#if defined(GSK_GLES) || defined(GSK_LEGACY)
return texture2D(sampler, texCoords);
#else
return texture(sampler, texCoords);
#endif
}
#ifdef GSK_GL3
layout(origin_upper_left) in vec4 gl_FragCoord;
#endif
vec2 gsk_get_frag_coord() {
vec2 fc = gl_FragCoord.xy;
#ifdef GSK_GL3
fc += u_viewport.xy;
#else
fc.x += u_viewport.x;
fc.y = (u_viewport.y + u_viewport.w) - fc.y;
#endif
return fc;
}
void gskSetOutputColor(vec4 color) {
vec4 result;
#if defined(NO_CLIP)
result = color;
#elif defined(RECT_CLIP)
result = color * gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
gsk_get_frag_coord());
#else
result = color * gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
gsk_get_frag_coord());
#endif
#if defined(GSK_GLES) || defined(GSK_LEGACY)
gl_FragColor = result;
#else
outputColor = result;
#endif
}

View File

@@ -1,57 +0,0 @@
#ifndef GSK_LEGACY
precision highp float;
#endif
#if defined(GSK_GLES) || defined(GSK_LEGACY)
#define _OUT_ varying
#define _IN_ varying
#define _NOPERSPECTIVE_
#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
#else
#define _OUT_ out
#define _IN_ in
#define _NOPERSPECTIVE_ noperspective
#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
#endif
struct GskRoundedRect
{
vec4 bounds; // Top left and bottom right
// Look, arrays can't be in structs if you want to return the struct
// from a function in gles or whatever. Just kill me.
vec4 corner_points1; // xy = top left, zw = top right
vec4 corner_points2; // xy = bottom right, zw = bottom left
};
// Transform from a C GskRoundedRect to what we need.
GskRoundedRect
gsk_create_rect(vec4[3] data)
{
vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
bounds.xw + vec2(data[2].zw * vec2(1, -1)));
return GskRoundedRect(bounds, corner_points1, corner_points2);
}
vec4
gsk_get_bounds(vec4[3] data)
{
return vec4(data[0].xy, data[0].xy + data[0].zw);
}
vec4 gsk_premultiply(vec4 c) {
return vec4(c.rgb * c.a, c.a);
}
vec4 gsk_scaled_premultiply(vec4 c, float s) {
// Fast version of gsk_premultiply(c) * s
// 4 muls instead of 7
float a = s * c.a;
return vec4(c.rgb * a, a);
}

View File

@@ -1,69 +0,0 @@
uniform mat4 u_projection;
uniform mat4 u_modelview;
uniform float u_alpha;
#if defined(GSK_GLES) || defined(GSK_LEGACY)
attribute vec2 aPosition;
attribute vec2 aUv;
_OUT_ vec2 vUv;
#else
_IN_ vec2 aPosition;
_IN_ vec2 aUv;
_OUT_ vec2 vUv;
#endif
// amount is: top, right, bottom, left
GskRoundedRect
gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
{
vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
vec4 new_corner_points1 = r.corner_points1;
vec4 new_corner_points2 = r.corner_points2;
if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
}
void
gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
{
r.bounds.xy += offset;
r.bounds.zw += offset;
r.corner_points1.xy += offset;
r.corner_points1.zw += offset;
r.corner_points2.xy += offset;
r.corner_points2.zw += offset;
}
void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
{
r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
}
#if defined(GSK_LEGACY)
// Can't have out or inout array parameters...
#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
#else
void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
{
#if defined(GSK_GLES)
out_r[0] = r.bounds;
out_r[1] = r.corner_points1;
out_r[2] = r.corner_points2;
#else
out_r = r;
#endif
}
#endif

View File

@@ -1,74 +0,0 @@
// VERTEX_SHADER
uniform vec4 u_geometry;
_NOPERSPECTIVE_ _OUT_ vec2 coord;
void main() {
gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
vec2 mv0 = u_modelview[0].xy;
vec2 mv1 = u_modelview[1].xy;
vec2 offset = aPosition - u_geometry.xy;
vec2 dir = vec2(dot(mv0, offset),
dot(mv1, offset));
coord = dir * u_geometry.zw;
}
// FRAGMENT_SHADER:
#ifdef GSK_LEGACY
uniform int u_num_color_stops;
#else
uniform highp int u_num_color_stops;
#endif
uniform bool u_repeat;
uniform vec2 u_range;
uniform float u_color_stops[6 * 5];
_NOPERSPECTIVE_ _IN_ vec2 coord;
float get_offset(int index) {
return u_color_stops[5 * index];
}
vec4 get_color(int index) {
int base = 5 * index + 1;
return vec4(u_color_stops[base],
u_color_stops[base + 1],
u_color_stops[base + 2],
u_color_stops[base + 3]);
}
void main() {
// Reverse scale
float offset = length(coord) * u_range.x + u_range.y;
if (u_repeat) {
offset = fract(offset);
}
if (offset < get_offset(0)) {
gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
return;
}
int n = u_num_color_stops - 1;
for (int i = 0; i < n; i++) {
float curr_offset = get_offset(i);
float next_offset = get_offset(i + 1);
if (offset >= curr_offset && offset < next_offset) {
float f = (offset - curr_offset) / (next_offset - curr_offset);
vec4 curr_color = gsk_premultiply(get_color(i));
vec4 next_color = gsk_premultiply(get_color(i + 1));
vec4 color = mix(curr_color, next_color, f);
gskSetOutputColor(color * u_alpha);
return;
}
}
gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha));
}

View File

@@ -1,41 +0,0 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
uniform vec4 u_child_bounds;
uniform vec4 u_texture_rect;
float wrap(float f, float wrap_for) {
return mod(f, wrap_for);
}
/* We get the texture coordinates via vUv,
* but that might be on a texture atlas, so we need to do the
* wrapping ourselves.
*/
void main() {
/* We map the texture coordinate to [1;0], then wrap it and scale the result again */
float tw = u_texture_rect.z - u_texture_rect.x;
float th = u_texture_rect.w - u_texture_rect.y;
float mapped_x = (vUv.x - u_texture_rect.x) / tw;
float mapped_y = (vUv.y - u_texture_rect.y) / th;
float wrapped_x = wrap(u_child_bounds.x + mapped_x * u_child_bounds.z, 1.0);
float wrapped_y = wrap(u_child_bounds.y + mapped_y * u_child_bounds.w, 1.0);
vec2 tp;
tp.x = u_texture_rect.x + (wrapped_x * tw);
tp.y = u_texture_rect.y + (wrapped_y * th);
vec4 diffuse = GskTexture(u_source, tp);
gskSetOutputColor(diffuse * u_alpha);
}

View File

@@ -1,42 +0,0 @@
// VERTEX_SHADER:
uniform vec4 u_color;
uniform float u_spread;
uniform vec2 u_offset;
uniform vec4[3] u_outline_rect;
_OUT_ vec4 final_color;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
final_color = gsk_premultiply(u_color) * u_alpha;
GskRoundedRect inside = gsk_create_rect(u_outline_rect);
GskRoundedRect outside = gsk_rounded_rect_shrink(inside, vec4(- u_spread));
gsk_rounded_rect_offset(outside, u_offset);
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
vec2 frag = gsk_get_frag_coord();
float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
0.0, 1.0);
gskSetOutputColor(final_color * alpha);
}

View File

@@ -139,7 +139,6 @@
#include "gskglshaderprivate.h"
#include "gskdebugprivate.h"
#include "gl/gskglrendererprivate.h"
#include "ngl/gsknglrendererprivate.h"
static GskGLUniformType
@@ -544,9 +543,7 @@ gsk_gl_shader_compile (GskGLShader *shader,
{
g_return_val_if_fail (GSK_IS_GL_SHADER (shader), FALSE);
if (GSK_IS_GL_RENDERER (renderer))
return gsk_gl_renderer_try_compile_gl_shader (GSK_GL_RENDERER (renderer), shader, error);
else if (GSK_IS_NGL_RENDERER (renderer))
if (GSK_IS_NGL_RENDERER (renderer))
return gsk_ngl_renderer_try_compile_gl_shader (GSK_NGL_RENDERER (renderer), shader, error);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,

View File

@@ -38,7 +38,6 @@
#include "gskcairorenderer.h"
#include "gskdebugprivate.h"
#include "gl/gskglrenderer.h"
#include "ngl/gsknglrenderer.h"
#include "gskprofilerprivate.h"
#include "gskrendernodeprivate.h"
@@ -508,8 +507,6 @@ get_renderer_for_name (const char *renderer_name)
else if (g_ascii_strcasecmp (renderer_name, "opengl") == 0 ||
g_ascii_strcasecmp (renderer_name, "ngl") == 0)
return GSK_TYPE_NGL_RENDERER;
else if (g_ascii_strcasecmp (renderer_name, "gl") == 0)
return GSK_TYPE_GL_RENDERER;
#ifdef GDK_RENDERING_VULKAN
else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0)
return GSK_TYPE_VULKAN_RENDERER;
@@ -520,12 +517,11 @@ get_renderer_for_name (const char *renderer_name)
#ifdef GDK_WINDOWING_BROADWAY
g_print ("broadway - Use the Broadway specific renderer\n");
#else
g_print ("broadway - disabled during GTK build\n");
g_print ("broadway - Disabled during GTK build\n");
#endif
g_print (" cairo - Use the Cairo fallback renderer\n");
g_print (" opengl - Use the default OpenGL renderer\n");
g_print (" gl - An OpenGL renderer\n");
g_print (" ngl - Another OpenGL renderer\n");
g_print (" ngl - An OpenGL renderer\n");
#ifdef GDK_RENDERING_VULKAN
g_print (" vulkan - Use the Vulkan renderer\n");
#else

View File

@@ -17,7 +17,7 @@
*/
/**
* GskRenderNode: (ref-func gsk_render_node_ref) (unref-func gsk_render_node_unref)
* GskRenderNode: (ref-func gsk_render_node_ref) (unref-func gsk_render_node_unref) (set-value-func gsk_value_set_render_node) (get-value-func gsk_value_get_render_node)
*
* `GskRenderNode` is the basic block in a scene graph to be
* rendered using `GskRenderer`.
@@ -620,3 +620,108 @@ gsk_render_node_deserialize (GBytes *bytes,
return node;
}
/**
* gsk_value_set_render_node:
* @value: a `GValue` initialized with type `GSK_TYPE_RENDER_NODE`
* @node: a `GskRenderNode`
*
* Stores the given `GskRenderNode` inside `value`.
*
* The `GValue` will acquire a reference to the `node`.
*/
void
gsk_value_set_render_node (GValue *value,
GskRenderNode *node)
{
GskRenderNode *old_node;
g_return_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE));
old_node = value->data[0].v_pointer;
if (node != NULL)
{
g_return_if_fail (GSK_IS_RENDER_NODE (node));
value->data[0].v_pointer = gsk_render_node_ref (node);
}
else
{
value->data[0].v_pointer = NULL;
}
if (old_node != NULL)
gsk_render_node_unref (old_node);
}
/**
* gsk_value_take_render_node:
* @value: a `GValue` initialized with type `GSK_TYPE_RENDER_NODE`
* @node: (transfer full) (nullable): a `GskRenderNode`
*
* Stores the given `GskRenderNode` inside `value`.
*
* This function transfers the ownership of the `node` to the `GValue`.
*/
void
gsk_value_take_render_node (GValue *value,
GskRenderNode *node)
{
GskRenderNode *old_node;
g_return_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE));
old_node = value->data[0].v_pointer;
if (node != NULL)
{
g_return_if_fail (GSK_IS_RENDER_NODE (node));
value->data[0].v_pointer = node;
}
else
{
value->data[0].v_pointer = NULL;
}
if (old_node != NULL)
gsk_render_node_unref (old_node);
}
/**
* gsk_value_get_render_node:
* @value: a `GValue` initialized with type `GSK_TYPE_RENDER_NODE`
*
* Retrieves the `GskRenderNode` stored inside the given `value`.
*
* Returns: (transfer none) (nullable): a `GskRenderNode`
*/
GskRenderNode *
gsk_value_get_render_node (const GValue *value)
{
g_return_val_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE), NULL);
return value->data[0].v_pointer;
}
/**
* gsk_value_dup_render_node:
* @value: a `GValue` initialized with type `GSK_TYPE_RENDER_NODE`
*
* Retrieves the `GskRenderNode` stored inside the given `value`, and acquires
* a reference to it.
*
* Returns: (transfer full) (nullable): a `GskRenderNode`
*/
GskRenderNode *
gsk_value_dup_render_node (const GValue *value)
{
g_return_val_if_fail (G_VALUE_HOLDS (value, GSK_TYPE_RENDER_NODE), NULL);
if (value->data[0].v_pointer == NULL)
return NULL;
return gsk_render_node_ref (value->data[0].v_pointer);
}

View File

@@ -534,6 +534,26 @@ GBytes * gsk_gl_shader_node_get_args (const GskRender
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_gl_shader_node_get_shader (const GskRenderNode *node) G_GNUC_PURE;
/**
* GSK_VALUE_HOLDS_RENDER_NODE:
* @value: a `GValue`
*
* Evaluates to %TRUE if @value was initialized with %GSK_TYPE_RENDER_NODE.
*/
#define GSK_VALUE_HOLDS_RENDER_NODE(value) (G_VALUE_HOLDS ((value), GSK_TYPE_RENDER_NODE))
GDK_AVAILABLE_IN_4_4
void gsk_value_set_render_node (GValue *value,
GskRenderNode *node);
GDK_AVAILABLE_IN_4_4
void gsk_value_take_render_node (GValue *value,
GskRenderNode *node);
GDK_AVAILABLE_IN_4_4
GskRenderNode * gsk_value_get_render_node (const GValue *value);
GDK_AVAILABLE_IN_4_4
GskRenderNode * gsk_value_dup_render_node (const GValue *value);
G_END_DECLS
#endif /* __GSK_RENDER_NODE_H__ */

View File

@@ -4377,15 +4377,6 @@ gsk_text_node_draw (GskRenderNode *node,
cairo_restore (cr);
}
/* We steal one of the bits in PangoGlyphVisAttr */
G_STATIC_ASSERT (sizeof (PangoGlyphVisAttr) == 4);
#define COLOR_GLYPH_BIT 2
#define GLYPH_IS_COLOR(g) (((*(guint32*)&(g)->attr) & COLOR_GLYPH_BIT) != 0)
#define GLYPH_SET_COLOR(g) (*(guint32*)(&(g)->attr) |= COLOR_GLYPH_BIT)
#define GLYPH_CLEAR_COLOR(g) (*(guint32*)(&(g)->attr) &= ~COLOR_GLYPH_BIT)
static void
gsk_text_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
@@ -4411,7 +4402,7 @@ gsk_text_node_diff (GskRenderNode *node1,
info1->geometry.x_offset == info2->geometry.x_offset &&
info1->geometry.y_offset == info2->geometry.y_offset &&
info1->attr.is_cluster_start == info2->attr.is_cluster_start &&
GLYPH_IS_COLOR (info1) == GLYPH_IS_COLOR (info2))
info1->attr.is_color == info2->attr.is_color)
continue;
gsk_render_node_diff_impossible (node1, node2, region);
@@ -4424,46 +4415,6 @@ gsk_text_node_diff (GskRenderNode *node1,
gsk_render_node_diff_impossible (node1, node2, region);
}
static gboolean
font_has_color_glyphs (PangoFont *font)
{
hb_face_t *face = hb_font_get_face (pango_font_get_hb_font (font));
return hb_ot_color_has_layers (face) ||
hb_ot_color_has_png (face) ||
hb_ot_color_has_svg (face);
}
static gboolean
glyph_has_color (PangoFont *font,
guint glyph)
{
hb_font_t *hb_font = pango_font_get_hb_font (font);
hb_face_t *face = hb_font_get_face (hb_font);
hb_blob_t *blob;
if (hb_ot_color_glyph_get_layers (face, glyph, 0, NULL, NULL) > 0)
return TRUE;
blob = hb_ot_color_glyph_reference_png (hb_font, glyph);
if (blob)
{
guint length = hb_blob_get_length (blob);
hb_blob_destroy (blob);
return length > 0;
}
blob = hb_ot_color_glyph_reference_svg (face, glyph);
if (blob)
{
guint length = hb_blob_get_length (blob);
hb_blob_destroy (blob);
return length > 0;
}
return FALSE;
}
/**
* gsk_text_node_new:
* @font: the `PangoFont` containing the glyphs
@@ -4488,7 +4439,6 @@ gsk_text_node_new (PangoFont *font,
GskRenderNode *node;
PangoRectangle ink_rect;
PangoGlyphInfo *glyph_infos;
gboolean has_color_glyphs;
int n;
pango_glyph_string_extents (glyphs, font, &ink_rect, NULL);
@@ -4507,7 +4457,6 @@ gsk_text_node_new (PangoFont *font,
self->has_color_glyphs = FALSE;
glyph_infos = g_malloc_n (glyphs->num_glyphs, sizeof (PangoGlyphInfo));
has_color_glyphs = font_has_color_glyphs (font);
n = 0;
for (int i = 0; i < glyphs->num_glyphs; i++)
@@ -4517,14 +4466,9 @@ gsk_text_node_new (PangoFont *font,
continue;
glyph_infos[n] = glyphs->glyphs[i];
GLYPH_CLEAR_COLOR (&glyph_infos[n]);
if (has_color_glyphs &&
glyph_has_color (font, glyph_infos[n].glyph))
{
self->has_color_glyphs = TRUE;
GLYPH_SET_COLOR (&glyph_infos[n]);
}
if (glyphs->glyphs[i].attr.is_color)
self->has_color_glyphs = TRUE;
n++;
}
@@ -5721,6 +5665,116 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_DEBUG_NODE] = node_type;
}
}
static void
gsk_render_node_content_serializer_finish (GObject *source,
GAsyncResult *result,
gpointer serializer)
{
GOutputStream *stream = G_OUTPUT_STREAM (source);
GError *error = NULL;
if (!g_output_stream_write_bytes_finish (stream, result, &error))
gdk_content_serializer_return_error (serializer, error);
else
gdk_content_serializer_return_success (serializer);
}
static void
gsk_render_node_content_serializer (GdkContentSerializer *serializer)
{
const GValue *value;
GskRenderNode *node;
GBytes *bytes;
value = gdk_content_serializer_get_value (serializer);
node = gsk_value_get_render_node (value);
bytes = gsk_render_node_serialize (node);
g_output_stream_write_bytes_async (gdk_content_serializer_get_output_stream (serializer),
bytes,
gdk_content_serializer_get_priority (serializer),
gdk_content_serializer_get_cancellable (serializer),
gsk_render_node_content_serializer_finish,
serializer);
g_bytes_unref (bytes);
}
static void
gsk_render_node_content_deserializer_finish (GObject *source,
GAsyncResult *result,
gpointer deserializer)
{
GOutputStream *stream = G_OUTPUT_STREAM (source);
GError *error = NULL;
gssize written;
GValue *value;
GskRenderNode *node;
GBytes *bytes;
written = g_output_stream_splice_finish (stream, result, &error);
if (written < 0)
{
gdk_content_deserializer_return_error (deserializer, error);
return;
}
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
/* For now, we ignore any parsing errors. We might want to revisit that if it turns
* out copy/paste leads to too many errors */
node = gsk_render_node_deserialize (bytes, NULL, NULL);
value = gdk_content_deserializer_get_value (deserializer);
gsk_value_take_render_node (value, node);
gdk_content_deserializer_return_success (deserializer);
}
static void
gsk_render_node_content_deserializer (GdkContentDeserializer *deserializer)
{
GOutputStream *output;
output = g_memory_output_stream_new_resizable ();
g_output_stream_splice_async (output,
gdk_content_deserializer_get_input_stream (deserializer),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
gdk_content_deserializer_get_priority (deserializer),
gdk_content_deserializer_get_cancellable (deserializer),
gsk_render_node_content_deserializer_finish,
deserializer);
g_object_unref (output);
}
static void
gsk_render_node_init_content_serializers (void)
{
gdk_content_register_serializer (GSK_TYPE_RENDER_NODE,
"application/x-gtk-render-node",
gsk_render_node_content_serializer,
NULL,
NULL);
gdk_content_register_serializer (GSK_TYPE_RENDER_NODE,
"text/plain;charset=utf-8",
gsk_render_node_content_serializer,
NULL,
NULL);
/* The serialization format only outputs ASCII, so we can do this */
gdk_content_register_serializer (GSK_TYPE_RENDER_NODE,
"text/plain",
gsk_render_node_content_serializer,
NULL,
NULL);
gdk_content_register_deserializer ("application/x-gtk-render-node",
GSK_TYPE_RENDER_NODE,
gsk_render_node_content_deserializer,
NULL,
NULL);
}
/*< private >
* gsk_render_node_init_types:
*
@@ -5735,6 +5789,7 @@ gsk_render_node_init_types (void)
{
gboolean initialized = TRUE;
gsk_render_node_init_types_once ();
gsk_render_node_init_content_serializers ();
g_once_init_leave (&register_types__volatile, initialized);
}
}

View File

@@ -1,25 +1,3 @@
gsk_private_gl_shaders = [
'gl/resources/preamble.glsl',
'gl/resources/preamble.fs.glsl',
'gl/resources/preamble.vs.glsl',
'gl/resources/border.glsl',
'gl/resources/blit.glsl',
'gl/resources/coloring.glsl',
'gl/resources/color.glsl',
'gl/resources/linear_gradient.glsl',
'gl/resources/radial_gradient.glsl',
'gl/resources/conic_gradient.glsl',
'gl/resources/color_matrix.glsl',
'gl/resources/blur.glsl',
'gl/resources/inset_shadow.glsl',
'gl/resources/outset_shadow.glsl',
'gl/resources/unblurred_outset_shadow.glsl',
'gl/resources/cross_fade.glsl',
'gl/resources/blend.glsl',
'gl/resources/repeat.glsl',
'gl/resources/custom.glsl',
]
gsk_private_ngl_shaders = [
'ngl/resources/preamble.glsl',
'ngl/resources/preamble.fs.glsl',
@@ -53,7 +31,6 @@ gsk_public_sources = files([
'gskrendernodeparser.c',
'gskroundedrect.c',
'gsktransform.c',
'gl/gskglrenderer.c',
'ngl/gsknglrenderer.c',
])
@@ -62,16 +39,6 @@ gsk_private_sources = files([
'gskdebug.c',
'gskprivate.c',
'gskprofiler.c',
'gl/gskglshaderbuilder.c',
'gl/gskglprofiler.c',
'gl/gskglglyphcache.c',
'gl/gskgldriver.c',
'gl/gskglrenderops.c',
'gl/gskglshadowcache.c',
'gl/gskgltextureatlas.c',
'gl/gskgliconcache.c',
'gl/opbuffer.c',
'gl/stb_rect_pack.c',
'ngl/gsknglattachmentstate.c',
'ngl/gsknglbuffer.c',
'ngl/gsknglcommandqueue.c',
@@ -85,6 +52,8 @@ gsk_private_sources = files([
'ngl/gskngltexturelibrary.c',
'ngl/gskngluniformstate.c',
'ngl/gskngltexturepool.c',
'ngl/gskglprofiler.c',
'ngl/stb_rect_pack.c',
'ngl/fp16.c',
])
@@ -106,7 +75,6 @@ gsk_public_headers = files([
install_headers(gsk_public_headers, 'gsk.h', subdir: 'gtk-4.0/gsk')
gsk_public_gl_headers = files([
'gl/gskglrenderer.h',
'ngl/gsknglrenderer.h',
])
install_headers(gsk_public_gl_headers, subdir: 'gtk-4.0/gsk/gl')
@@ -177,7 +145,6 @@ gsk_resources_xml = configure_file(output: 'gsk.resources.xml',
command: [
find_program('gen-gsk-gresources-xml.py'),
'@OUTPUT@',
gsk_private_gl_shaders,
gsk_private_ngl_shaders,
gsk_private_vulkan_compiled_shaders,
gsk_private_vulkan_shaders

View File

@@ -30,7 +30,7 @@
#include "inlinearray.h"
#include "../gl/gskglprofilerprivate.h"
#include "gskglprofilerprivate.h"
G_BEGIN_DECLS

View File

@@ -2838,9 +2838,6 @@ compute_phase_and_pos (float value, float *pos)
}
}
#define COLOR_GLYPH_BIT 2
#define GLYPH_IS_COLOR(g) (((*(guint32*)&(g)->attr) & COLOR_GLYPH_BIT) != 0)
static inline void
gsk_ngl_render_job_visit_text_node (GskNglRenderJob *job,
const GskRenderNode *node,
@@ -2903,7 +2900,7 @@ gsk_ngl_render_job_visit_text_node (GskNglRenderJob *job,
/* If the glyph has color, we don't need to recolor anything.
* We tell the shader by setting the color to vec4(-1).
*/
if (!force_color && GLYPH_IS_COLOR (gi))
if (!force_color && gi->attr.is_color)
c = nc;
else
c = cc;

View File

@@ -24,7 +24,7 @@
#include "gskngltypesprivate.h"
#include "gskngltexturepoolprivate.h"
#include "../gl/stb_rect_pack.h"
#include "stb_rect_pack.h"
G_BEGIN_DECLS

Some files were not shown because too many files have changed in this diff Show More