Package pyffi :: Package formats :: Package tga
[hide private]
[frames] | no frames]

Source Code for Package pyffi.formats.tga

  1  """ 
  2  :mod:`pyffi.formats.tga` --- Targa (.tga) 
  3  ========================================= 
  4   
  5  Implementation 
  6  -------------- 
  7   
  8  .. autoclass:: TgaFormat 
  9     :show-inheritance: 
 10     :members: 
 11   
 12  Regression tests 
 13  ---------------- 
 14   
 15  Read a TGA file 
 16  ^^^^^^^^^^^^^^^ 
 17   
 18  >>> # check and read tga file 
 19  >>> stream = open('tests/tga/test.tga', 'rb') 
 20  >>> data = TgaFormat.Data() 
 21  >>> data.inspect(stream) 
 22  >>> data.read(stream) 
 23  >>> stream.close() 
 24  >>> data.header.width 
 25  60 
 26  >>> data.header.height 
 27  20 
 28   
 29  Parse all TGA files in a directory tree 
 30  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
 31   
 32  >>> for stream, data in TgaFormat.walkData('tests/tga'): 
 33  ...     data.read(stream) 
 34  ...     print(stream.name) 
 35  tests/tga/test.tga 
 36  tests/tga/test_footer.tga 
 37   
 38  Create a TGA file from scratch and write to file 
 39  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
 40   
 41  >>> data = TgaFormat.Data() 
 42  >>> from tempfile import TemporaryFile 
 43  >>> stream = TemporaryFile() 
 44  >>> data.write(stream) 
 45  >>> stream.close() 
 46  """ 
 47   
 48  # ***** BEGIN LICENSE BLOCK ***** 
 49  # 
 50  # Copyright (c) 2007-2011, Python File Format Interface 
 51  # All rights reserved. 
 52  # 
 53  # Redistribution and use in source and binary forms, with or without 
 54  # modification, are permitted provided that the following conditions 
 55  # are met: 
 56  # 
 57  #    * Redistributions of source code must retain the above copyright 
 58  #      notice, this list of conditions and the following disclaimer. 
 59  # 
 60  #    * Redistributions in binary form must reproduce the above 
 61  #      copyright notice, this list of conditions and the following 
 62  #      disclaimer in the documentation and/or other materials provided 
 63  #      with the distribution. 
 64  # 
 65  #    * Neither the name of the Python File Format Interface 
 66  #      project nor the names of its contributors may be used to endorse 
 67  #      or promote products derived from this software without specific 
 68  #      prior written permission. 
 69  # 
 70  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 71  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 72  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 73  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 74  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 75  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 76  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 77  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 78  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 79  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 80  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 81  # POSSIBILITY OF SUCH DAMAGE. 
 82  # 
 83  # ***** END LICENSE BLOCK ***** 
 84   
 85  import struct, os, re 
 86   
 87  import pyffi.object_models.xml 
 88  import pyffi.object_models.common 
 89  import pyffi.object_models.xml.basic 
 90  import pyffi.object_models.xml.struct_ 
 91  import pyffi.object_models 
 92  import pyffi.utils.graph 
 93  from pyffi.utils.graph import EdgeFilter 
 94   
