A Question about Image

Good Morning (19:45 local time, 1 epic party later…)



I try to make some use of com.jme3.Texture.Image and have two questions, none of them addressed in the javadoc:



First:

[java] public List<ByteBuffer> getData() {

return data;

}

[/java]

What use does it have returning an ArrayList as a List?



Second:

There is obviously the possibility to set data by index. Where and how are those indizes used?

  1. Whats the use in returning an ArrayList? Its standrad coding practice to return the super implementatin when it has all relevant methods.
  2. For multitextures I guess, similar to the buffers in meshes.



    The data inside image is meant to be uploaded to a graphics card, so its using a special kind of ByteBuffer.
  1. it is? thats a knock-out when sometimes in the future a field was added to the derived class.



    Ok, i ask because i have to provide a copy-constructor ot use it.



    Currently i have the following:



    [java]import java.nio.ByteBuffer;

    import java.util.ArrayList;



    import com.jme3.texture.Image.Format;



    public class Image extends com.jme3.texture.Image

    {

    @SuppressWarnings("unchecked")

    public Image(Image i)

    {

    mipMapSizes = i.mipMapSizes.clone();

    data = (ArrayList<ByteBuffer>) i.data.clone();

    format = i.format;

    width = i.width;

    height = i.height;

    depth = i.depth;

    efficentData = i.efficentData;

    multiSamples = i.multiSamples;

    }

    }

    [/java]



    I still see a problem uprising when efficientData is a pointer that i dont want to shallow-copy. How is efficientData computed/is there any way to adress this or do you think it should be adressed at all?

Man, just use your own image loader, you dont want to use this as texture anyway.

I want to use this as a texture, i just dont want to use this exclusively for that.



I need to have something that is usable as a Texture for jm3 and as BufferedImage for Swing and has some additional fields (like offset, Id, overlay-painting etc) So i have to find some advanced solution.



I’ll give you an example: In my engine, i have the possibility to overlay-paint Textures like that:

[java] /*********************************************************************

  • Resize image (copy) to given values.
  • @param img The source-image to resize.
  • @param w The new width.
  • @param h The new height.
  • @param fWipeAlpha Set to true if Alpha should be hard-wiped.
  • @param clrPaint Overlay paint-color.
  • @param fFixedRatio set to true if ratio should stay the same.
  • @return The new, resized image */

    public static BufferedImage resizeImage(BufferedImage img, int w,

    int h, boolean fWipeAlpha, int clrPaint, boolean fFixedRatio)

    {

    // create appropriate buffer

    BufferedImage destImage = new BufferedImage(w, h,

    BufferedImage.TYPE_INT_ARGB);



    int iNewWidth = w;

    int iNewHeight = h;



    if (fFixedRatio)

    {

    // calculate effective dimensions

    double dRatio = Math.min( (double)h / (double)img.getHeight(),

    (double)w / (double)img.getWidth());

    iNewWidth = (int)(((double)img.getWidth()) * dRatio);

    iNewHeight = (int)(((double)img.getHeight()) * dRatio);

    };



    // paint shrunk graphic into buffer

    Graphics2D g = destImage.createGraphics();

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,

    RenderingHints.VALUE_INTERPOLATION_BICUBIC);

    int iOffX = (w - iNewWidth) / 2;

    int iOffY = (h - iNewHeight) / 2;

    g.drawImage(img, iOffX, iOffY, iNewWidth, iNewHeight, null);



    if (fWipeAlpha || clrPaint != 0xFF000000)

    {

    // we buffer channels to increase performance

    double dbRed = ((double)(clrPaint & 0xFF)) / 127;

    double dbGreen = ((double)((clrPaint >>> 8 ) & 0xFF)) / 127;

    double dbBlue = ((double)((clrPaint >>> 16 ) & 0xFF)) / 127;



    for (int i = 0; i < destImage.getHeight (); i++)

    {

    int [] rgb = destImage.getRGB (0, i, destImage.getWidth(),

    1, null, 0, destImage.getWidth ()*4);

    for (int j = 0; j < rgb.length; j++)

    {

    int pixel = rgb[j];

    int alpha = (pixel >>> 24) & 0xFF;



    if (fWipeAlpha)

    {

    // wipe 75% transparency for better image

    if (alpha < 0x40)

    alpha = 0;

    else

    alpha = 255;

    }

    if (clrPaint != 0xFF000000)

    {

    int red = (int)((double)(pixel & 0xFF ) * dbRed);

    int green = (int)((double)(((pixel >> 8 ) & 0xFF))
  • dbGreen);

    int blue = (int)((double)(((pixel >> 16 ) & 0xFF))
  • dbBlue);

    red = (red <= 0xFF) ? red : 0xFF;

    green = (green <= 0xFF) ? green : 0xFF;

    blue = (blue <= 0xFF) ? blue : 0xFF;

    pixel = (blue << 16) | (green << 8 ) | red;

    }

    else

    pixel &= 0x00FFFFFF;

    rgb [j] = (alpha << 24) | pixel;

    }

    destImage.setRGB (0, i, destImage.getWidth (), 1, rgb, 0,

    destImage.getWidth ()*4);

    };

    };

    g.dispose();

    return destImage;

    };

    [/java]



    i need to provide similar functionallity for jm3-textures to use jm3 in my engine. So I either need to derive from Image to add some additional features (like constructing/setting from BufferedImage, wich is what i currently do), or i have to include all functionallity of both into a new class, wich is a “little bit difficult” because Jave still has no cast-operator-overload.



    I must confess that while i became a fan of java in pretty short time, i currently find more and more annoying limits in that language;-/

