问题
I am trying to load image files in vala/clutter while running a timeout animation, using "image.set_load_async" stops the animation for a while when the file loads are requested. This is the source:
// valac --thread --pkg clutter-1.0 --pkg=gio-2.0 test_1.vala -o test_1
using Clutter;
class SlideImage : Clutter.Actor {
protected Texture image = new Texture ();
public int loaded = 0;
public SlideImage (string file) {
try {
loaded = 1;
image.set_load_async (true);
image.load_finished.connect((t, a) => {
loaded = 2;
this.add_actor (image);
});
image.set_from_file (file);
} catch (Error e) {
warning("Error setting SlideImageReflected gradient : %s", e.message);
}
}
}
class ClutterSlideShow {
protected Stage stage;
protected int width = 800;
protected int height =700;
protected string[] file_names = {};
private int file_pointer = 0;
private int counter = 0;
private SlideImage showA = null;
private SlideImage showB = null;
private SlideImage showC = null;
private SlideImage showD = null;
private SlideImage showE = null;
public ClutterSlideShow (string folder) {
try {
var directory = File.new_for_path (folder);
var enumerator = directory.enumerate_children (FileAttribute.STANDARD_NAME,0);
FileInfo file_info;
while ((file_info = enumerator.next_file ()) != null) {
file_names += folder+"/"+file_info.get_name ();
}
} catch (Error e) {
stderr.printf ("Error ClutterSlideShow listing files: %s\n", e.message);
}
stage = Stage.get_default ();
stage.hide.connect (Clutter.main_quit);
stage.color = Color () { red = 0, green = 0, blue = 0, alpha = 255 };;
stage.set_size (width, height);
stage.show_all ();
}
protected string get_next_file () {
file_pointer++;
if (file_pointer > file_names.length) file_pointer = 1;
return file_names[file_pointer-1];
}
public void start () {
Timeout.add (15, run);
}
private bool run () {
if (showA == null) {
showA = new SlideImage (get_next_file ());
showA.set_x (0);
showA.set_y (0);
} else if (showA.loaded == 2) {
stage.add_actor (showA);
showA.loaded = 3;
} else if (showA.loaded == 3) {
showA.set_y (showA.get_y () + 1);
counter++;
if (counter==100) {
showB = new SlideImage (get_next_file ());
showB.set_x (100);
showB.set_y (0);
stage.add_actor (showB);
showC = new SlideImage (get_next_file ());
showC.set_x (200);
showC.set_y (0);
stage.add_actor (showC);
showD = new SlideImage (get_next_file ());
showD.set_x (300);
showD.set_y (0);
stage.add_actor (showD);
showE = new SlideImage (get_next_file ());
showE.set_x (400);
showE.set_y (0);
stage.add_actor (showE);
}
}
return true;
}
}
int main (string[] args) {
if (Thread.supported () == false) {
stderr.printf ("Threads are not supported!\n");
return -1;
}
var result = init (ref args);
if (result != Clutter.InitError.SUCCESS) {
stderr.printf("Error: %s\n", result.to_string());
return 1;
}
var slide_show = new ClutterSlideShow ("/usr/share/backgrounds/");
slide_show.start ();
Clutter.main ();
return 0;
}
I have also tried to use "threads" but I get a "Segmentation fault (core dumped)" that I don't know how to debug or fix. This is the example:
// valac --thread --pkg clutter-1.0 --pkg=gio-2.0 test_2.vala -o test_2
using Clutter;
class SlideImage : Clutter.Actor {
protected Texture image = new Texture ();
public int loaded = 0;
public SlideImage (string file) {
loaded = 1;
load_image_in_background.begin(file, (obj, res) => {
try {
loaded = 2;
this.add_actor (image);
} catch (ThreadError e) {
string msg = e.message;
stderr.printf(@"Thread error: $msg\n");
}
});
}
async void load_image_in_background (string file) throws ThreadError {
SourceFunc callback = load_image_in_background.callback;
ThreadFunc<void*> run = () => {
try {
// Help ! The next line results in "Segmentation fault"
image.set_from_file (file);
} catch (Error e) {
warning("Error setting SlideImage texture : %s", e.message);
}
Idle.add((owned) callback);
return null;
};
Thread.create<void*>(run, false);
yield;
}
}
class ClutterSlideShow {
protected Stage stage;
protected int width = 800;
protected int height =700;
protected string[] file_names = {};
private int file_pointer = 0;
private int counter = 0;
private SlideImage showA = null;
private SlideImage showB = null;
private SlideImage showC = null;
private SlideImage showD = null;
private SlideImage showE = null;
public ClutterSlideShow (string folder) {
try {
var directory = File.new_for_path (folder);
var enumerator = directory.enumerate_children (FileAttribute.STANDARD_NAME,0);
FileInfo file_info;
while ((file_info = enumerator.next_file ()) != null) {
file_names += folder+"/"+file_info.get_name ();
}
} catch (Error e) {
stderr.printf ("Error ClutterSlideShow listing files: %s\n", e.message);
}
stage = Stage.get_default ();
stage.hide.connect (Clutter.main_quit);
stage.color = Color () { red = 0, green = 0, blue = 0, alpha = 255 };;
stage.set_size (width, height);
stage.show_all ();
}
protected string get_next_file () {
file_pointer++;
if (file_pointer > file_names.length) file_pointer = 1;
return file_names[file_pointer-1];
}
public void start () {
Timeout.add (15, run);
}
private bool run () {
if (showA == null) {
showA = new SlideImage (get_next_file ());
showA.set_x (0);
showA.set_y (0);
} else if (showA.loaded == 2) {
stage.add_actor (showA);
showA.loaded = 3;
} else if (showA.loaded == 3) {
showA.set_y (showA.get_y () + 1);
counter++;
if (counter==100) {
showB = new SlideImage (get_next_file ());
showB.set_x (100);
showB.set_y (0);
stage.add_actor (showB);
showC = new SlideImage (get_next_file ());
showC.set_x (200);
showC.set_y (0);
stage.add_actor (showC);
showD = new SlideImage (get_next_file ());
showD.set_x (300);
showD.set_y (0);
stage.add_actor (showD);
showE = new SlideImage (get_next_file ());
showE.set_x (400);
showE.set_y (0);
stage.add_actor (showE);
}
}
return true;
}
}
int main (string[] args) {
if (Thread.supported () == false) {
stderr.printf ("Threads are not supported!\n");
return -1;
}
var result = init (ref args);
if (result != Clutter.InitError.SUCCESS) {
stderr.printf("Error: %s\n", result.to_string());
return 1;
}
var slide_show = new ClutterSlideShow ("/usr/share/backgrounds/");
slide_show.start ();
Clutter.main ();
return 0;
}
回答1:
The execution "stop" is produced due two reasons:
- The file load (can be threaded)
- The image/actor creation (can't be threaded but improved)
I received help from an expert (thanks Victor), the next code improves it with:
- Threads the file load
- Scales the image to its final "clutter" size
This is the code:
/**
* Build with:
* valac --thread --pkg clutter-1.0 --pkg gio-2.0 --pkg gdk-3.0 --target-glib=2.32 test_2.vala -o test_2
*/
const string SLIDESHOW_PATH = "/usr/share/backgrounds";
public class SlideImage : Clutter.Actor {
public bool ready { get; private set; default = false; }
private Clutter.Image image;
private Thread thread;
public SlideImage (File file, int width, int height) {
load_image_async.begin (file, width, height, (obj, res) => {
if (image == null)
return;
set_size (width, height);
set_content_scaling_filters (Clutter.ScalingFilter.TRILINEAR,
Clutter.ScalingFilter.LINEAR);
set_content_gravity (Clutter.ContentGravity.CENTER);
set_content (image);
ready = true;
});
}
private async void load_image_async (File file, int width, int height) {
var pixbuf = yield load_pixbuf_from_file_async (file);
if (pixbuf != null) {
image = new Clutter.Image ();
try {
float relation_w = pixbuf.get_width () / width;
float relation_h = pixbuf.get_height () / height;
float offset_x = 0.0f;
float offset_y = 0.0f;
float scale = 1.0f;
if (relation_w > relation_h) {
scale = (float) height / (float) pixbuf.get_height ();
if (pixbuf.get_width () > width)
offset_x = -((float) ((pixbuf.get_width () * scale) - width)) / 2.0f;
} else {
scale = (float) width / (float) pixbuf.get_width ();
if (pixbuf.get_height () > height)
offset_y = -((float) ((pixbuf.get_height () * scale) - height)) / 2.0f;
}
var pixbuf_scaled = new Gdk.Pixbuf (pixbuf.get_colorspace (),
pixbuf.get_has_alpha (),
pixbuf.get_bits_per_sample (),
width,
height);
pixbuf.scale (pixbuf_scaled, 0, 0, width, height, offset_x, offset_y, scale, scale, Gdk.InterpType.BILINEAR);
image.set_data (pixbuf_scaled.get_pixels (),
pixbuf_scaled.get_has_alpha () ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888,
pixbuf_scaled.get_width (),
pixbuf_scaled.get_height (),
pixbuf_scaled.get_rowstride ());
} catch (Error err) {
warning ("Could not set image from pixbuf: %s", err.message);
image = null;
}
}
}
private async Gdk.Pixbuf? load_pixbuf_from_file_async (File file) {
SourceFunc callback = load_pixbuf_from_file_async.callback;
Gdk.Pixbuf? pixbuf = null;
ThreadFunc<void*> thread_func = () => {
try {
pixbuf = new Gdk.Pixbuf.from_file (file.get_path ());
message ("loaded pixbuf");
} catch (Error e) {
warning ("Error loading pixbuf: %s", e.message);
}
Idle.add ((owned) callback);
return null;
};
thread = new Thread<void*> ("load-pixbuf-in-background", thread_func);
yield;
return pixbuf;
}
}
public class ClutterSlideShow {
private const int WIDTH = 800;
private const int HEIGHT = 600;
private int counter = 0;
private List<File> files;
private Clutter.Stage stage;
private SlideImage showA;
private SlideImage showB;
private SlideImage showC;
private SlideImage showD;
private SlideImage showE;
public ClutterSlideShow (string folder) {
load_files (File.new_for_path (folder));
init_stage ();
}
public void start () {
Timeout.add (15, run);
}
protected File? get_next_file () {
var file = files.nth_data (0);
if (file != null)
files.remove (file);
return file;
}
private void load_files (File directory) {
files = new List<File> ();
try {
var enumerator = directory.enumerate_children (FileAttribute.STANDARD_NAME,0);
FileInfo file_info;
while ((file_info = enumerator.next_file ()) != null) {
var child_file = directory.get_child (file_info.get_name ());
files.prepend (child_file);
}
} catch (Error e) {
warning (e.message);
}
}
private void init_stage () {
stage = new Clutter.Stage ();
stage.background_color = Clutter.Color () { red = 0, green = 0, blue = 0, alpha = 255 };
stage.set_size (WIDTH, HEIGHT);
stage.show ();
stage.hide.connect (Clutter.main_quit);
}
private bool run () {
if (showA == null) {
showA = new SlideImage (get_next_file (), 400, 400);
showA.x = 0;
showA.y = 0;
} else if (showA.get_parent () != stage) {
stage.add_child (showA);
} else if (showA.ready) {
showA.set_y (showA.get_y () + 1);
counter++;
if (counter == 50) {
showB = new SlideImage (get_next_file (), 400, 400);
showB.set_x (100);
showB.set_y (0);
stage.add_child (showB);
showC = new SlideImage (get_next_file (), 400, 400);
showC.set_x (200);
showC.set_y (0);
stage.add_child (showC);
showD = new SlideImage (get_next_file (), 400, 400);
showD.set_x (300);
showD.set_y (0);
stage.add_child (showD);
showE = new SlideImage (get_next_file (), 400, 400);
showE.set_x (400);
showE.set_y (0);
stage.add_child (showE);
}
}
return true;
}
}
void main (string[] args) {
var result = Clutter.init (ref args);
if (result != Clutter.InitError.SUCCESS)
error ("Failed to init clutter: %s", result.to_string ());
var slide_show = new ClutterSlideShow (SLIDESHOW_PATH);
slide_show.start ();
Clutter.main ();
}
来源:https://stackoverflow.com/questions/16919447/vala-clutter-texture-loading-with-thread