Merge lp:~teemperor/pantheon-greeter/wallpaper-settings-from-file into lp:~elementary-pantheon/pantheon-greeter/trunk

Proposed by Raphael Isemann
Status: Work in progress
Proposed branch: lp:~teemperor/pantheon-greeter/wallpaper-settings-from-file
Merge into: lp:~elementary-pantheon/pantheon-greeter/trunk
Diff against target: 650 lines (+328/-208)
5 files modified
src/PantheonGreeter.vala (+4/-6)
src/PantheonUser.vala (+7/-13)
src/UserList.vala (+2/-2)
src/Wallpaper.vala (+102/-187)
src/WallpaperActor.vala (+213/-0)
To merge this branch: bzr merge lp:~teemperor/pantheon-greeter/wallpaper-settings-from-file
Reviewer Review Type Date Requested Status
Cody Garver (community) Needs Fixing
Review via email: mp+229515@code.launchpad.net

Description of the change

LightDM-API doesn't supply things like scaling-method and primary/secondary-color via it's API and there is no way to read a dconf-db on in a folder where we don't have the access rights. Also, the LightDM Api seems to no longer recognize our way of determining the wallpapers (?).

So we read from now on the necessary information for the wallpaper from a file from the disk.
The file is named greeter-settings and is just a set of UTF-8-encoded key-value pars.
It is located directory in the home-folder of each user as we can read that on elementary OS due to 755 rights.

For other distributions like arch (which use 700-rights by default) we need to make a workaround with an external directory in case the user wants it background displayed without making his home 755 or 711.

This merge adds the functionality to the greeter that we read the file from the disk and display the wallpaper itself. Things like scaling and color are ignored for now, if i would add this too to the patch we would end up with a humongous match that nobody wants to review (like last time when i started dumping patches on lp).

Things that should be tested:

* It can read the wallpaper from the disk and displays it in any form (scaling is as said not the target of this patch).

* It doesn't crash on a malformed greeter-settings file

You can use this program [1] to generate a greeter-settings file for now. It's up to decision on what way this file will be written (gala-plugin, cerbere-service, directly from the shell-plug).

Cheers

[1] http://pastebin.com/pUD257Xw

To post a comment you must log in.
Revision history for this message
Raphael Isemann (teemperor) wrote :

Patch was not supposed to be that big, i renamed Wallpaper.vala to WallpaperActor.vala due to the fact that one now represents the actual Wallpaper and the other the actor on the screen. The renaming made the diff quite big.

Revision history for this message
Raphael Isemann (teemperor) wrote :

It seems i also fixed the no-wallpaper without userchange bug with that.

Revision history for this message
Cody Garver (codygarver) wrote :

Use bzr mv!

review: Needs Fixing

Unmerged revisions

276. By Raphael Isemann