95 -class TgaFormat(pyffi.object_models.xml.FileFormat):
96 """This class implements the TGA format.""" 97 xml_file_name = 'tga.xml' 98 # where to look for tga.xml and in what order: 99 # TGAXMLPATH env var, or TgaFormat module directory 100 xml_file_path = [os.getenv('TGAXMLPATH'), os.path.dirname(__file__)] 101 # filter for recognizing tga files by extension 102 RE_FILENAME = re.compile(r'^.*\.tga$', re.IGNORECASE) 103 104 # basic types 105 int = pyffi.object_models.common.Int 106 uint = pyffi.object_models.common.UInt 107 byte = pyffi.object_models.common.Byte 108 ubyte = pyffi.object_models.common.UByte 109 char = pyffi.object_models.common.Char 110 short = pyffi.object_models.common.Short 111 ushort = pyffi.object_models.common.UShort 112 float = pyffi.object_models.common.Float 113 PixelData = pyffi.object_models.common.UndecodedData 114
115 - class FooterString(pyffi.object_models.xml.basic.BasicBase):
116 """The Targa footer signature."""
117 - def __str__(self):
118 return 'TRUEVISION-XFILE.\x00'
119
120 - def read(self, stream, data):
121 """Read signature from stream. 122 123 :param stream: The stream to read from. 124 :type stream: file 125 """ 126 signat = stream.read(18) 127 if signat != self.__str__().encode("ascii"): 128 raise ValueError( 129 "invalid Targa signature: expected '%s' but got '%s'" 130 %(self.__str__(), signat))
131
132 - def write(self, stream, data):
133 """Write signature to stream. 134 135 :param stream: The stream to read from. 136 :type stream: file 137 """ 138 stream.write(self.__str__().encode("ascii"))
139
140 - def get_value(self):
141 """Get signature. 142 143 :return: The signature. 144 """ 145 return self.__str__()
146
147 - def set_value(self, value):
148 """Set signature. 149 150 :param value: The value to assign. 151 :type value: str 152 """ 153 if value != self.__str__(): 154 raise ValueError( 155 "invalid Targa signature: expected '%s' but got '%s'" 156 %(self.__str__(), value))
157
158 - def get_size(self, data=None):
159 """Return number of bytes that the signature occupies in a file. 160 161 :return: Number of bytes. 162 """ 163 return 18
164
165 - def get_hash(self, data=None):
166 """Return a hash value for the signature. 167 168 :return: An immutable object that can be used as a hash. 169 """ 170 return self.__str__()
171
172 - class Image(pyffi.utils.graph.GlobalNode):
173 - def __init__(self):
174 # children are either individual pixels, or RLE packets 175 self.children = []
176
177 - def read(self, stream, data):
178 data = data 179 if data.header.image_type in (TgaFormat.ImageType.INDEXED, 180 TgaFormat.ImageType.RGB, 181 TgaFormat.ImageType.GREY): 182 self.children = [ 183 TgaFormat.Pixel(argument=data.header.pixel_size) 184 for i in xrange(data.header.width 185 * data.header.height)] 186 for pixel in self.children: 187 pixel.read(stream, data) 188 else: 189 self.children = [] 190 count = 0 191 while count < data.header.width * data.header.height: 192 pixel = TgaFormat.RLEPixels( 193 argument=data.header.pixel_size) 194 pixel.read(stream, data) 195 self.children.append(pixel) 196 count += pixel.header.count + 1
197
198 - def write(self, stream, data):
199 data = data 200 for child in self.children: 201 child.arg = data.header.pixel_size 202 child.write(stream, data)
203
204 - def get_detail_child_nodes(self, edge_filter=EdgeFilter()):
205 for child in self.children: 206 yield child
207
208 - def get_detail_child_names(self, edge_filter=EdgeFilter()):
209 for i in xrange(len(self.children)): 210 yield str(i)
211
212 - class Data(pyffi.object_models.FileFormat.Data):
213
214 - def __init__(self):
215 self.header = TgaFormat.Header() 216 self.image = TgaFormat.Image() 217 self.footer = None # TgaFormat.Footer() is optional
218
219 - def inspect(self, stream):
220 """Quick heuristic check if stream contains Targa data, 221 by looking at the first 18 bytes. 222 223 :param stream: The stream to inspect. 224 :type stream: file 225 """ 226 # XXX todo: set some of the actual fields of the header 227 228 pos = stream.tell() 229 # read header 230 try: 231 id_length, colormap_type, image_type, \ 232 colormap_index, colormap_length, colormap_size, \ 233 x_origin, y_origin, width, height, \ 234 pixel_size, flags = struct.unpack("<BBBHHBHHHHBB", 235 stream.read(18)) 236 except struct.error: 237 # could not read 18 bytes 238 # not a TGA file 239 raise ValueError("Not a Targa file.") 240 finally: 241 stream.seek(pos) 242 # check if tga type is valid 243 # check pixel size 244 # check width and height 245 if not(image_type in (1, 2, 3, 9, 10, 11) 246 and pixel_size in (8, 24, 32) 247 and width <= 100000 248 and height <= 100000): 249 raise ValueError("Not a Targa file.")
250 # this looks like a tga file! 251
252 - def read(self, stream):
253 """Read a tga file. 254 255 :param stream: The stream from which to read. 256 :type stream: ``file`` 257 """ 258 # read the file 259 self.inspect(stream) # quick check 260 261 # header 262 self.header.read(stream, self) 263 264 # image 265 self.image.read(stream, self) 266 267 # check if we are at the end of the file 268 if not stream.read(1): 269 self.footer = None 270 return 271 272 # footer 273 stream.seek(-26, os.SEEK_END) 274 self.footer = TgaFormat.Footer() 275 self.footer.read(stream, self)
276
277 - def write(self, stream):
278 """Write a tga file. 279 280 :param stream: The stream to write to. 281 :type stream: ``file`` 282 """ 283 self.header.write(stream, self) 284 self.image.write(stream, self) 285 if self.footer: 286 self.footer.write(stream, self)
287
288 - def get_global_child_nodes(self, edge_filter=EdgeFilter()):
289 yield self.header 290 yield self.image 291 if self.footer: 292 yield self.footer
293
294 - def get_global_child_names(self, edge_filter=EdgeFilter()):
295 yield "Header" 296 yield "Image" 297 if self.footer: 298 yield "Footer"
299 300 if __name__ == '__main__': 301 import doctest 302 doctest.testmod() 303