El formato de imagen PCX: Parte 2 Final

El formato de imagen PCX: Parte 2 Final


Imagen creada usando Gemini


Introducción

En la parte anterior de este artículo, estuvimos viendo todo lo referente a la organización interna de un archivo de imagen PCX, lo cual como se comentó puede ser útil para comprender cómo están diseñados otros formatos de archivo de imagen de mapa de bits.

En muchos de estos formatos se utiliza la misma compresión RLE para reducir el tamaño del archivo de imagen en disco, y debido a la relativa simplicidad de un PCX resulta idóneo para empezar a estudiar estos contenidos.

En cambio, en esta segunda parte, vamos a dedicarnos a escribir código para mostrar una de estas imágenes en pantalla usando QuickBASIC.

Por si no lo recuerdan, existen distintas variantes o versiones de archivos ".pcx", casi todas pensadas para usar una paleta de colores debido a la práctica común de la época por las limitaciones de memoria de video, no obstante, como se comentó en la primera parte del texto, nosostros nos vamos a centrar en mostrar en pantalla una imagen ".pcx" con soporte para 16 colores (4 BPP).

En realidad no existen tantas diferencias entre las distintas variantes, además de la colocación dentro del archivo de imagen de la paleta de colores, o la ausencia de esta si se trata de la versión para color verdadero. Por eso el procedimiento va a ser bastante parecido en todas las versiones donde se usa una paleta de colores salvo por algunos detalles, puesto en este caso en particular para 16 colores cada byte de la zona de datos del archivo representa dos pixel consecutivos de la imagen en lugar de uno solo como sucede con la variante para 256 colores (8 BPP). Pero en la práctica es necesario tener presente estas diferencias y detectar la versión del archivo de imagen a partir de los datos guardados en la cabecera para ese cometido, porque a la hora de mostrar la imagen en pantalla es imprescindible tener presente hasta el más mínimo detalle o no nos será posible presentarla correctamente.

Nota: En el caso de la versión de PCX para una imagen en color verdadero las diferencia en la interpretación pueden resultar más importantes.

En resumen, para cuando terminemos de escribir este código en QuickBASIC, podremos mostrar en pantalla una imagen PCX de 16 colores como se expone en la siguiente captura de una ventana de DOSBox:

En la captura expuesta se muestra una foto de la cantante israelí Ofra Haza convertida a PCX de 4 BPP (usando IrfanView de IrfanSkiljan); en otro espacio mío compartí hace poco un comentario sobre una canción interpretada por esta artista ahora desaparecida y pueden escucharla en: 🔗 Jerusalén de Oro

Nota: La pobre calidad de la imagen mostrada no se debe ni a la implementación ni a la imagen en sí misma, sino a la pérdida de calidad en ella cuando se la convierte desde una imagen de color verdadero (como era en principio) a una imagen de sólo 16 colores, debido a no disponer de imágenes originales de 16 colores en formato PCX.

Por cierto, me olvidaba de mencionarlo, para llevar a cabo esta tarea utilicé DOSBox y Microsoft QuickBASIC 4.5, si bien no es obligatorio hacerlo así, y se puede usar un entorno como QB64 para poder programar con código QuickBASIC directamente en un Windows moderno de 64 bits, dado estos Windows no tienen soporte para programas de MS-DOS de 16 bits.

De hecho, si descargan el código fuente disponible más adelante, podrán encontrar además de éste y de la imagen, el programa compilado tanto con QuickBASIC 4.5 para correr en un MS-DOS o en un emulador como DOSBox como un programa compilado con QB64 para correrlo directamente en un Windows de 64 bits.

Nota: En caso de necesidad para comprender detalles del formato de imagen PCX, la primera parte de este artículo pueden verla a partir del enlace: 🔗 Parte 1


El programa principal

Las líneas de código mostradas seguidamente resumen el programa principal hecho en QuickBASIC:

DefInt A-Z

DECLARE SUB GetBPCXData (PCXData AS STRING, FileNumber AS INTEGER)
DECLARE SUB ShowPCXRLE4Bits (FileName AS STRING, XResolution AS INTEGER, YResolution AS INTEGER)

Dim XResolution As Integer
Dim YResolution As Integer

Screen 12

XResolution = 640
YResolution = 480

On Error GoTo ErrorHandler

ShowPCXRLE4Bits "Ofra4BPP.pcx", XResolution, YResolution