We now read the wallpaper from a .greeter-settings file

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/PantheonGreeter.vala'
2--- src/PantheonGreeter.vala 2014-03-13 21:16:43 +0000
3+++ src/PantheonGreeter.vala 2014-08-04 20:29:46 +0000
4@@ -30,12 +30,10 @@
5
6 TimeLabel time;
7 Indicators indicators;
8- Wallpaper wallpaper;
9+ WallpaperActor wallpaper;
10
11 Settings settings;
12
13-
14-
15 public static PantheonGreeter instance { get; private set; }
16
17 //from this width on we use the shrinked down version
18@@ -73,7 +71,7 @@
19 userlist_actor = new UserListActor (userlist);
20 time = new TimeLabel ();
21 indicators = new Indicators (settings);
22- wallpaper = new Wallpaper ();
23+ wallpaper = new WallpaperActor ();
24
25
26 get_screen ().monitors_changed.connect (monitors_changed);
27@@ -86,7 +84,7 @@
28 monitors_changed ();
29
30 userlist.current_user_changed.connect ((user) => {
31- wallpaper.set_wallpaper (user.background);
32+ wallpaper.set_wallpaper (user.wallpaper);
33 indicators.keyboard_menu.user_changed_cb (user);
34 });
35
36@@ -260,4 +258,4 @@
37 Gtk.main ();
38
39 return Posix.EXIT_SUCCESS;
40-}
41+}
42\ No newline at end of file
43
44=== modified file 'src/PantheonUser.vala'
45--- src/PantheonUser.vala 2014-03-13 15:53:44 +0000
46+++ src/PantheonUser.vala 2014-08-04 20:29:46 +0000
47@@ -35,11 +35,14 @@
48
49 public UserType usertype { get; private set; }
50
51+ public Wallpaper wallpaper { get; private set; }
52+
53 public LoginOption (int index, LightDM.User user) {
54 this.index = index;
55 this.user = user;
56 usertype = UserType.NORMAL;
57 avatar_ready = false;
58+ wallpaper = new Wallpaper.from_user (user);
59 }
60
61 public LoginOption.Guest (int index) {
62@@ -47,6 +50,7 @@
63 usertype = UserType.GUEST;
64 user = null;
65 avatar_ready = true;
66+ wallpaper = new Wallpaper.with_default_settings ();
67 }
68
69 public LoginOption.Manual (int index) {
70@@ -54,6 +58,7 @@
71 usertype = UserType.MANUAL;
72 user = null;
73 avatar_ready = true;
74+ wallpaper = new Wallpaper.with_default_settings ();
75 }
76
77 public string get_markup () {
78@@ -99,17 +104,6 @@
79 }
80 }
81
82- public string background {
83- get {
84- switch(usertype) {
85- case UserType.NORMAL: return get_lightdm_user ().background;
86- case UserType.MANUAL: return "";
87- case UserType.GUEST: return "";
88- }
89- return "";
90- }
91- }
92-
93 public string display_name {
94 get {
95 switch(usertype) {
96@@ -131,7 +125,7 @@
97 return "";
98 }
99 }
100-
101+
102 public bool logged_in {
103 get {
104 switch(usertype) {
105@@ -157,4 +151,4 @@
106 public bool is_normal () {
107 return usertype == UserType.NORMAL;
108 }
109-}
110+}
111\ No newline at end of file
112
113=== modified file 'src/UserList.vala'
114--- src/UserList.vala 2014-03-12 00:26:46 +0000
115+++ src/UserList.vala 2014-08-04 20:29:46 +0000
116@@ -62,7 +62,7 @@
117 size = index;
118
119 foreach (LoginOption user in users) {
120- user.load_avatar ();
121+ user.load_avatar.begin ();
122 }
123 }
124
125@@ -91,4 +91,4 @@
126 return get_user (i - 1);
127 return get_user (i);
128 }
129-}
130+}
131\ No newline at end of file
132
133=== modified file 'src/Wallpaper.vala'
134--- src/Wallpaper.vala 2014-03-13 17:16:53 +0000
135+++ src/Wallpaper.vala 2014-08-04 20:29:46 +0000
136@@ -19,192 +19,107 @@
137 END LICENSE
138 ***/
139
140-/**
141- * Represents a area on the screen where the current wallpaper of a user gets
142- * displayed.
143- */
144-public class Wallpaper : Clutter.Group {
145- /**
146- *
147- */
148- List<GtkClutter.Texture> wallpapers = new List<GtkClutter.Texture> ();
149- List<Cancellable> loading_wallpapers = new List<Cancellable> ();
150-
151- /**
152- * Contains old Textures that were used for wallpapers. They are recycled
153- * in the @make_texture method.
154- */
155- Queue<GtkClutter.Texture> unused_wallpapers = new Queue<GtkClutter.Texture> ();
156-
157- int gpu_limit;
158-
159- string[] cache_path = {};
160- Gdk.Pixbuf[] cache_pixbuf = {};
161- int max_cache = 3;
162-
163- string last_loaded = "";
164-
165- public int screen_width { get; set; }
166- public int screen_height { get; set; }
167-
168- public Wallpaper () {
169- GL.GLint result = 1;
170- GL.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, out result);
171- gpu_limit = result;
172- }
173-
174- string get_default () {
175- return new GLib.Settings ("org.pantheon.desktop.greeter").get_string ("default-wallpaper");
176- }
177-
178- public void reposition () {
179- set_wallpaper (last_loaded);
180- }
181-
182- public void set_wallpaper (string? path) {
183- var file_path = (path == null || path == "") ? get_default () : path;
184-
185- var file = File.new_for_path (file_path);
186-
187- if (!file.query_exists ()) {
188- warning ("File %s does not exist!\n", file_path);
189- return;
190- }
191-
192- last_loaded = file_path;
193-
194- clean_cache ();
195- load_wallpaper.begin (file_path, file);
196- }
197-
198- async void load_wallpaper (string path, File file) {
199-
200+public errordomain SettingsParserError { NO_FILE, MALFORMED, OPTION, COLOR }
201+
202+public enum PictureOption { NONE, WALLPAPER, CENTERED, SCALED, STRETCHED, ZOOM, SPANNED }
203+
204+public class Wallpaper {
205+
206+ public string uri { get; private set; }
207+ public PictureOption option { get; private set; }
208+ public Clutter.Color primary_color { get; private set; }
209+ public Clutter.Color secondary_color { get; private set; }
210+ public string fallback_uri { get; private set; }
211+
212+ public Wallpaper.with_default_settings () {
213+ apply_default_settings ();
214+ }
215+
216+ public Wallpaper.from_user (LightDM.User user) {
217+ apply_default_settings ();
218 try {
219- Gdk.Pixbuf? buf = try_load_from_cache (path);
220- //if we still dont have a wallpaper now, load from file
221- if (buf == null) {
222- var cancelable = new Cancellable ();
223- loading_wallpapers.append (cancelable);
224- InputStream stream = yield file.read_async (GLib.Priority.DEFAULT);
225- buf = yield Gdk.Pixbuf.new_from_stream_async (stream, cancelable);
226- loading_wallpapers.remove (cancelable);
227- // we downscale the pixbuf as far as we can on the CPU
228- buf = validate_pixbuf (buf);
229- //add loaded wallpapers and paths to cache
230- cache_path += path;
231- cache_pixbuf += buf;
232- }
233- //check if the currently loaded wallpaper is the one we loaded in this method
234- if (last_loaded != path)
235- return; //if not, abort
236-
237- var new_wallpaper = make_texture ();
238- new_wallpaper.opacity = 0;
239- new_wallpaper.set_from_pixbuf (buf);
240- resize (new_wallpaper);
241- add_child (new_wallpaper);
242- new_wallpaper.animate (Clutter.AnimationMode.EASE_OUT_QUINT, 500, opacity: 255);
243-
244- // abort all currently loading wallpapers
245- foreach (var c in loading_wallpapers) {
246- c.cancel ();
247- }
248- foreach (var other_wallpaper in wallpapers) {
249- wallpapers.remove (other_wallpaper);
250- other_wallpaper.animate (Clutter.AnimationMode.EASE_IN_QUINT, 500, opacity: 0).completed.connect (() => {
251- remove_child (other_wallpaper);
252- unused_wallpapers.push_tail (other_wallpaper);
253- });
254- }
255- wallpapers.append (new_wallpaper);
256-
257+ string settings_path = Path.build_path ("/",
258+ user.home_directory, ".greeter-settings");
259+ File settings_file = File.new_for_path (settings_path);
260+
261+ if (!settings_file.query_exists ()) {
262+ throw new SettingsParserError.NO_FILE ("Missing .greeter-settings file in home-directory");
263+ }
264+
265+ var dis = new DataInputStream (settings_file.read ());
266+ string line;
267+
268+ while ((line = dis.read_line (null)) != null) {
269+ string[] keyvalue = line.split ("=", 2);
270+ if (keyvalue.length != 2) {
271+ throw new SettingsParserError.MALFORMED ("Missing = delimiter in file");
272+ }
273+ keyvalue[0] = keyvalue[0].strip ();
274+ keyvalue[1] = keyvalue[1].strip ();
275+ switch (keyvalue[0]) {
276+ case "picture-uri":
277+ uri = keyvalue[1];
278+ break;
279+ case "picture-option":
280+ option = parse_option (keyvalue[1]);
281+ break;
282+ case "primary-color":
283+ if (!primary_color.parse_string (keyvalue[1]))
284+ throw new SettingsParserError.COLOR ("primary-color couldn't be parsed");
285+ break;
286+ case "secondary-color":
287+ if (!secondary_color.parse_string (keyvalue[1]))
288+ throw new SettingsParserError.COLOR ("secondary-color couldn't be parsed");
289+ break;
290+ default:
291+ throw new SettingsParserError.MALFORMED ("Unknown key on the left side of the =");
292+ }
293+ }
294 } catch (Error e) {
295- if (get_default() != path) {
296- set_wallpaper (get_default ());
297- }
298- warning (@"Can't load: '$path' due to $(e.message)");
299- }
300- }
301-
302- /**
303- * Creates a texture. It also recycles old unused wallpapers if possible
304- * as spamming constructors is expensive.
305- */
306- GtkClutter.Texture make_texture () {
307- if (unused_wallpapers.is_empty ()) {
308- return new GtkClutter.Texture ();
309- } else {
310- return unused_wallpapers.pop_head ();
311- }
312- }
313-
314- /**
315- * Resizes the cache if there are more pixbufs cached then max_mache allows
316- */
317- void clean_cache () {
318- int l = cache_path.length;
319- if (l > max_cache) {
320- cache_path = cache_path [l - max_cache : l];
321- cache_pixbuf = cache_pixbuf [l - max_cache : l];
322- }
323- }
324-
325- /**
326- * Looks up the pixbuf of the image-file with the given path in the cache.
327- * Returns null if there is no pixbuf for that file in cache
328- */
329- Gdk.Pixbuf? try_load_from_cache (string path) {
330- for (int i = 0; i < cache_path.length; i++) {
331- if (cache_path[i] == path)
332- return cache_pixbuf[i];
333- }
334- return null;
335- }
336-
337- /**
338- * makes the pixbuf fit inside the GPU limit and scales it to
339- * screen size to save memory.
340- */
341- Gdk.Pixbuf validate_pixbuf (Gdk.Pixbuf pixbuf) {
342- Gdk.Pixbuf result = scale_to_rect (pixbuf, gpu_limit, gpu_limit);
343- result = scale_to_rect (pixbuf, screen_width, screen_height);
344- return result;
345- }
346-
347- /**
348- * Scales the pixbuf down to fit in the given dimensions.
349- */
350- Gdk.Pixbuf scale_to_rect (Gdk.Pixbuf pixbuf, int rw, int rh) {
351- int h = pixbuf.height;
352- int w = pixbuf.width;
353-
354- if (h > rh || w > rw) {
355- float hw = (float)h/w*rw;
356- float wh = (float)w/h*rh;
357- if (h < w) {
358- return pixbuf.scale_simple (rw, (int) (hw), Gdk.InterpType.BILINEAR);
359- } else {
360- return pixbuf.scale_simple ((int) (wh), rh, Gdk.InterpType.BILINEAR);
361- }
362- }
363- return pixbuf;
364- }
365-
366- void resize (GtkClutter.Texture tex) {
367- int w, h;
368- tex.get_base_size (out w, out h);
369-
370- if (width > (w * height) / h) {
371- tex.width = width;
372- tex.height = (int) (h * width / w);
373-
374- if (height > tex.height) {
375- tex.height = height;
376- tex.width = (int) (w * height / h);
377- }
378- } else {
379- tex.height = height;
380- tex.width = (int) (w * height / h);
381- }
382- }
383-}
384+ warning ("Error while parsing settings-file for " + user.name + ": " + e.message);
385+ apply_default_settings ();
386+ }
387+ }
388+
389+ /**
390+ * Resets this Wallpaper to safe default-settings
391+ */
392+ private void apply_default_settings () {
393+ uri = "";
394+ option = PictureOption.WALLPAPER;
395+ primary_color = Clutter.Color.from_pixel (0);
396+ secondary_color = Clutter.Color.from_pixel (0);
397+ fallback_uri = new GLib.Settings ("org.pantheon.desktop.greeter").get_string ("default-wallpaper");
398+ }
399+
400+ // helper functions below
401+
402+ private PictureOption parse_option (string str) throws SettingsParserError {
403+ switch (str.up ()) {
404+ case "NONE":
405+ return PictureOption.NONE;
406+
407+ case "WALLPAPER":
408+ return PictureOption.WALLPAPER;
409+
410+ case "CENTERED":
411+ return PictureOption.CENTERED;
412+
413+ case "SCALED":
414+ return PictureOption.SCALED;
415+
416+ case "STRETCHED":
417+ return PictureOption.STRETCHED;
418+
419+ case "ZOOM":
420+ return PictureOption.ZOOM;
421+
422+ case "SPANNED":
423+ return PictureOption.SPANNED;
424+
425+ default:
426+ throw new SettingsParserError.OPTION ("Couldn't parse option-parameter");
427+ }
428+ }
429+
430+}
431\ No newline at end of file
432
433=== added file 'src/WallpaperActor.vala'
434--- src/WallpaperActor.vala 1970-01-01 00:00:00 +0000
435+++ src/WallpaperActor.vala 2014-08-04 20:29:46 +0000
436@@ -0,0 +1,213 @@
437+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
438+/***
439+ BEGIN LICENSE
440+
441+ Copyright (C) 2011-2014 elementary Developers
442+
443+ This program is free software: you can redistribute it and/or modify it
444+ under the terms of the GNU Lesser General Public License version 3, as published
445+ by the Free Software Foundation.
446+
447+ This program is distributed in the hope that it will be useful, but
448+ WITHOUT ANY WARRANTY; without even the implied warranties of
449+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
450+ PURPOSE. See the GNU General Public License for more details.
451+
452+ You should have received a copy of the GNU General Public License along
453+ with this program. If not, see <http://www.gnu.org/licenses/>
454+
455+ END LICENSE
456+***/
457+
458+/**
459+ * Represents a area on the screen where the current wallpaper of a user gets
460+ * displayed.
461+ */
462+public class WallpaperActor : Clutter.Group {
463+
464+ List<GtkClutter.Texture> wallpapers = new List<GtkClutter.Texture> ();
465+ List<Cancellable> loading_wallpapers = new List<Cancellable> ();
466+
467+ /**
468+ * Contains old Textures that were used for wallpapers. They are recycled
469+ * in the @make_texture method.
470+ */
471+ Queue<GtkClutter.Texture> unused_wallpapers = new Queue<GtkClutter.Texture> ();
472+
473+ int gpu_limit;
474+
475+ string[] cache_path = {};
476+ Gdk.Pixbuf[] cache_pixbuf = {};
477+ int max_cache = 3;
478+
479+ Wallpaper? last_loaded = null;
480+
481+ public int screen_width { get; set; }
482+ public int screen_height { get; set; }
483+
484+ public WallpaperActor () {
485+ GL.GLint result = 1;
486+ GL.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, out result);
487+ gpu_limit = result;
488+ }
489+
490+ public void reposition () {
491+ if (last_loaded != null)
492+ set_wallpaper (last_loaded);
493+ }
494+
495+ public void set_wallpaper (Wallpaper wallpaper) {
496+ last_loaded = wallpaper;
497+
498+ clean_cache ();
499+ load_wallpaper.begin (wallpaper, false);
500+ }
501+
502+ async void load_wallpaper (Wallpaper wallpaper, bool use_fallback) {
503+ string path = "";
504+ if (use_fallback) {
505+ path = wallpaper.fallback_uri;
506+ } else {
507+ path = wallpaper.uri;
508+ }
509+ warning ("starting to load " + path + " with fall back " + use_fallback.to_string ());
510+ File file = File.new_for_path (path);
511+ try {
512+ Gdk.Pixbuf? buf = try_load_from_cache (path);
513+ //if we still dont have a wallpaper now, load from file
514+ if (buf == null) {
515+ var cancelable = new Cancellable ();
516+ loading_wallpapers.append (cancelable);
517+ InputStream stream = yield file.read_async (GLib.Priority.DEFAULT);
518+ buf = yield Gdk.Pixbuf.new_from_stream_async (stream, cancelable);
519+ loading_wallpapers.remove (cancelable);
520+ // we downscale the pixbuf as far as we can on the CPU
521+ buf = validate_pixbuf (buf);
522+ //add loaded wallpapers and paths to cache
523+ cache_path += path;
524+ cache_pixbuf += buf;
525+ }
526+ //check if the currently loaded wallpaper is the one we loaded in this method
527+ if (last_loaded != wallpaper)
528+ return; //if not, abort
529+
530+ var new_wallpaper = make_texture ();
531+ new_wallpaper.opacity = 0;
532+ new_wallpaper.set_from_pixbuf (buf);
533+ resize (new_wallpaper);
534+ add_child (new_wallpaper);
535+ new_wallpaper.animate (Clutter.AnimationMode.EASE_OUT_QUINT, 500, opacity: 255);
536+
537+ // abort all currently loading wallpapers
538+ foreach (var c in loading_wallpapers) {
539+ c.cancel ();
540+ }
541+ loading_wallpapers = new List<Cancellable> ();
542+ foreach (var other_wallpaper in wallpapers) {
543+ wallpapers.remove (other_wallpaper);
544+ other_wallpaper.animate (Clutter.AnimationMode.EASE_IN_QUINT, 500, opacity: 0).completed.connect (() => {
545+ remove_child (other_wallpaper);
546+ unused_wallpapers.push_tail (other_wallpaper);
547+ });
548+ }
549+ wallpapers.append (new_wallpaper);
550+
551+ }
552+ catch (IOError.CANCELLED e) {
553+ // do nothing, we cancelled on purpose
554+ } catch (Error e) {
555+ // empty paths are not worth the report as for example guest-users have empthy paths
556+ // to their non-exisiting wallpapers.
557+ // !use_fallback because a malformed/missing fallback-wallpaper (e.g. the default-wallpaper)
558+ // is worth a report
559+ if (path != "" && !use_fallback) {
560+ warning (@"Can't load: '$path' due to $(e.message) from $(e.code.to_string ())");
561+ }
562+ if (!use_fallback) {
563+ load_wallpaper (wallpaper, true);
564+ }
565+ }
566+ }
567+
568+ /**
569+ * Creates a texture. It also recycles old unused wallpapers if possible
570+ * as spamming constructors is expensive.
571+ */
572+ GtkClutter.Texture make_texture () {
573+ if (unused_wallpapers.is_empty ()) {
574+ return new GtkClutter.Texture ();
575+ } else {
576+ return unused_wallpapers.pop_head ();
577+ }
578+ }
579+
580+ /**
581+ * Resizes the cache if there are more pixbufs cached then max_mache allows
582+ */
583+ void clean_cache () {
584+ int l = cache_path.length;
585+ if (l > max_cache) {
586+ cache_path = cache_path [l - max_cache : l];
587+ cache_pixbuf = cache_pixbuf [l - max_cache : l];
588+ }
589+ }
590+
591+ /**
592+ * Looks up the pixbuf of the image-file with the given path in the cache.
593+ * Returns null if there is no pixbuf for that file in cache
594+ */
595+ Gdk.Pixbuf? try_load_from_cache (string path) {
596+ for (int i = 0; i < cache_path.length; i++) {
597+ if (cache_path[i] == path)
598+ return cache_pixbuf[i];
599+ }
600+ return null;
601+ }
602+
603+ /**
604+ * makes the pixbuf fit inside the GPU limit and scales it to
605+ * screen size to save memory.
606+ */
607+ Gdk.Pixbuf validate_pixbuf (Gdk.Pixbuf pixbuf) {
608+ Gdk.Pixbuf result = scale_to_rect (pixbuf, gpu_limit, gpu_limit);
609+ result = scale_to_rect (pixbuf, screen_width, screen_height);
610+ return result;
611+ }
612+
613+ /**
614+ * Scales the pixbuf down to fit in the given dimensions.
615+ */
616+ Gdk.Pixbuf scale_to_rect (Gdk.Pixbuf pixbuf, int rw, int rh) {
617+ int h = pixbuf.height;
618+ int w = pixbuf.width;
619+
620+ if (h > rh || w > rw) {
621+ float hw = (float)h/w*rw;
622+ float wh = (float)w/h*rh;
623+ if (h < w) {
624+ return pixbuf.scale_simple (rw, (int) (hw), Gdk.InterpType.BILINEAR);
625+ } else {
626+ return pixbuf.scale_simple ((int) (wh), rh, Gdk.InterpType.BILINEAR);
627+ }
628+ }
629+ return pixbuf;
630+ }
631+
632+ void resize (GtkClutter.Texture tex) {
633+ int w, h;
634+ tex.get_base_size (out w, out h);
635+
636+ if (width > (w * height) / h) {
637+ tex.width = width;
638+ tex.height = (int) (h * width / w);
639+
640+ if (height > tex.height) {
641+ tex.height = height;
642+ tex.width = (int) (w * height / h);
643+ }
644+ } else {
645+ tex.height = height;
646+ tex.width = (int) (w * height / h);
647+ }
648+ }
649+}
650\ No newline at end of file

Subscribers

People subscribed via source and target branches