Загрузка и сохранение изображений формата TGA

Примеры того, как можно загружать и сохранять изображения формата TGA без всяких сторонних библиотек.

Платформы: Windows, Linux
Авторы: yetifoot и csde_rats


Пример загрузки:

' TGA Loader v0.4 by yetifoot
' PUBLIC DOMAIN
'
' This program loads 16/24/32 bit Targa images, in uncompressed, or RLE format.
'
' The important function is TGA_Load
'   Passing a filename to this function will return a pointer to a 32bit FB GFX compatible
'   buffer.  Failure will result in a NULL pointer.
'
' Credit is due to NeHe
'   http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=33
' Also to Paul Bourke
'   http://astronomy.swin.edu.au/~pbourke/dataformats/tga/

#IFNDEF __TGALOAD_BI__            ' See If The Header Has Been Defined Yet
#DEFINE __TGALOAD_BI__            ' If Not, Define It.

#INCLUDE "crt.bi"                 ' Standard Header For File I/O

Type TGA_HEADER Field = 1
    idlength        As Ubyte
    colourmaptype   As Ubyte
    datatypecode    As Ubyte
    colourmaporigin As Ushort
    colourmaplength As Ushort
    colourmapdepth  As Ubyte
    x_origin        As Ushort
    y_origin        As Ushort
    Width           As Ushort
    height          As Ushort
    bitsperpixel    As Ubyte
    imagedescriptor As Ubyte
End Type

' Type 2 (Uncompressed RGB)
Function TGA_Load_2(hFile As FILE Ptr, tga_info As TGA_HEADER Ptr) As Any Ptr
    Dim As Ubyte Ptr img_buf, tmp_buf
    Dim FBGFXHeader As Uinteger
    Dim As Integer i, y
    Dim numpixels As Uinteger
    Dim tmpcol As Ushort
    Dim As Uinteger w, h

    ' w & h need to be integer for calculations, not the short they are in the header
    w = tga_info->Width
    h = tga_info->height

    ' Calculate number of pixels in image.
    numpixels = w * h

    ' Allocate a buffer for the image (to be compatible with PUT etc)
    img_buf = Allocate((numpixels * 4) + 4)

    ' Read uncompressed data into buffer
    If fread(@img_buf[4], 1, numpixels * (tga_info->bitsperpixel / 8), hFile) <> numpixels * (tga_info->bitsperpixel / 8) Then
        Deallocate(img_buf)
        Return NULL
    End If

    ' If stored in file as 16/24 bit then we need to expand it to the
    ' 32 bits we will be returning
    If tga_info->bitsperpixel = 16 Or tga_info->bitsperpixel = 24 Then
        ' Create a temporary buffer
        tmp_buf = Allocate((numpixels * 4) + 4)
        If tmp_buf = NULL Then
            Deallocate(img_buf)
            Return NULL
        End If
        ' Fix if 16bit
        If tga_info->bitsperpixel = 16 Then
            For i = 0 To numpixels - 1
                tmpcol = img_buf[(i * 2) + 4] Or (img_buf[(i * 2) + 5] Shl 8)
                tmp_buf[(i * 4) + 4] = ((tmpcol And &H001F) * &HFF) / &H1F
                tmp_buf[(i * 4) + 5] = (((tmpcol And &H03E0) Shr 5) * &HFF) / &H1F
                tmp_buf[(i * 4) + 6] = (((tmpcol And &H7C00) Shr 10) * &HFF) / &H1F
                tmp_buf[(i * 4) + 7] = 255
            Next i
        End If
        ' Fix if 24bit
        If tga_info->bitsperpixel = 24 Then
            For i = 0 To numpixels - 1
                tmp_buf[(i * 4) + 4] = img_buf[(i * 3) + 4]
                tmp_buf[(i * 4) + 5] = img_buf[(i * 3) + 5]
                tmp_buf[(i * 4) + 6] = img_buf[(i * 3) + 6]
                tmp_buf[(i * 4) + 7] = 255
            Next i
        End If
        ' Free image buffer, and set the pointer to the temporary buffer
        Deallocate(img_buf)
        img_buf = tmp_buf
        tmp_buf = NULL
    End If

    ' Add the FB GFX style header
    FBGFXHeader = (h Shl 16) Or (w Shl 3) Or 4
    memcpy(@img_buf[0], @FBGFXHeader, 4)

    ' Return a pointer to the 32 bit FB GFX compatible buffer
    Return img_buf