While InKey$ = ""
Wend

End

ErrorHandler:
Screen 0
Select Case Err
    Case 100
        Print
        Print "La imagen a cargar no parece estar en formato PCX (*.pcx)"
        Print
        End
    Case 101
        Print
        Print "El programa espera una imagen PCX comprimida con RLE y con una profundidad de color de 16 colores"
        Print
        End
    Case 105
        Print
        Print "El programa encontró un final inesperado del archivo de imagen PCX, es posible el archivo esté‚ dañado"
        Print
        End
    Case Else
        Print
        Print "El programa terminó con un error inesperado"
        Print
        End
End Select

La líneas de código mostrada definen un par de subrutinas encargadas de llevar a cabo la tarea de interpretar y mostrar el ".pcx" en la pantalla, establecen un modo de video 12 de 640x480 pixel con 16 colores, y llaman a la subrutina ShowPCXRLE4Bits en la cual está el código capaz de interpretar una imagen de 4 Bits por pixel y comprimida usando RLE.

Por lo demás, el código se limita a tratar algunos errores lanzados por dichas subrutinas en caso de encontrar una anomalía.


La subrutina GetBPCXData

La subrutina GetBPCXData se dedica a leer un byte del archivo PCX, y es utilizada por la subrutina ShowPCXRLE4Bits para ir obteniendo los datos según los necesite.

El código a continuación pertenece a la implementación de esta subrutina:

Sub GetBPCXData (PCXData As String, FileNumber As Integer)
    Dim Dat As String * 1
    Get FileNumber, , Dat
    If EOF(FileNumber) Then Error 105 'Final de archivo inesperado
    PCXData = Dat
End Sub

En este caso se trata de una subrutina bastante simple, la cual devuelve el byte leído a través del parámetro por referencia PCXData, declarado como un String puesto QuickBASIC no dispone de un tipo de dato Byte y se suele emular con una cadena de caracteres de un solo caracter.


La subrutina ShowPCXRLE4Bits

El centro del proceso de mostrar el archivo ".pcx" en pantalla se encuentra en la subrutina ShowPCXRLE4Bits la cual se lista a continuación:

Sub ShowPCXRLE4Bits (FileName As String, XResolution As Integer, YResolution As Integer)
    Dim FileNumber As Integer
    Dim PCXId As Integer
    Dim PCXVersion As Integer
    Dim RLEEncode As Integer
    Dim BitsPerPixel As Integer
    Dim ImageWidth As Long
    Dim ImageHeight As Long
    Dim ColorPlanes As Integer
    Dim BytesPerLine As Long

    FileNumber = FreeFile

    Open FileName For Binary As FileNumber

    Dim PCXFileHeader(127) As String * 1
    Dim i As Integer

    'Los datos de la cabecera son leídos
    For i = 0 To 127
        GetBPCXData PCXFileHeader(i), FileNumber
    Next

    PCXId = Asc(PCXFileHeader(0))
    PCXVersion = Asc(PCXFileHeader(1))
    RLEEncode = Asc(PCXFileHeader(2))
    BitsPerPixel = Asc(PCXFileHeader(3))
    ImageWidth = Asc(PCXFileHeader(9)) * 2 ^ 8 + Asc(PCXFileHeader(8))
    ImageHeight = Asc(PCXFileHeader(11)) * 2 ^ 8 + Asc(PCXFileHeader(10))
    ColorPlanes = Asc(PCXFileHeader(65))
    BytesPerLine = Asc(PCXFileHeader(67)) * 2 ^ 8 + Asc(PCXFileHeader(66))
 
    If PCXId <> 10 Then Error 100 'No es un PCX
    If PCXVersion <> 5 Or RLEEncode <> 1 Or BitsPerPixel <> 4 Or ColorPlanes <> 1 Then Error 101 'No es un PCX del formato esperado

    'Cargar la paletta VGA de 16 colores de la cabecera
    For i = 0 To 15
        Palette i, Int(Asc(PCXFileHeader(i * 3 + 18)) / 4) * 65536 + Int(Asc(PCXFileHeader(i * 3 + 17)) / 4) * 256 + Int(Asc(PCXFileHeader(i * 3 + 16)) / 4)
    Next i

    Dim Dat As String * 1
    Dim ColorCode As Integer
    Dim Count As Integer
    Dim OriginX As Integer
    Dim OriginY As Integer
    Dim x As Integer
    Dim y As Integer

    OriginX = XResolution / 2 - ImageWidth / 2
    OriginY = YResolution / 2 - ImageHeight / 2

    x = OriginX
    y = OriginY

    Do While y - OriginY < ImageHeight
        GetBPCXData Dat, FileNumber
        ColorCode = Asc(Dat)

        Count = 1
        If ColorCode > 192 Then
            Count = ColorCode - 192
            GetBPCXData Dat, FileNumber
            ColorCode = Asc(Dat)
        End If

        For i = 1 To Count
            PSet (x, y), (ColorCode And 240) / 16
            x = x + 1
            PSet (x, y), ColorCode And 15
            x = x + 1
            If x - OriginX > ImageWidth Then
                y = y + 1
                x = OriginX
            End If
        Next i
    Loop
    Close FileNumber
