public static Image load(InputStream in, boolean flip) throws IOException {
        boolean flipH = false;

        // open a stream to the file
        DataInputStream dis = new DataInputStream(new BufferedInputStream(in));

        // ---------- Start Reading the TGA header ---------- //
        // length of the image id (1 byte)
        int idLength = dis.readUnsignedByte();

        // Type of color map (if any) included with the image
        // 0 - no color map data is included
        // 1 - a color map is included
        int colorMapType = dis.readUnsignedByte();

        // Type of image being read:
        int imageType = dis.readUnsignedByte();

        // Read Color Map Specification (5 bytes)
        // Index of first color map entry (if we want to use it, uncomment and remove extra read.)
//        short cMapStart = flipEndian(dis.readShort());
        dis.readShort();
        // number of entries in the color map
        short cMapLength = flipEndian(dis.readShort());
        // number of bits per color map entry
        int cMapDepth = dis.readUnsignedByte();

        // Read Image Specification (10 bytes)
        // horizontal coordinate of lower left corner of image. (if we want to use it, uncomment and remove extra read.)
//        int xOffset = flipEndian(dis.readShort());
        dis.readShort();
        // vertical coordinate of lower left corner of image. (if we want to use it, uncomment and remove extra read.)
//        int yOffset = flipEndian(dis.readShort());
        dis.readShort();
        // width of image - in pixels
        int width = flipEndian(dis.readShort());
        // height of image - in pixels
        int height = flipEndian(dis.readShort());
        // bits per pixel in image.
        int pixelDepth = dis.readUnsignedByte();
        int imageDescriptor = dis.readUnsignedByte();
        if ((imageDescriptor & 32) != 0) // bit 5 : if 1, flip top/bottom ordering
        {
            flip = !flip;
        }
        if ((imageDescriptor & 16) != 0) // bit 4 : if 1, flip left/right ordering
        {
            flipH = !flipH;
        }

        // ---------- Done Reading the TGA header ---------- //

        // Skip image ID
        if (idLength > 0) {
            dis.skip(idLength);
        }

        ColorMapEntry[] cMapEntries = null;
        if (colorMapType != 0) {
            // read the color map.
            int bytesInColorMap = (cMapDepth * cMapLength) >> 3;
            int bitsPerColor = Math.min(cMapDepth / 3, 8);

            byte[] cMapData = new byte[bytesInColorMap];
            dis.read(cMapData);

            // Only go to the trouble of constructing the color map
            // table if this is declared a color mapped image.
            if (imageType == TYPE_COLORMAPPED || imageType == TYPE_COLORMAPPED_RLE) {
                cMapEntries = new ColorMapEntry[cMapLength];
                int alphaSize = cMapDepth - (3 * bitsPerColor);
                float scalar = 255f / (FastMath.pow(2, bitsPerColor) - 1);
                float alphaScalar = 255f / (FastMath.pow(2, alphaSize) - 1);
                for (int i = 0; i < cMapLength; i++) {
                    ColorMapEntry entry = new ColorMapEntry();
                    int offset = cMapDepth * i;
                    entry.red = (byte) (int) (getBitsAsByte(cMapData, offset, bitsPerColor) * scalar);
                    entry.green = (byte) (int) (getBitsAsByte(cMapData, offset + bitsPerColor, bitsPerColor) * scalar);
                    entry.blue = (byte) (int) (getBitsAsByte(cMapData, offset + (2 * bitsPerColor), bitsPerColor) * scalar);
                    if (alphaSize <= 0) {
                        entry.alpha = (byte) 255;
                    } else {
                        entry.alpha = (byte) (int) (getBitsAsByte(cMapData, offset + (3 * bitsPerColor), alphaSize) * alphaScalar);
                    }

                    cMapEntries[i] = entry;
                }
            }
        }


        // Allocate image data array
        Format format;
        byte[] rawData = null;
        int dl;
        if (pixelDepth == 32) {
            rawData = new byte[width * height * 4];
            dl = 4;
        } else {
            rawData = new byte[width * height * 3];
            dl = 3;
        }
        int rawDataIndex = 0;

        if (imageType == TYPE_TRUECOLOR) {
            byte red = 0;
            byte green = 0;
            byte blue = 0;
            byte alpha = 0;

            // Faster than doing a 16-or-24-or-32 check on each individual pixel,
            // just make a separate loop for each.
            if (pixelDepth == 16) {
                byte[] data = new byte[2];
                float scalar = 255f / 31f;
                for (int i = 0; i <= (height - 1); i++) {
                    if (!flip) {
                        rawDataIndex = (height - 1 - i) * width * dl;
                    }
                    for (int j = 0; j < width; j++) {
                        data[1] = dis.readByte();
                        data[0] = dis.readByte();
                        rawData[rawDataIndex++] = (byte) (int) (getBitsAsByte(data, 1, 5) * scalar);
                        rawData[rawDataIndex++] = (byte) (int) (getBitsAsByte(data, 6, 5) * scalar);
                        rawData[rawDataIndex++] = (byte) (int) (getBitsAsByte(data, 11, 5) * scalar);
                        if (dl == 4) {
                            // create an alpha channel
                            alpha = getBitsAsByte(data, 0, 1);
                            if (alpha == 1) {
                                alpha = (byte) 255;
                            }
                            rawData[rawDataIndex++] = alpha;
                        }
                    }
                }

                format = dl == 4 ? Format.RGBA8 : Format.RGB8;
            } else if (pixelDepth == 24) {
                for (int y = 0; y < height; y++) {
                    if (!flip) {
                        rawDataIndex = (height - 1 - y) * width * dl;
                    } else {
                        rawDataIndex = y * width * dl;
                    }

                    dis.readFully(rawData, rawDataIndex, width * dl);
//                    for (int x = 0; x < width; x++) {
                    //read scanline
//                        blue = dis.readByte();
//                        green = dis.readByte();
//                        red = dis.readByte();
//                        rawData[rawDataIndex++] = red;
//                        rawData[rawDataIndex++] = green;
//                        rawData[rawDataIndex++] = blue;
//                    }
                }
                format = Format.BGR8;
            } else if (pixelDepth == 32) {
                for (int i = 0; i <= (height - 1); i++) {
                    if (!flip) {
                        rawDataIndex = (height - 1 - i) * width * dl;
                    }

                    for (int j = 0; j < width; j++) {
                        blue = dis.readByte();
                        green = dis.readByte();
                        red = dis.readByte();
                        alpha = dis.readByte();
                        rawData[rawDataIndex++] = red;
                        rawData[rawDataIndex++] = green;
                        rawData[rawDataIndex++] = blue;
                        rawData[rawDataIndex++] = alpha;
                    }
                }
                format = Format.RGBA8;
            } else {
                throw new IOException("Unsupported TGA true color depth: " + pixelDepth);
            }
        } else if (imageType == TYPE_TRUECOLOR_RLE) {
            byte red = 0;
            byte green = 0;
            byte blue = 0;
            byte alpha = 0;
            // Faster than doing a 16-or-24-or-32 check on each individual pixel,
            // just make a separate loop for each.
            if (pixelDepth == 32) {
                for (int i = 0; i <= (height - 1); ++i) {
                    if (!flip) {
                        rawDataIndex = (height - 1 - i) * width * dl;
                    }

                    for (int j = 0; j < width; ++j) {
                        // Get the number of pixels the next chunk covers (either packed or unpacked)
                        int count = dis.readByte();
                        if ((count & 0x80) != 0) {
                            // It's an RLE-packed block: use the following pixel for the next <count> pixels.
                            count &= 0x07f;
                            j += count;
                            blue = dis.readByte();
                            green = dis.readByte();
                            red = dis.readByte();
                            alpha = dis.readByte();
                            while (count-- >= 0) {
                                rawData[rawDataIndex++] = red;
                                rawData[rawDataIndex++] = green;
                                rawData[rawDataIndex++] = blue;
                                rawData[rawDataIndex++] = alpha;
                            }
                        } else {
                            // It's not RLE packed, but the next <count> pixels are raw.
                            j += count;
                            while (count-- >= 0) {
                                blue = dis.readByte();
                                green = dis.readByte();
                                red = dis.readByte();
                                alpha = dis.readByte();
                                rawData[rawDataIndex++] = red;
                                rawData[rawDataIndex++] = green;
                                rawData[rawDataIndex++] = blue;
                                rawData[rawDataIndex++] = alpha;
                            }
                        }
                    }
                }
                format = Format.RGBA8;
            } else if (pixelDepth == 24) {
                for (int i = 0; i <= (height - 1); i++) {
                    if (!flip) {
                        rawDataIndex = (height - 1 - i) * width * dl;
                    }
                    for (int j = 0; j < width; ++j) {
                        // Get the number of pixels the next chunk covers (either packed or unpacked)
                        int count = dis.readByte();
                        if ((count & 0x80) != 0) {
                            // It's an RLE-packed block: use the following pixel for the next <count> pixels.
                            count &= 0x07f;
                            j += count;
                            blue = dis.readByte();
                            green = dis.readByte();
                            red = dis.readByte();
                            while (count-- >= 0) {
                                rawData[rawDataIndex++] = red;
                                rawData[rawDataIndex++] = green;
                                rawData[rawDataIndex++] = blue;
                            }
                        } else {
                            // It's not RLE packed, but the next <count> pixels are raw.
                            j += count;
                            while (count-- >= 0) {
                                blue = dis.readByte();
                                green = dis.readByte();
                                red = dis.readByte();
                                rawData[rawDataIndex++] = red;
                                rawData[rawDataIndex++] = green;
                                rawData[rawDataIndex++] = blue;
                            }
                        }
                    }
                }
                format = Format.RGB8;
            } else if (pixelDepth == 16) {
                byte[] data = new byte[2];
                float scalar = 255f / 31f;
                for (int i = 0; i <= (height - 1); i++) {
                    if (!flip) {
                        rawDataIndex = (height - 1 - i) * width * dl;
                    }
                    for (int j = 0; j < width; j++) {
                        // Get the number of pixels the next chunk covers (either packed or unpacked)
                        int count = dis.readByte();
                        if ((count & 0x80) != 0) {
                            // It's an RLE-packed block: use the following pixel for the next <count> pixels.
                            count &= 0x07f;
                            j += count;
                            data[1] = dis.readByte();
                            data[0] = dis.readByte();
                            blue = (byte) (int) (getBitsAsByte(data, 1, 5) * scalar);
                            green = (byte) (int) (getBitsAsByte(data, 6, 5) * scalar);
                            red = (byte) (int) (getBitsAsByte(data, 11, 5) * scalar);
                            while (count-- >= 0) {
                                rawData[rawDataIndex++] = red;
                                rawData[rawDataIndex++] = green;
                                rawData[rawDataIndex++] = blue;
                            }
                        } else {
                            // It's not RLE packed, but the next <count> pixels are raw.
                            j += count;
                            while (count-- >= 0) {
                                data[1] = dis.readByte();
                                data[0] = dis.readByte();
                                blue = (byte) (int) (getBitsAsByte(data, 1, 5) * scalar);
                                green = (byte) (int) (getBitsAsByte(data, 6, 5) * scalar);
                                red = (byte) (int) (getBitsAsByte(data, 11, 5) * scalar);
                                rawData[rawDataIndex++] = red;
                                rawData[rawDataIndex++] = green;
                                rawData[rawDataIndex++] = blue;
                            }
                        }
                    }
                }
                format = Format.RGB8;
            } else {
                throw new IOException("Unsupported TGA true color depth: " + pixelDepth);
            }

        } else if (imageType == TYPE_COLORMAPPED) {
            int bytesPerIndex = pixelDepth / 8;

            if (bytesPerIndex == 1) {
                for (int i = 0; i <= (height - 1); i++) {
                    if (!flip) {
                        rawDataIndex = (height - 1 - i) * width * dl;
                    }
                    for (int j = 0; j < width; j++) {
                        int index = dis.readUnsignedByte();
                        if (index >= cMapEntries.length || index < 0) {
                            throw new IOException("TGA: Invalid color map entry referenced: " + index);
                        }

                        ColorMapEntry entry = cMapEntries[index];
                        rawData[rawDataIndex++] = entry.blue;
                        rawData[rawDataIndex++] = entry.green;
                        rawData[rawDataIndex++] = entry.red;
                        if (dl == 4) {
                            rawData[rawDataIndex++] = entry.alpha;
                        }

                    }
                }
            } else if (bytesPerIndex == 2) {
                for (int i = 0; i <= (height - 1); i++) {
                    if (!flip) {
                        rawDataIndex = (height - 1 - i) * width * dl;
                    }
                    for (int j = 0; j < width; j++) {
                        int index = flipEndian(dis.readShort());
                        if (index >= cMapEntries.length || index < 0) {
                            throw new IOException("TGA: Invalid color map entry referenced: " + index);
                        }

                        ColorMapEntry entry = cMapEntries[index];
                        rawData[rawDataIndex++] = entry.blue;
                        rawData[rawDataIndex++] = entry.green;
                        rawData[rawDataIndex++] = entry.red;
                        if (dl == 4) {
                            rawData[rawDataIndex++] = entry.alpha;
                        }
                    }
                }
            } else {
                throw new IOException("TGA: unknown colormap indexing size used: " + bytesPerIndex);
            }

            format = dl == 4 ? Format.RGBA8 : Format.RGB8;
        } else {
            throw new IOException("Monochrome and RLE colormapped images are not supported");
        }


        in.close();
        // Get a pointer to the image memory
        ByteBuffer scratch = BufferUtils.createByteBuffer(rawData.length);
        scratch.clear();
        scratch.put(rawData);
        scratch.rewind();
        // Create the Image object
        Image textureImage = new Image();
        textureImage.setFormat(format);
        textureImage.setWidth(width);
        textureImage.setHeight(height);
        textureImage.setData(scratch);
        return textureImage;
    }
<code>loadImage</code> is a manual image loader which is entirely
independent of AWT. OUT: RGB888 or RGBA8888 Image object


@param in
           InputStream of an uncompressed 24b RGB or 32b RGBA TGA
@param flip
           Flip the image vertically
@return <code>Image</code> object that contains the
        image, either as a RGB888 or RGBA8888
@throws java.io.IOException if an I/O error occurs
jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java