End Function

' Type 10 (RLE Compressed RGB)
Function TGA_Load_10(hFile As FILE Ptr, tga_info As TGA_HEADER Ptr) As Any Ptr
    Dim img_buf As Ubyte Ptr
    Dim FBGFXHeader As Uinteger
    Dim numpixels As Uinteger
    Dim tmpcol As Uinteger
    Dim As Uinteger w, h
    Dim file_buf As Ubyte Ptr
    Dim file_buf_pos As Uinteger
    Dim file_buf_size As Uinteger
    Dim t_pos As Uinteger
    Dim counter As Short
    Dim colorbuffer As Ubyte Ptr
    Dim bytesPerPixel As Integer
    Dim As Uinteger currentpixel              ' Current Pixel We Are Reading From Data
    Dim As Uinteger currentbyte               ' Current Byte We Are Writing Into Imagedata
    Dim As Ubyte chunkheader                  ' Variable To Store The Value Of The Id Chunk

    ' w & h need to be integer for calculations, not the short they are in the header
    w = tga_info->Width
    h = tga_info->height

    ' find out the size of the remaining data (save and restore file pos)
    t_pos = ftell(hFile)
    fseek(hFile, 0, SEEK_END)
    file_buf_size = ftell(hFile) - t_pos
    fseek(hFile, t_pos, SEEK_SET)

    ' Calculate number of pixels in image.
    numpixels = w * h

    ' Allocate a buffer for the image (to be compatible with PUT etc)
    img_buf = Allocate((numpixels * 4) + 4)
    If img_buf = NULL Then
        Return NULL
    End If

    ' Allocate a buffer for the files compressed data (to avoid 1 byte reads)
    file_buf = Allocate(file_buf_size)
    If file_buf = NULL Then
        Deallocate(img_buf)
        Return NULL
    End If

    ' Load files compressed data to buffer
    If fread(file_buf, 1, file_buf_size, hFile) <> file_buf_size Then
        Deallocate(file_buf)
        Deallocate(img_buf)
        Return NULL
    End If

    bytesPerPixel = tga_info->bitsperpixel / 8

    Do                                      ' Start Loop
        chunkheader = file_buf[file_buf_pos]
        file_buf_pos += 1
        If chunkheader < 128 Then             ' If The Chunk Is A 'RAW' Chunk
            chunkheader += 1                    ' Add 1 To The Value To Get Total Number Of Raw Pixels
            ' Start Pixel Reading Loop
            For counter = 0 To chunkheader - 1
                ' Try To Read 1 Pixel
                colorbuffer = @file_buf[file_buf_pos]
                file_buf_pos += bytesPerPixel
                If bytesPerPixel = 2 Then         ' If It's A 16bpp Image...
                    tmpcol = colorbuffer[0] Or (colorbuffer[1] Shl 8)
                    img_buf[currentbyte + 4] = ((tmpcol And &H001F) * &HFF) / &H1F
                    img_buf[currentbyte + 5] = (((tmpcol And &H03E0) Shr 5) * &HFF) / &H1F
                    img_buf[currentbyte + 6] = (((tmpcol And &H7C00) Shr 10) * &HFF) / &H1F
                    img_buf[currentbyte + 7] = 255  ' Write Dummy 'A' Byte
                End If
                If bytesPerPixel = 3 Then         ' If It's A 24bpp Image...
                    img_buf[currentbyte + 4] = colorbuffer[0]
                    img_buf[currentbyte + 5] = colorbuffer[1]
                    img_buf[currentbyte + 6] = colorbuffer[2]
                    img_buf[currentbyte + 7] = 255  ' Write Dummy 'A' Byte
                End If
                If bytesPerPixel = 4 Then         ' If It's A 32bpp Image...
                    img_buf[currentbyte + 4] = colorbuffer[0]
                    img_buf[currentbyte + 5] = colorbuffer[1]
                    img_buf[currentbyte + 6] = colorbuffer[2]
                    img_buf[currentbyte + 7] = colorbuffer[3]
                End If
                ' Increment The Byte Counter By The Number Of Bytes In A Pixel
                currentbyte += 4                  ' Always 4 because output is 32 bit
                currentpixel += 1                 ' Increment The Number Of Pixels By 1
            Next counter
        Else                                  ' If It's An RLE Header
            chunkheader -= 127                  ' Subtract 127 To Get Rid Of The ID Bit
            ' Read The Next Pixel
            colorbuffer = @file_buf[file_buf_pos]
            file_buf_pos += bytesPerPixel
            ' Start The Loop
            For counter = 0 To chunkheader - 1
                If bytesPerPixel = 2 Then         ' If It's A 16bpp Image...
                    tmpcol = colorbuffer[0] Or (colorbuffer[1] Shl 8)
                    img_buf[currentbyte + 4] = ((tmpcol And &H001F) * &HFF) / &H1F
                    img_buf[currentbyte + 5] = (((tmpcol And &H03E0) Shr 5) * &HFF) / &H1F
                    img_buf[currentbyte + 6] = (((tmpcol And &H7C00) Shr 10) * &HFF) / &H1F
                    img_buf[currentbyte + 7] = 255  ' Write Dummy 'A' Byte
                End If
                If bytesPerPixel = 3 Then         ' If It's A 24bpp Image...
                    img_buf[currentbyte + 4] = colorbuffer[0]
                    img_buf[currentbyte + 5] = colorbuffer[1]
                    img_buf[currentbyte + 6] = colorbuffer[2]
                    img_buf[currentbyte + 7] = 255  ' Write Dummy 'A' Byte
                End If
                If bytesPerPixel = 4 Then         ' If It's A 32bpp Image
                    img_buf[currentbyte + 4] = colorbuffer[0]
                    img_buf[currentbyte + 5] = colorbuffer[1]
                    img_buf[currentbyte + 6] = colorbuffer[2]
                    img_buf[currentbyte + 7] = colorbuffer[3]
                End If
                ' Increment The Byte Counter
                currentbyte += 4                 ' Always 4 because output is 32 bit
                currentpixel += 1                ' Increment The Pixel Counter
            Next counter
        End If
    Loop While(currentpixel < numpixels)   ' More Pixels To Read? ... Start Loop Over

    ' Free the file buffer
    Deallocate(file_buf)

    ' Add the FB GFX style header
    FBGFXHeader = (h Shl 16) Or (w Shl 3) Or 4
    memcpy(@img_buf[0], @FBGFXHeader, 4)

    ' Return a pointer to the 32 bit FB GFX compatible buffer
    Return img_buf
