Изображения Libgdx из растровых изображений нарисованы сплошным черным
Для начала, я знаю, что libgdx в основном используется для игр. Я только что узнал об этом и хотел попытаться придать ему дополнительную (возможную) цель, например простую рамку для фотографий. Приведенный ниже код является лишь частью доказательства концепции, и когда он работает, он должен эволюционировать в более крупное приложение.
Ниже я опубликовал очень простой класс на то, что я до сих пор. Он выполняет каждую [num] секунд в другом потоке, загружает изображение с диска, помещает его в растровое изображение и создает из него текстуру в потоке GL (если я все правильно понимаю).
Я пришел к этому коду после долгих проб и ошибок. Мне потребовался час, чтобы понять, что в потоке OpenGL должна быть создана текстура. Когда текстура создается вне потока, на изображениях появляются просто большие черные ящики без загруженной текстуры.
Что ж, когда я запустил эту версию класса с текстурами, созданными в потоке, я, наконец, увидел изображение, показывающее и постепенно исчезающее [num] секунд.
Но после 15 выполнений изображения снова начинают появляться как черные ящики, как будто текстура создается вне потока GL. Я не получаю никаких исключений, напечатанных в консоли.
Приложение работает на Raspberry Pi с разделением памяти 128/128. Изображения в формате JPEG в формате 1920*1080 (не прогрессивные). Использование памяти выглядит следующим образом:
ВИРТ: 249м
RES: 37м
SHR: 10 м Командная строка: java -Xmx128M -DPI=true -DLWJGJ_BACKEND=GLES -Djava.library.path=libs:/opt/vc/lib:. -classpath *:. org.pidome.raspberry.mirrorclient.BootStrapper
Я вижу, что RES растет, когда загружается новое изображение, но после загрузки оно возвращается к 37.
System.out.println("Счетчик обмена: " +swapCounter); продолжает давать мне вывод, когда поток запущен.
Не могли бы вы, ребята, указать мне правильное направление, чтобы решить проблему, состоящую в том, что после 15 итераций текстуры больше не отображаются, а изображения полностью черные?
Вот мой текущий код (имя PhotosActor вводит в заблуждение, результат первой попытки стать актером):
public class PhotosActor {
List<Image> images = new ArrayList<>();
private String imgDir = "appimages/photos/";
List<String> fileSet = new ArrayList<>();
private final ScheduledExecutorService changeExecutor = Executors.newSingleThreadScheduledExecutor();
Stage stage;
int swapCounter = 0;
public PhotosActor(Stage stage) {
this.stage = stage;
}
public final void preload(){
loadFileSet();
changeExecutor.scheduleAtFixedRate(switchimg(), 10, 10, TimeUnit.SECONDS);
}
private Runnable switchimg(){
Runnable run = () -> {
try {
swapCounter++;
FileInputStream input = new FileInputStream(fileSet.get(new Random().nextInt(fileSet.size())));
Gdx2DPixmap gpm = new Gdx2DPixmap(input, Gdx2DPixmap.GDX2D_FORMAT_RGB888);
input.close();
Pixmap map = new Pixmap(gpm);
Gdx.app.postRunnable(() -> {
System.out.println("Swap counter: " +swapCounter);
Texture tex = new Texture(map);
map.dispose();
Image newImg = new Image(tex);
newImg.addAction(Actions.sequence(Actions.alpha(0),Actions.fadeIn(1f),Actions.delay(5),Actions.run(() -> {
if(images.size()>1){
Image oldImg = images.remove(1);
oldImg.getActions().clear();
oldImg.remove();
}
})));
images.add(0,newImg);
stage.addActor(newImg);
newImg.toBack();
if(images.size()>1){ images.get(1).toBack(); }
});
} catch (Exception ex) {
Logger.getLogger(PhotosActor.class.getName()).log(Level.SEVERE, null, ex);
}
};
return run;
}
private void loadFileSet(){
File[] files = new File(imgDir).listFiles();
for (File file : files) {
if (file.isFile()) {
System.out.println("Loading: " + imgDir + file.getName());
fileSet.add(imgDir + file.getName());
}
}
}
}
Заранее спасибо и ура, Джон.
1 ответ
Я смог решить это сам, пару минут назад мне показалось, что я должен избавиться от текстуры. Я был верить, что удаление изображения также удалило текстуру. Что он явно не сделал (или я должен обновить до более поздней версии).
Итак, я создал новый класс, расширяющий класс изображений:
public class PhotoImage extends Image {
Texture tex;
public PhotoImage(Texture tex){
super(tex);
this.tex = tex;
}
public void dispose(){
try {
this.tex.dispose();
} catch(Exception ex){
System.out.println(ex.getMessage());
}
}
}
Во всех местах, где я ссылался на класс изображений, я изменил его на этот класс PhotoImage. Измененный класс теперь выглядит так:
public class PhotosActor {
List<PhotoImage> images = new ArrayList<>();
private String imgDir = "appimages/photos/";
List<String> fileSet = new ArrayList<>();
private final ScheduledExecutorService changeExecutor = Executors.newSingleThreadScheduledExecutor();
Stage stage;
int swapCounter = 0;
public PhotosActor(Stage stage) {
this.stage = stage;
}
public final void preload(){
loadFileSet();
changeExecutor.scheduleAtFixedRate(switchimg(), 10, 10, TimeUnit.SECONDS);
}
private Runnable switchimg(){
Runnable run = () -> {
try {
swapCounter++;
byte[] byteResult = readLocalRandomFile();
Pixmap map = new Pixmap(byteResult, 0, byteResult.length);
Gdx.app.postRunnable(() -> {
System.out.println("Swap counter: " +swapCounter);
Texture tex = new Texture(map);
map.dispose();
PhotoImage newImg = new PhotoImage(tex);
images.add(0,newImg);
stage.addActor(newImg);
addTransform(newImg);
});
} catch (Exception ex) {
Logger.getLogger(PhotosActor.class.getName()).log(Level.SEVERE, null, ex);
}
};
return run;
}
public void addTransform(Image img){
switch(new Random().nextInt(3)){
case 0:
img.toBack();
if(images.size()>1){ images.get(1).toBack(); }
img.addAction(Actions.sequence(Actions.alpha(0),Actions.fadeIn(1f),Actions.delay(5),Actions.run(() -> {
removeOldImg();
})));
break;
case 1:
img.toBack();
if(images.size()>1){ images.get(1).toBack(); }
img.setPosition(1920f, 1080f);
img.addAction(Actions.sequence(Actions.moveTo(0f, 0f, 5f),Actions.run(() -> {
removeOldImg();
})));
break;
case 2:
img.toBack();
if(images.size()>1){ images.get(1).toBack(); }
img.setScale(0f, 0f);
img.setPosition(960f, 540f);
img.addAction(Actions.sequence(Actions.parallel(Actions.scaleTo(1f, 1f, 5f), Actions.moveTo(0f, 0f, 5f)),Actions.run(() -> {
removeOldImg();
})));
break;
}
}
private void removeOldImg(){
if(images.size()>1){
PhotoImage oldImg = images.remove(1);
oldImg.remove();
oldImg.getActions().clear();
oldImg.dispose();
}
System.out.println("Amount of images: " + images.size());
}
private byte[] readLocalRandomFile() throws Exception{
FileInputStream input = null;
try {
input = new FileInputStream(fileSet.get(new Random().nextInt(fileSet.size())));
ByteArrayOutputStream out;
try (InputStream in = new BufferedInputStream(input)) {
out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n = 0;
while (-1 != (n = in.read(buf))) {
out.write(buf, 0, n);
}
out.close();
return out.toByteArray();
} catch (IOException ex) {
Logger.getLogger(PhotosActor.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (FileNotFoundException ex) {
Logger.getLogger(PhotosActor.class.getName()).log(Level.SEVERE, null, ex);
}
throw new Exception("No data");
}
private void loadFileSet(){
File[] files = new File(imgDir).listFiles();
for (File file : files) {
if (file.isFile()) {
System.out.println("Loading: " + imgDir + file.getName());
fileSet.add(imgDir + file.getName());
}
}
}
}
В функцию удаления я сейчас добавил
oldImg.dispose();
избавиться от текстуры. Теперь переходы изображений на Raspberry Pi работают со скоростью 50 кадров в секунду, и счетчик вращения изображений включен: теперь 88. Если там, где люди думают, спасибо за ваше время!