-------------------------------------------------------------------------------- -- Copyright 2000 Xilinx, Inc. All rights reserved. -------------------------------------------------------------------------------- -- -- Description: functions to read and write PGM (Portable Gray Map) -- files for Xilinx Reed-Solomon tutorials -- -------------------------------------------------------------------------------- PACKAGE pgm_pkg IS TYPE pixel_array_type IS ARRAY(0 TO 256*256 - 1) OF NATURAL; TYPE pgm_record_type IS RECORD magic_number : STRING(1 TO 2); width : NATURAL; height : NATURAL; max_val : NATURAL; pixel : pixel_array_type; END RECORD; --------------------------------------------------------------------------- -- -- FUNCTION PROTOTYPES: --------------------------------------------------------------------------- -- FUNCTION read_pgm_file(filename : IN STRING) RETURN pgm_record_type; FUNCTION write_pgm_file(filename : IN STRING; pgm : pgm_record_type) RETURN BOOLEAN; END pgm_pkg; LIBRARY ieee; USE ieee.std_logic_1164.ALL; LIBRARY std; USE std.textio.ALL; PACKAGE BODY pgm_pkg IS FUNCTION read_pgm_file(filename : IN STRING) RETURN pgm_record_type IS FILE filehandle : TEXT IS IN filename; VARIABLE inline : LINE; VARIABLE inline_copy : LINE; VARIABLE char : CHARACTER; VARIABLE good : BOOLEAN; VARIABLE number : NATURAL; VARIABLE header_index : NATURAL; VARIABLE magic_number : STRING(1 TO 2); VARIABLE got_magic_number : BOOLEAN := FALSE; VARIABLE width : NATURAL; VARIABLE height : NATURAL; VARIABLE max_val : NATURAL; VARIABLE got_header : BOOLEAN; VARIABLE pi : NATURAL; -- pixel index VARIABLE pgm : pgm_record_type; BEGIN -- PGM (Portable Gray Map) format -- -- Magic number - P2 for ASCII format -- Width - ASCII characters in decimal -- Height - ASCII characters in decimal -- Max_Val - ASCII characters in decimal -- Width*Height grey values, each in ASCII decimal between 0 and Max_Val -- -- any line starting with # is ignored (comments) -- no line should be longer than 70 characters -- -- Width, Height and Max_Val must be separated by whitespace (spaces, tabs, newline) -- -- example: -- -- P2 -- # test.pgm -- 4 4 -- 15 -- 0 1 2 3 -- 4 5 6 7 -- 8 9 10 11 -- 12 13 14 15 -- got_magic_number := FALSE; got_header := FALSE; header_index := 0; pi := 0; -- pixel index WHILE (NOT(ENDFILE(filehandle))) LOOP READLINE(filehandle, inline); -- make a copy of inline string for later (note it's an access type) inline_copy := new STRING'(inline.ALL); READ(inline, char, good); -- move on to next line if it's a comment NEXT WHEN char = '#'; -- special case for magic number IF (got_magic_number = FALSE) THEN ASSERT (char = 'P' OR char = 'p') REPORT "Error: Not PGM format file. Magic number should start with P." SEVERITY FAILURE; READ(inline, char, good); ASSERT (char = '2') REPORT "Error: Not ASCII PGM format file. Magic number should be P2." SEVERITY FAILURE; pgm.magic_number(1) := 'P'; pgm.magic_number(2) := '2'; got_magic_number := TRUE; -- strip off first two characters of inline_copy as they were magic_number READ(inline_copy, char, good); READ(inline_copy, char, good); END IF; IF (got_magic_number = TRUE) THEN -- it should be width, height, max_val, or pixel data -- note that inline_copy is now used as first character of -- inline has been stripped off and can't be re-inserted easily good := TRUE; WHILE (inline_copy'LENGTH /= 0 AND good = TRUE) LOOP READ(inline_copy, number, good); IF (good = TRUE) THEN IF (got_header = TRUE) THEN pgm.pixel(pi) := number; pi := pi + 1; ELSE IF (header_index = 0) THEN -- it must be width pgm.width := number; header_index := 1; -- look for height next ELSIF (header_index = 1) THEN -- it must be height pgm.height := number; header_index := 2; -- look for max_val next ELSE -- it must be max_val pgm.max_val := number; got_header := TRUE; END IF; -- header_index END IF; -- got_header END IF; -- good END LOOP; END IF; -- got_magic_number = TRUE END LOOP; -- NOT ENDFILE ASSERT got_header REPORT "Error: Can't find header information - giving up." SEVERITY FAILURE; RETURN pgm; END read_pgm_file; FUNCTION write_pgm_file(filename : IN STRING; pgm : pgm_record_type) RETURN BOOLEAN IS FILE filehandle : TEXT IS OUT filename; VARIABLE outline : LINE; VARIABLE num_added_to_row : NATURAL; VARIABLE pi : NATURAL; -- pixel index VARIABLE ri : NATURAL := 0; CONSTANT row_comment : STRING := "# row "; BEGIN WRITE(outline, pgm.magic_number); WRITELINE(filehandle, outline); WRITE(outline, pgm.width); WRITELINE(filehandle, outline); WRITE(outline, pgm.height); WRITELINE(filehandle, outline); WRITE(outline, pgm.max_val); WRITELINE(filehandle, outline); pi := 0; WHILE (pi < pgm.width*pgm.height) LOOP num_added_to_row := 0; -- no line should be longer than 70 characters WHILE (pi < pgm.width*pgm.height AND num_added_to_row < pgm.width AND outline'LENGTH < 67) LOOP -- add comment to specify row number IF (pi REM pgm.width = 0) THEN WRITELINE(filehandle, outline); WRITE(outline, row_comment); WRITE(outline, ri, left, 5); WRITELINE(filehandle, outline); ri := ri + 1; END IF; IF (pgm.max_val < 10) THEN WRITE(outline, pgm.pixel(pi), right, 2); ELSIF (pgm.max_val < 100) THEN WRITE(outline, pgm.pixel(pi), right, 3); ELSIF (pgm.max_val < 1000) THEN WRITE(outline, pgm.pixel(pi), right, 4); ELSE WRITE(outline, pgm.pixel(pi), right, 5); END IF; pi := pi + 1; num_added_to_row := num_added_to_row + 1; END LOOP; WRITELINE(filehandle, outline); END LOOP; RETURN TRUE; END write_pgm_file; END pgm_pkg;