End Function

Function TGA_Load(filename As Zstring Ptr) As Any Ptr
    Dim tga_info As TGA_HEADER
    Dim hFile As FILE Ptr
    Dim tga_id As Ubyte Ptr
    Dim As Ubyte Ptr img_buf, tmp_buf
    Dim y As Integer
    Dim numpixels As Uinteger
    Dim As Uinteger w, h

    ' Open file
    hFile = fopen(filename, "rb")
    If hFile = NULL Then
        Return NULL
    End If

    ' Read header
    If fread(@tga_info, 1, Sizeof(TGA_HEADER), hFile) <> Sizeof(TGA_HEADER) Then
        fclose(hFile)
        Return NULL
    End If

    ' Read ID String if there is one
    If tga_info.idlength <> 0 Then
        tga_id = Allocate(tga_info.idlength)
        If fread(@tga_id, 1, tga_info.idlength, hFile) <> tga_info.idlength Then
            Deallocate(tga_id)
            fclose(hFile)
            Return NULL
        End If
        Deallocate(tga_id)
    End If

    ' Load to buffer depending on image type
    Select Case tga_info.datatypecode
        Case 2
            ' Uncompressed RGB
            img_buf = TGA_Load_2(hFile, @tga_info)
        Case 10
            ' Compressed RGB
            img_buf = TGA_Load_10(hFile, @tga_info)
        Case Else
            fclose(hFile)
            Return NULL
    End Select

    ' Vertical flip if necessary
    If ((tga_info.imagedescriptor And 32) Shr 5) = 0 Then
        w = tga_info.width
        h = tga_info.height
        numpixels = w * h
        ' Create a temporary buffer
        tmp_buf = Allocate((numpixels * 4) + 4)
        If tmp_buf = NULL Then
            Deallocate(img_buf)
            fclose(hFile)
            Return NULL
        End If
        For y = 0 To h - 1
            memcpy(@tmp_buf[(y * w * 4) + 4], @img_buf[(((h - 1) - y) * w * 4) + 4], w * 4)
        Next y
        ' Copy FB GFX Header
        memcpy(@tmp_buf[0], @img_buf[0], 4)
        ' Free image buffer, and set the pointer to the temporary buffer
        Deallocate(img_buf)
        img_buf = tmp_buf
        tmp_buf = NULL
    End If

    ' Close the file
    fclose(hFile)

    ' Return a pointer to the 32 bit FB GFX compatible buffer
    Return img_buf