Using an interface that is independent of a particular implementation (e.g. List vs ArrayList) is standard practice in the Java language. If at any point we decide to use a LinkedList for example, no user code will be broken.



For your 2nd question, a list of bytebuffers is defined as an image since some images such as 3D images, array images and cubemaps are actually a list of 2D images.

Using an interface that is independent of a particular implementation (e.g. List vs ArrayList) is standard practice in the Java language. If at any point we decide to use a LinkedList for example, no user code will be broken.

Ok, understood. Would you mind providing a protected copy constructor, so that if that point happens, no user-code will be broken?

[patch]Index: Image.java
===================================================================
--- Image.java (revision 7058)
+++ Image.java (working copy)
@@ -194,6 +194,24 @@
}

/**
+ * Copy-Constructor: create new clone from given image.
+ * @param i Image to construct from */
+ @SuppressWarnings("unchecked")
+ protected Image(Image i)
+ {
+ super(Type.Texture);
+ mipMapSizes = i.mipMapSizes.clone();
+ data = (ArrayList<ByteBuffer>) i.data.clone();
+ format = i.format;
+ width = i.width;
+ height = i.height;
+ depth = i.depth;
+ efficentData = i.efficentData;
+ multiSamples = i.multiSamples;
+ }
+
+
+ /**
* Constructor instantiates a new <code>Image</code> object. The
* attributes of the image are defined during construction.
*
[/patch]

The Image class implements Cloneable, no need for a copy constructor

Cloneable is good when you do



[java]a = b.clone();[/java]



it is of no use if you have



class derived1 extends base;

class derived2 extends base;



derived1 a = new derived1();

derived2 b = new derived2(a);



You can allways implement clone() with a copy-constructor by simply calling it in the clone-method on yourself for the new object, but you cant do it vice versa, because clone() doesnt work on an object (something like a.cloneTo(Object b) would at least have some use), but instead returns a freshly created object of an already set class.

rhavin said:
Cloneable is good when you do

[java]a = b.clone();[/java]

it is of no use if you have

class derived1 extends base;
class derived2 extends base;

derived1 a = new derived1();
derived2 b = new derived2(a);

You can *allways* implement clone() with a copy-constructor by simply calling it in the clone-method on yourself for the new object, but you cant do it vice versa.

Okay, in that case you can use copy constructors as you see fit.
However they will not be implemented in jME3 because we will need to add them everywhere and their use is limited
Momoko_Fan said:
… use is limited.


What limitation could a copy-constructor have that a clone()-method doesnt have?

I am not going to discuss this. Implementing Cloneable is the proper Java way of cloning objects. There are issues associated with copy constructors, for example, derived1 might work differently than derived2 and merely copying their data will not make sense.



I guess I better say this now, you’re coming from a C++ background, so perhaps its best to get acquainted with Java practices instead of complaining how we are not doing this the C++ way?

Sorry, didnt want to upset you, but i think that some things you’ve said are not correct:

Momoko_Fan said:
Implementing Cloneable is the proper Java way of cloning objects

Most seem to think that "clone is broken". I see this like Effective Java Author, Josh Bloch and would simply not use it if im not forced to use it.
Momoko_Fan said:
There are issues associated with copy constructors, for example, derived1 might work differently than derived2 and merely copying their data will not make sense.

Yes, but that has to be handled in derived1's & derived2's cc whitch both can initally call base's cc.

I dont want to talk against Java practices, i just dont see a reason to implement something in a limeted way when there is no efford at all implementing it unlimited.

But as i said, i dont want to upset anyone and if "Implementing Cloneable is the proper Java way of cloning objects" is your way to code, thats ok.

This engine has 7 years of experience in it and has been preferred over other java 3D solutions many times. Believe us when we say how we do this is the right way and the java way.

Just because the guy wrote a Java book does not make him a genius. I don’t see the opinion of “clone is broken” anywhere else. Some of his points make sense yet I don’t think they are relevant at all to jME3.



You generally operate directly with jME3 objects, not Cloneables. In addition you never would want to do the kind of cross-cloning he mentions, doing a new Image(meshObj) just because both extend GLObject does not make sense. Likewise for new Geometry(nodeObj).



Most of the burdens of clone() are handled by the implementor of the method, at jME3 we took precautions to avoid the common pitfalls and are already experienced in dealing with them.

I think it’s important to remember that Bloch doesn’t condemn completely the idea of cloning, he just suggests that the idea of Cloneable that doesn’t expose a public clone() method is broken and that other aspects lead to bad practices. He himself references using a public clone() method on his own classes that just don’t implement Cloneable.



The thing that a clone method (regardless of the interface) gives you that a copy constructor doesn’t is the ability to polymorphically call it. Cloneable doesn’t help with that but any number of other interfaces or abstract classes might… and even reflection can be used. For example, I can clone any Spatial without needing to figure out what type it is and which Class I should use to call a copy constructor. That seems a lot easier to me.



Easy code generic code:



Spatial myCopy = someSpatial.clone();





Harder code:



Class c= someSpatial.getClass();

Constructor ctor = c.getConstructor( c );

Spatial myCopy = (Spatial)ctor.newInstance(someSpatial);



…and a bunch of exceptions to catch to do it.



So, while it may be “bad practice” in general. I’d still argue that a framework that uses it properly and makes sure to dot all of its 'i’s and cross its 't’s is ok.



After all, Bloch condemns .equals() too but we still use that as standard practice as Java coders. You just have to be aware of the gotchas.

Oh, i too have a couple of clone’s in my classes, mostly implemented pretty simple like this:



[java] /** clone for cloners */

public ImageTile clone()

{return new ImageTile(this);}

[/java]

… because I have a copy-constructor.