Exporting images from ZGE

All topics about ZGameEditor goes here.

Moderator: Moderators

User avatar
Ats
Posts: 800
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Exporting images from ZGE

Post by Ats »

Hello. A lot of image programs such as nomacs can't open the TGA files produced by the snippet. After a little research, it appears that a "TRUE" TGA file needs some additional information:
http://fileformats.archiveteam.org/wiki/TGA wrote:TGA files have no signature at the beginning of the file. They can be identified fairly reliably by testing whether the first 18 bytes have sensible values for TGA format, but that is nontrivial.

Some, but not all, TGA files have a signature at the end of the file. In that case, the last 18 bytes of the file are the ASCII characters "TRUEVISION-XFILE.", followed by a NUL byte (0x00).
So here's my modified screenDump function that produce compatible TGA files. Maybe it can be optimized, as I didn't know any another way to store the magical sentence as bytes at the end of the file.

Code: Select all

void saveScreenDump(string filename)
{
  //Based on code by Kjell http://www.emix8.org/forum/viewtopic.php?f=1&t=1156&p=7242&hilit=save+image#p7242
  int w = App.ViewportWidth;
  int h = App.ViewportHeight;

  byte[18] header;

  header[2] = 2;
  header[16] = 32;
  header[17] = 8;

  header[12] = w;
  header[13] = w >> 8;

  header[14] = h;
  header[15] = h >> 8;

  byte[18] footer;

  footer[0]  = 0x54; // T
  footer[1]  = 0x52; // R
  footer[2]  = 0x55; // U
  footer[3]  = 0x45; // E
  footer[4]  = 0x56; // V
  footer[5]  = 0x49; // I
  footer[6]  = 0x53; // S
  footer[7]  = 0x49; // I
  footer[8]  = 0x4F; // O
  footer[9]  = 0x4E; // N
  footer[10] = 0x2D; // -
  footer[11] = 0x58; // X
  footer[12] = 0x46; // F
  footer[13] = 0x49; // I
  footer[14] = 0x4C; // L
  footer[15] = 0x45; // E
  footer[16] = 0x2E; // .
  footer[17] = 0x00; // followed by a NUL byte

  byte[] buffer;
  buffer.SizeDim1 = header.SizeDim1 + w*h*4 + footer.SizeDim1;

  int dst=0;
  for(int i=0; i<header.SizeDim1; i++) buffer[dst++] = header[i];

  glReadPixels(App.ViewportX, App.ViewportY, w, h, 0x80E1, 0x1401, buffer[dst]);

  dst = buffer.SizeDim1 - footer.SizeDim1;
  for(int i=0; i<footer.SizeDim1; i++) buffer[dst++] = footer[i];

  @FileAction( File : @File(FileName : filename, Encoding : 1, TargetArray : buffer), Action : 1 );
}
User avatar
Kjell
Posts: 1924
Joined: Sat Feb 23, 2008 11:15 pm

Re: Exporting images from ZGE

Post by Kjell »

Hi Ats,
Ats wrote: Fri Mar 04, 2022 10:20 amMaybe it can be optimized, as I didn't know any another way to store the magical sentence as bytes at the end of the file.
You could do something like this:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <Source>
<![CDATA[//

void glReadPixels(int x, int y, int width, int height, int format, int type, xptr data){}]]>
      </Source>
    </ZExternalLibrary>
    <ZLibrary Comment="Screenshot" HasInitializer="1">
      <Source>
<![CDATA[//

private byte[18] header;
private byte[18] footer;

{
  header[2] = 2; // Uncompressed RGB
  header[16] = 32; // Bits per pixel

  string s = "TRUEVISION-XFILE.";

  for(int i=0; i<length(s); i++)
  {
    footer[i] = ord(subStr(s, i, 1));
  }

  footer[17] = 0;
}

//

void captureTGA(string fileName)
{
  int w, h;

  w = App.ViewportWidth;
  h = App.ViewportHeight;

  header[12] = w;
  header[13] = w >> 8;

  header[14] = h;
  header[15] = h >> 8;

  byte[] buffer;
  buffer.SizeDim1 = w * h * 4 + 36;

  int j = buffer.SizeDim1 - 18;

  for(int i=0; i<18; i++)
  {
    buffer[i] = header[i];
    buffer[j++] = footer[i];
  }

  glReadPixels(App.ViewportX, App.ViewportY, w, h, 0x80E1, 0x1401, buffer[18]);
  @FileAction(File: @File(FileName: fileName, Encoding: 1, TargetArray: buffer), Action: 1);
}]]>
      </Source>
    </ZLibrary>
  </OnLoaded>
  <OnUpdate>
    <KeyPress Comment="Press S to capture" CharCode="83" RepeatDelay="0.25">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[//

captureTGA("capture.tga");]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnRender>
    <RenderNet VertexColors="255">
      <RenderVertexExpression>
