2013-06-09 3 views
1

J'essaye d'envoyer un BufferedImage sur le socket, je le fais en convertissant l'image en byte[] et ensuite je l'envoie après l'avoir encodé en Base64. J'envoie plus de 2 BufferedImages, l'un d'eux est "full", l'autre est transparent à environ 50%. Le problème que j'ai, c'est que quand ils arrivent, la deuxième image est toujours visuellement transparente, mais quand j'obtiens le tableau de données via Raster, il a été changé. J'ai fait un petit code de test pour démontrer le problème;ByteArrayOutput/InputStream sur l'image transparente

 BufferedImage levelBufferedOriginal = ... 
     BufferedImage backgroundBufferedOriginal = ... 

     byte[] levelDataOriginal = ((DataBufferByte) levelBufferedOriginal.getRaster().getDataBuffer()).getData(); 
     byte[] backgroundDataOriginal = ((DataBufferByte) backgroundBufferedOriginal.getRaster().getDataBuffer()).getData(); 

     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     byte[] temp = null, temp2=null; 
     try { 
      ImageIO.write(levelBufferedOriginal, "png", baos); 
      baos.flush(); 
      temp = baos.toByteArray(); 
      baos.close(); 

      baos=new ByteArrayOutputStream(); 
      ImageIO.write(backgroundBufferedOriginal, "png", baos); 
      baos.flush(); 
      temp2 = baos.toByteArray(); 
      baos.close(); 
     } catch (IOException e1) { 
      e1.printStackTrace(); 
     } 



     BufferedImage levelBufferedNew = null; 
     BufferedImage backgroundBufferedNew = null; 

     try { 
      levelBufferedNew = ImageIO.read(new ByteArrayInputStream(temp)); 
      backgroundBufferedNew = ImageIO.read(new ByteArrayInputStream(temp2)); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

     byte[] levelDataNew = ((DataBufferByte) levelBufferedNew.getRaster().getDataBuffer()).getData(); 
     byte[] backgroundDataNew = ((DataBufferByte) backgroundBufferedNew.getRaster().getDataBuffer()).getData(); 


     System.out.println("LEVEL: " + Arrays.equals(levelDataOriginal, levelDataNew)); 
     System.out.println("BACKGROUND: " + Arrays.equals(backgroundDataOriginal, backgroundDataNew)); 

Tout ce que je fais ici, est simplement le transformer BufferedImage à byte[], puis de nouveau, et de comparer les données que je reçois de DataBufferByte. La sortie est

NIVEAU: false

CONTEXTE: true

arrière-plan est l'image "complète", et le niveau est celui avec quelques pixels transparents.

Si l'idée générale est fausse, j'aimerais en entendre une autre, tout ce que je veux, c'est pouvoir recréer exactement 2 images tamponnées.

+0

Les images sont-elles visuellement différentes? Comme vous le mentionnez, les images sont translucides. Il se peut que l'encodage/décodage PNG inverse les valeurs pour certains pixels transparents, sans affecter les visuels de l'image. Voir alpha prémultiplié vs non prémultiplié. – haraldK

+0

Les images sont visuellement les mêmes. Le problème que je reçois, c'est quand je commence à éditer les données d'image de niveau (levelDataNew) à "-1" (pour définir un pixel transparent), il devient noir à la place, donc je ne vois pas l'arrière-plan. Alors qu'une modification de levelDataOriginal va définir le pixel transparent. – Limon

+0

Que voulez-vous dire par "-1"? 0xFF? Un pixel complètement transparent est 0x0, tandis qu'un pixel complètement opaque devrait être 0xff. En outre, l'état prémultiplié est important, si vous essayez de "fondre" ce que vous avez déjà "disparu" (rendu transparent). – haraldK

Répondre

1

edit: Ce que nous avons mis en place jusqu'à présent:

  • Les images (avant et après) sont TYPE_BYTE_INDEXED (13) avec IndexColorModel (carte de couleur)
  • L'image avant a une couleur transparente dans la table de couleurs, à l'index 255 (qui est la valeur -1 dans le tableau d'octets, comme Java utilise byte s signé). L'image après a une valeur différente à cet index, ce qui n'est pas transparent.
  • Les images sont dé/sérialisées au format PNG, en utilisant ImageIO
  • Les images sont visuellement égales, mais les données brutes des pixels (le tableau d'octets) diffère

Ce qui nous amène à la conclusion que le ImageIO PNGImageWriter réorganise les entrées de la carte en couleur lors de l'écriture, ce qui donne des données pixel/carte de couleurs différentes.

Cela nous laisse essentiellement deux options:

  1. sérialisation les données d'image d'une manière différente, pour assurer les données de carte couleur/pixel n'est pas modifiée. Il est possible d'envoyer le tableau de données de pixels, avec le tableau de mappage de couleurs et la hauteur/largeur de l'image, puis de recréer l'image exactement sur le client. C'est un peu de code, et est probablement couvert par d'autres questions sur SO. Ne comptez pas sur les cartes de données/couleurs de pixels qui sont identiques. Utilisez la valeur ((IndexColorModel) levelBufferedNew.getColorModel()).getTransparentPixel() pour tester/set la transparence au lieu de la valeur codée en dur -1. Cela nécessite à peu près pas d'autre changement dans votre code.

Remarque: Ces solutions ne fonctionnent que pour les images TYPE_BYTE_INDEXED (13).Pour une approche plus générique (mais peut-être plus lente), utilisez le code de la réponse d'origine pour définir les parties transparentes et utilisez (levelBufferedNew.getRGB(x, y) >> 24) == 0 pour tester la transparence. Cela devrait fonctionner même pour TYPE_INT_ARGB ou TYPE_4BYTE_ABGR.

réponse originale:

Au lieu de jongler avec l'image au niveau de tableau d'octets, pourquoi ne pas essayer d'utiliser Java2D normal? ;-)

Quelque chose comme:

Graphics2D g = levelBufferedNew.createGraphics(); 
try { 
    g.setComposite(AlphaComposite.Clear); 
    g.fillOval(x, y, w, h); // The area you want to make transparent 
} 
finally { 
    g.dispose(); 
} 

... devrait fonctionner. PS: Comme les images utilisent IndexColorModel, vous pouvez utiliser le getTransparentPixel() pour obtenir l'indice de pixels transparent, au lieu de se fier à un certain index (-1/255). Ensuite, vous pouvez toujours manipuler au niveau de la matrice d'octets. ;-)

+0

Une autre raison d'utiliser un tableau d'octets est que je peux déterminer où se trouve le sol; Je peux demander si pixel (x, y) est! = -1, puis sa masse solide, de sorte que l'objet doit entrer en collision avec lui, et c'est le coeur du problème, malgré les images identiques, les données du tableau d'octets sont foirées en haut Les cellules qui sont -1 dans l'original ne sont pas -1 dans la version transmise. – Limon

+0

Vous supposez que -1 (ou vraiment 255) signifie transparent. Ce n'est pas le cas. Toute valeur peut être transparente. Vous devez interroger IndexColorModel. – haraldK

+0

Eh bien, j'ai édité une image dans Paint.NET, et les parties transparentes de l'image avaient la valeur -1. Si ça a l'air! = -1, je vois la couleur, dès que je la change à -1, c'est parti. – Limon

Questions connexes