End Function

#ENDIF                                  ' __TGALOAD_BI__ End Inclusion Guard


Dim Handle As Any Ptr
Handle = tga_load("1.tga")

Screenres 320, 240, 32

Put (0, 0),Handle , Alpha

Sleep

Пример сохранения:
#INCLUDE "fbgfx.bi"
Type TGAHEADER Field=1
    As Byte  idlength
    As Byte  colourmaptype
    As Byte  datatypecode
    As Short colourmaporigin
    As Short colourmaplength
    As Byte  colourmapdepth
    As Short x_origin
    As Short y_origin
    As Short Width
    As Short height
    As Byte  bitsperpixel
    As Byte  imagedescriptor
End Type
Enum TGAERRORS
    err_ok   =0
    err_file
    err_format
    err_size
End Enum

Function SaveTGA(Byval filename   As String        , _
    Byval img        As fb.image Ptr=0, _
    Byval targetbits As Integer     = 32) As Integer
    Dim As TGAHEADER   hdr
    Dim As Integer     l,w,h,b,p,hFile
    Dim As Byte Ptr    lpPixels

    If Len(filename)=0 Then Return err_file
    ' file must be in 24 or 32 bits
    If targetbits<>24 And _
        targetbits<>32 Then
        Return err_format
    End If
    ' no screen or screenres active
    If screenptr=0     Then Return err_format
    ' must pixels be locked for reading?
    l=iif(img=0,1,0)
    If l Then
        ' get values from screen
        Screeninfo w,h,,b,p
    Else
        ' get values from image
        w=img->Width:h=img->height:b=img->bpp:p=img->pitch
    End If
    If b<4 Then Return err_format
    If w<1 Then Return err_format
    If h<1 Then Return err_format
    ' try to open a file with write access
    hFile=Freefile
    If Open(filename For Binary Access Write As #hFile) Then
        Return err_file
    End If
    ' build header for uncompressed TGA
    With hdr
        .datatypecode   =2   ' RGB
        .width          =w   ' size x
        .height         =h   ' size y
        .bitsperpixel   =targetbits
    End With
    Put #hFile,,hdr ' write TGA header
    If l Then
        Screenlock
        lpPixels=ScreenPtr ' first tripel in first row
    Else
        lpPixels=cptr(Byte Ptr,img)
        lpPixels+=32 ' first tripel in first row
    End If
    w Shl=2 ' width in bytes
    If targetbits=32 Then ' 32 bits save row by row
        While h
            Put #hFile,,*lpPixels,w ' width in pixels
            lpPixels+=p '+ pitch in bytes = one row
            h-=1
        Wend
    Else ' 24 bits save r,g,b row by row
        While h
            For x As Integer=0 To w-4 Step 4
                Put #hFile,,lpPixels[x+0] ' red
                Put #hFile,,lpPixels[x+1] ' green
                Put #hFile,,lpPixels[x+2] ' blue
            Next
            lpPixels+=p '+ pitch in bytes = one row
            h-=1
        Wend
    End If
    ' close the file
    Close hFile
    ' must be unlocked?
    If l Then Screenunlock
    Return err_ok
End Function

Screenres 640,480,24 ' or 32
Dim As FB.image Ptr test=imagecreate(320,200,0)
Circle test,(160,100),50,Rgb(&HFF,    0,   0)      ' red
Circle test,(160,100),49,Rgb(   0,    0,&HFF),,,,F ' blue
Circle      (320,240),50,Rgb(   0,&HFF,    0)      ' green
Circle      (320,240),49,Rgb(&HFF,&HFF,    0),,,,F ' yellow

If SaveTGA("screen32.tga")        Then' save the whole screen as 32bit tga
    Beep:? "error screen32.tga"
End If
If SaveTGA("image32.tga",test)    Then' save the image as 32bit tga
    Beep:? "error image32.tga"
End If
If SaveTGA("screen24.tga",,24)    Then' save the whole screen as 24bit tga
    Beep:? "error screen24.tga"
End If
If SaveTGA("image24.tga",test,24) Then' save the image as 24bit tga
    Beep:? "error image24.tga"
End If
Sleep 1000
Imagedestroy test
End