<![CDATA[//

Color.R = Vertex.X + 0.5;
Color.G = Vertex.Y + 0.5;
Color.B = 1;]]>
      </RenderVertexExpression>
    </RenderNet>
  </OnRender>
</ZApplication>
K
User avatar
Ats
Posts: 800
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Exporting images from ZGE

Post by Ats »

Wow, thanks Kjell, I've learned so much from the changes you made in order to optimize the snippet :shock:
User avatar
Ats
Posts: 800
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Exporting images from ZGE

Post by Ats »

I just discovered that grabbing a TGA also takes the alpha layer, resulting in a semi transparent image, depending on which program you uses to open it, or, for instance, when uploading it to Twitter:
omeganaut_alpha.png
omeganaut_alpha.png (124.55 KiB) Viewed 1039 times
So I changed the code to grab only the RGB channels:

Code: Select all

private byte[18] TGA_header;
private byte[18] TGA_footer;

{
  TGA_header[2] = 2; // Uncompressed RGB
  TGA_header[16] = 24; // 24 bits per pixel

  string s = "TRUEVISION-XFILE.";
  for (int i=0; i<length(s); i++) TGA_footer[i] = ord(subStr(s, i, 1));
  TGA_footer[17] = 0;
}

void captureScreen()
{
  if (PhotoMode)
  {
    PhotoMode = 0;

    int w = App.ViewportWidth,
        h = App.ViewportHeight;

    TGA_header[12] = w;
    TGA_header[13] = w >> 8;

    TGA_header[14] = h;
    TGA_header[15] = h >> 8;

    byte[] buffer;
    buffer.SizeDim1 = w * h * 3 + 36; // 3 bytes per pixel + 18 header + 18 footer

    int
      hi = 0,
      fi = buffer.SizeDim1 - 18;

    for (int i=0; i<18; i++)
    {
      buffer[hi++] = TGA_header[i];
      buffer[fi++] = TGA_footer[i];
    }

    glReadPixels(App.ViewportX, App.ViewportY, w, h, 0x1907 /* GL_RGB */, 0x1401 /* GL_UNSIGNED_BYTE */, buffer[18]);

    @FileAction(File: @File(FileName: "Omeganaut_" + IntToStr(getSystemTime()) + ".tga", Encoding: 1, TargetArray: buffer), Action: 1);
  }
}
But now the red and blue are inverted:
Untitled-1.png
Untitled-1.png (47.87 KiB) Viewed 1039 times
Turns out TGA expects the data in BGR format...

So I tried to swap red and blue:

Code: Select all

for (int i = 18; i < w * h * 3 + 18; i += 3) {
    byte temp = buffer[i];
    buffer[i] = buffer[i + 2];     // Red <- Blue
    buffer[i + 2] = temp;         // Blue <- Red
}
or to copy only RGB data from RGBA buffer and ignore alpha:

Code: Select all

int rgbIndex = 18;
for (int i = 0; i < w * h * 4; i += 4) {
    buffer[rgbIndex++] = rgbaBuffer[i];     // R
    buffer[rgbIndex++] = rgbaBuffer[i + 1]; // G
    buffer[rgbIndex++] = rgbaBuffer[i + 2]; // B
    // Skip alpha (rgbaBuffer[i + 3])
}
But the whole for loop is taking ages to process.

I don't understand why GL_RGBA is capturing the colors in the wrong order but correctly?
And is there a way to suppress or not capture the alpha without inverting colors or wasting time?
Last edited by Ats on Sat Dec 14, 2024 12:25 pm, edited 3 times in total.
User avatar
Ats
Posts: 800
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Exporting images from ZGE

Post by Ats »

All right, I found it, I just need to grab the GL_BGR instead:
glReadPixels(App.ViewportX, App.ViewportY, w, h, 0x80E0 /* GL_BGR */, 0x1401 /* GL_UNSIGNED_BYTE */, buffer[18]);
User avatar
Kjell
Posts: 1924
Joined: Sat Feb 23, 2008 11:15 pm

Re: Exporting images from ZGE

Post by Kjell »

Hi Ats,

The 0x80E1 value equals GL_BGRA .. not GL_RGBA :wink: But yes, simply use GL_BGR as format in glReadPixels if you don't want / need the alpha channel.

K
Post Reply