# TGA files randomly flips?

**URL:** <https://discourse.processing.org/t/tga-files-randomly-flips/41339>\
**Category:** Processing\
**Created:** [March 17, 2023, 11:49pm UTC](https://discourse.processing.org/t/tga-files-randomly-flips/41339 "2023-03-17T23:49:03Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Aryszin](https://avatars.discourse-cdn.com/v4/letter/a/ec9cab/32.png) [@Aryszin](https://discourse.processing.org/u/Aryszin)\
**Post date:** [March 17, 2023, 11:49pm UTC](https://discourse.processing.org/t/tga-files-randomly-flips/41339/1 "2023-03-17T23:49:03Z")

</div>

Im saving some tga files, but it sadly when loading sometimes flips the image…

```auto
layers[currentlayer].save(directory[0]+"/undo/frame_"+currentlayer+"_"+undoimgids[currentlayer]+".tga");

```

this is used to save the file!

```auto
undoimg = loadImage(directory[0]+"/undo/frame_"+currentlayer+"_"+undoimgids[currentlayer]+".tga");

```

this loads it!

---

<div class="post-metadata">

**Author:** ![josephh](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/josephh/32/210_2.png) [@josephh](https://discourse.processing.org/u/josephh)\
**Post date:** [March 20, 2023, 1:04pm UTC](https://discourse.processing.org/t/tga-files-randomly-flips/41339/2 "2023-03-20T13:04:20Z")

</div>

Hi @Aryszin,

> [@Aryszin](#):
>
> when loading sometimes flips the image…

Can you elaborate on that? Does it flips the image on a specific axis? (x or y)

Can you provide an example `.tga` file that does the flip “sometimes”?

---

<div class="post-metadata">

**Author:** ![Aryszin](https://avatars.discourse-cdn.com/v4/letter/a/ec9cab/32.png) [@Aryszin](https://discourse.processing.org/u/Aryszin)\
**Post date:** [March 20, 2023, 2:45pm UTC](https://discourse.processing.org/t/tga-files-randomly-flips/41339/3 "2023-03-20T14:45:20Z")

</div>

It flips in the y axis when loading.

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [March 23, 2023, 12:22pm UTC](https://discourse.processing.org/t/tga-files-randomly-flips/41339/4 "2023-03-23T12:22:27Z")

</div>

Hello @Aryszin,

> [@Aryszin](#):
>
> Im saving some tga files, but it sadly when loading sometimes flips the image…

My exploration of this…

There is a GitHub issue here:

> <https://github.com/processing/processing/issues/1682>
>
> See processing/processing-web#67

Source code here:

> <https://github.com/processing/processing/blob/master/core/src/processing/core/PApplet.java#L5750>

Give it a moment to find the line when opening!

TGA Specifications here:

> <https://github.com/DEAKSoftware/Truevision-TGA/blob/master/truevision-tga.pdf>

I suggest you download it to view.

I modified the source code and providing an example to “reverse” the image on loading (toggle between two choices with mouse press):

```auto
// Loading a TGA and selecting how to "reverse" image (2 choices)
// glv
// 2023-03-23
// Source code was was used on example.

//https://github.com/processing/processing/issues/1682
//https://github.com/DEAKSoftware/Truevision-TGA/blob/master/truevision-tga.pdf
//https://github.com/processing/processing4/blob/0c02c66f9e2fdf2fa13d796a473f4acc35154517/core/src/processing/core/PImage.java

PImage img0, img1, img2;
boolean toggle = true;
int org = 0;
String url;

void setup()
  {
  size(400, 200);

  url = "http://learningprocessing.com/code/assets/sunflower.jpg"; //This can also be your image
  img0 = loadImage(url);
  
  // Save image with Processing  
  img0.save("/data/test.tga");
  
  textSize(24);
  textAlign(CENTER, CENTER);
  
    toggle = !toggle;
  
  // try\catch because myloadTGA() throws exception
  try {
      InputStream input = createInput("/data/test.tga");
      img1 = myloadTGA(input); 
      } 
  catch(IOException e) 
      {
      System.out.println("!");
      } 

  noLoop();
  }
  
void draw()
  {
  
  image(img0, 0, 0);
  text("BEFORE", width/4, 10) ;
  
  image(img1, 200, 0);
  text("AFTER", 3*width/4, 10) ;
  }
  
void mousePressed()
  {  
  toggle = !toggle;
  
  // try\catch because myloadTGA() throws exception
  try {
      InputStream input = createInput("/data/test.tga");
      img1 = myloadTGA(input); 
      } 
  catch(IOException e) 
      {
      System.out.println("!");
      }   
  
  redraw();  
  }

// ******************************************************************

// Source is:
//https://github.com/processing/processing4/blob/0c02c66f9e2fdf2fa13d796a473f4acc35154517/core/src/processing/core/PImage.java#L2806
// see //glv comments for changes

/**
 * Targa image loader for RLE-compressed TGA files.
 * <p>
 * Rewritten for 0115 to read/write RLE-encoded targa images.
 * For 0125, non-RLE encoded images are now supported, along with
 * images whose y-order is reversed (which is standard for TGA files).
 * <p>
 * A version of this function is in MovieMaker.java. Any fixes here
 * should be applied over in MovieMaker as well.
 * <p>
 * Known issue with RLE encoding and odd behavior in some apps:
 * https://github.com/processing/processing/issues/2096
 * Please help!
 */
PImage myloadTGA(InputStream input) throws IOException { // ignore
  byte[] header = new byte[18];
  int offset = 0;
  do {
    int count = input.read(header, offset, header.length - offset);
    if (count == -1) return null;
    offset += count;
  } while (offset < 18);

  /*
      header[2] image type code
   2 (0x02) - Uncompressed, RGB images.
   3 (0x03) - Uncompressed, black and white images.
   10 (0x0A) - Run-length encoded RGB images.
   11 (0x0B) - Compressed, black and white images. (grayscale?)
   header[16] is the bit depth (8, 24, 32)
   header[17] image descriptor (packed bits)
   0x20 is 32 = origin upper-left
   0x28 is 32 + 8 = origin upper-left + 32 bits
   7 6 5 4 3 2 1 0
   128 64 32 16 8 4 2 1
   */

  int format = 0;

  if (((header[2] == 3) || (header[2] == 11)) && // B&W, plus RLE or not
    (header[16] == 8) && // 8 bits
    ((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32 bit
    format = ALPHA;
  } else if (((header[2] == 2) || (header[2] == 10)) && // RGB, RLE or not
    (header[16] == 24) && // 24 bits
    ((header[17] == 0x20) || (header[17] == 0))) { // origin
    format = RGB;
  } else if (((header[2] == 2) || (header[2] == 10)) &&
    (header[16] == 32) &&
    ((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32
    format = ARGB;
  }

  if (format == 0) {
    System.err.println("Unknown .tga file format");
    return null;
  }

  int w = ((header[13] & 0xff) << 8) + (header[12] & 0xff);
  int h = ((header[15] & 0xff) << 8) + (header[14] & 0xff);
  PImage outgoing = new PImage(w, h, format);

// *********************************glv edit*********************************

  // where "reversed" means upper-left corner (normal for most of
  // the modernized world, but "reversed" for the tga spec)
  // https://github.com/processing/processing/issues/1682
  
  boolean reversed = false;
  if(toggle == true)
    reversed = (header[17] & 0x20) != 0;
  else if(toggle == false)
    reversed = (header[17] & 0x20) == 0;
  println("header[17] =", hex(header[17] & 0x20, 2), reversed); 
   
// ****************************************************************************

  if ((header[2] == 2) || (header[2] == 3)) { // not RLE encoded
    if (reversed) {
      int index = (h-1) * w;
      switch (format) {
      case ALPHA:
        for (int y = h-1; y >= 0; y--) {
          for (int x = 0; x < w; x++) {
            outgoing.pixels[index + x] = input.read();
          }
          index -= w;
        }
        break;
      case RGB:
        for (int y = h-1; y >= 0; y--) {
          for (int x = 0; x < w; x++) {
            outgoing.pixels[index + x] =
              input.read() | (input.read() << 8) | (input.read() << 16) |
              0xff000000;
          }
          index -= w;
        }
        break;
      case ARGB:
        for (int y = h-1; y >= 0; y--) {
          for (int x = 0; x < w; x++) {
            outgoing.pixels[index + x] =
              input.read() | (input.read() << 8) | (input.read() << 16) |
              (input.read() << 24);
          }
          index -= w;
        }
      }
    } else { // not reversed
      int count = w * h;
      switch (format) {
      case ALPHA:
        for (int i = 0; i < count; i++) {
          outgoing.pixels[i] = input.read();
        }
        break;
      case RGB:
        for (int i = 0; i < count; i++) {
          outgoing.pixels[i] =
            input.read() | (input.read() << 8) | (input.read() << 16) |
            0xff000000;
        }
        break;
      case ARGB:
        for (int i = 0; i < count; i++) {
          outgoing.pixels[i] =
            input.read() | (input.read() << 8) | (input.read() << 16) |
            (input.read() << 24);
        }
        break;
      }
    }
  } else { // header[2] is 10 or 11
    int index = 0;
    int[] px = outgoing.pixels;

    while (index < px.length) {
      int num = input.read();
      boolean isRLE = (num & 0x80) != 0;
      if (isRLE) {
        num -= 127; // (num & 0x7F) + 1

// *********************************glv edit*********************************
// glv This was giving me issues with errors; I suspect the IDE and NOT the code 

        //int pixel = 0;
        //pixel = switch (format)
        // {
        // case ALPHA -> input.read();
        // case RGB -> 0xFF000000 |
        // input.read() | (input.read() << 8) | (input.read() << 16);
        // case ARGB -> input.read() |
        // (input.read() << 8) | (input.read() << 16) | (input.read() << 24);
        // default -> 0;
        // };

     // glv This worked! Borrowed form Processing 3 source
        int pixel = 0;
        switch (format) {
        case ALPHA:
          pixel = input.read();
          break;
        case RGB:
          pixel = 0xFF000000 |
            input.read() | (input.read() << 8) | (input.read() << 16);
          //(input.read() << 16) | (input.read() << 8) | input.read();
          break;
        case ARGB:
          pixel = input.read() |
            (input.read() << 8) | (input.read() << 16) | (input.read() << 24);
          break;
        }

// ****************************************************************************

        for (int i = 0; i < num; i++) {
          px[index++] = pixel;
          if (index == px.length) break;
        }
      } else { // write up to 127 bytes as uncompressed
        num += 1;
        switch (format) {
        case ALPHA:
          for (int i = 0; i < num; i++) {
            px[index++] = input.read();
          }
          break;
        case RGB:
          for (int i = 0; i < num; i++) {
            px[index++] = 0xFF000000 |
              input.read() | (input.read() << 8) | (input.read() << 16);
            //(input.read() << 16) | (input.read() << 8) | input.read();
          }
          break;
        case ARGB:
          for (int i = 0; i < num; i++) {
            px[index++] = input.read() | //(input.read() << 24) |
              (input.read() << 8) | (input.read() << 16) | (input.read() << 24);
            //(input.read() << 16) | (input.read() << 8) | input.read();
          }
          break;
        }
      }
    }

    if (!reversed) {
      int[] temp = new int[w];
      for (int y = 0; y < h/2; y++) {
        int z = (h-1) - y;
        System.arraycopy(px, y*w, temp, 0, w);
        System.arraycopy(px, z*w, px, y*w, w);
        System.arraycopy(temp, 0, px, z*w, w);
      }
    }
  }
  input.close();
  return outgoing;
}

```

![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/e/3/e35b0d762e39343c4fe179b9427397064b0c207c.jpeg)

![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/9/1/9198f75351f96893d870513774d6377622e7bd68.png)

This can easily be modified to make a custom method for your needs.

`:)`