End Sub

En la subrutina primeramente se declaran una serie de variables para obtener valores claves de la cabecera de la imagen, si bien esto también se puede hacer usando una estructura con los elementos correspondientes de la cabecera, y así se podría leer toda la cabecera en una sola operación.

En este caso se lo hizo de manera distinta, y la cabecera se lee en un arreglo de 128 byte para posteriormente obtener los datos leídos en el arreglo y guardarlos en las variables correspondientes; estos datos se leen de byte en byte a través de la subrutina GetBPCXData, si bien también podrían leerse de una sola vez.

Los datos leídos en el arreglo, como se ha dicho, se guardan en las distintas variables, y para esto es necesario tener presente que cuando se trata de valores de más de un byte están guardados en la cabecera usando el formato conocido como punta delgada (Little Endian), por lo cual deben intercambiarse los byte como pueden ver se lo hace.

Nota: El alto y el ancho de la imagen se guardan en la cabecera usando un entero sin signo, pero como QuickBASIC no dispone de enteros sin signo, se usó un dato Long de modo no se produzcan errores debidos a una mala interpretación de los valores.

En lo adelante se leen los datos de la paleta de colores de la cabecera en el arreglo, puesto en los PCX de 16 colores esa es la paleta utilizada, la paleta de la cabecera del archivo de imagen, y después se interpretan sus valores a medida se va cargando la paleta VGA de QuickBASIC con la instrucción Palette.

Por fin se procesan los datos de la imagen usando un bucle Do While, obteniendo los byte de la zona de datos o mapa de bits del archivo de imagen, y se muestran en pantalla los pixeles en sus posiciones relativas usando una instrucción PSet.

Los bytes obtenidos deben distinguirse, porque como se supone se utiliza compresión, estos pueden ser tanto un indice de color de la paleta como un número de la cantidad de veces a repetir un color, y por eso se comparan los valores con 192.

En caso de ser un índice de un color en la paleta (valor del byte menor a 192), se tiene presente que cada byte leído contiene dos códigos de color, puesto cada color del un pixel en esta versión de un ".pcx" para 16 colores se represente con 4 bits (4 BPP).

Por lo demás todo debe estar bastante claro, si se conocen los detalles del formato PCX expuestos en la primera parte del texto, y se estudia con detenimiento el código presentado ahora en la implementación.


Código fuente

Los interesados pueden descargar el fuente de la implementación para mostrar una imagen PCX de 16 colores por medio de este enlace: 🔗 PCX4BPP.zip

Nota: El archivo comprimido también contiene el programa compilado con QuickBASIC 4.5 y con QB64 para poder correrlo directamente en un MS-DOS o un emulador o en un Windows de 64 bits respectivamente.


¿Qué te ha parecido esta última parte?

Tú participación aportando ideas, dando sugerencias, o expresando tu crítica constructiva a través de tus comentarios, es importante y fundamental para permitirnos crecer y crear contenidos con cada vez más calidad.

También es importante tu voto si crees vale la pena concederlo.

¡Nos vemos en otro artículo sobre retroinformática!



0
0
0.000
2 comments
avatar

Thanks for your contribution to the STEMsocial community. Feel free to join us on discord to get to know the rest of us!

Please consider delegating to the @stemsocial account (85% of the curation rewards are returned).

Consider setting @stemsocial as a beneficiary of this post's rewards if you would like to support the community and contribute to its mission of promoting science and education on Hive. 
 

0
0
0.000
avatar

Congratulations @retrotech-cuba! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)

You received more than 2000 upvotes.
Your next target is to reach 2250 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

0
0
0.000