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

Source Code for Package pyffi.formats.dds

  1  """ 
  2  :mod:`pyffi.formats.dds` --- DirectDraw Surface (.dds) 
  3  ====================================================== 
  4   
  5  Implementation 
  6  -------------- 
  7   
  8  .. autoclass:: DdsFormat 
  9     :show-inheritance: 
 10     :members: 
 11   
 12  Regression tests 
 13  ---------------- 
 14   
 15  Read a DDS file 
 16  ^^^^^^^^^^^^^^^ 
 17   
 18  >>> # check and read dds file 
 19  >>> stream = open('tests/dds/test.dds', 'rb') 
 20  >>> data = DdsFormat.Data() 
 21  >>> data.inspect(stream) 
 22  >>> data.header.pixel_format.size 
 23  32 
 24  >>> data.header.height 
 25  20 
 26  >>> data.read(stream) 
 27  >>> len(data.pixeldata.get_value()) 
 28  888 
 29   
 30  Parse all DDS files in a directory tree 
 31  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
 32   
 33  >>> for stream, data in DdsFormat.walkData('tests/dds'): 
 34  ...     print(stream.name) 
 35  tests/dds/test.dds 
 36   
 37  Create a DDS file from scratch and write to file 
 38  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
 39   
 40  >>> data = DdsFormat.Data() 
 41  >>> from tempfile import TemporaryFile 
 42  >>> stream = TemporaryFile() 
 43  >>> data.write(stream) 
 44   
 45  Get list of versions 
 46  ^^^^^^^^^^^^^^^^^^^^ 
 47   
 48  >>> for vnum in sorted(DdsFormat.versions.values()): 
 49  ...     print('0x%08X' % vnum) 
 50  0x09000000 
 51  0x0A000000 
 52  """ 
 53   
 54  # ***** BEGIN LICENSE BLOCK ***** 
 55  # 
 56  # Copyright (c) 2007-2011, Python File Format Interface 
 57  # All rights reserved. 
 58  # 
 59  # Redistribution and use in source and binary forms, with or without 
 60  # modification, are permitted provided that the following conditions 
 61  # are met: 
 62  # 
 63  #    * Redistributions of source code must retain the above copyright 
 64  #      notice, this list of conditions and the following disclaimer. 
 65  # 
 66  #    * Redistributions in binary form must reproduce the above 
 67  #      copyright notice, this list of conditions and the following 
 68  #      disclaimer in the documentation and/or other materials provided 
 69  #      with the distribution. 
 70  # 
 71  #    * Neither the name of the Python File Format Interface 
 72  #      project nor the names of its contributors may be used to endorse 
 73  #      or promote products derived from this software without specific 
 74  #      prior written permission. 
 75  # 
 76  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 77  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 78  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 79  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 80  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 81  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 82  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 83  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 84  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 85  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 86  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 87  # POSSIBILITY OF SUCH DAMAGE. 
 88  # 
 89  # ***** END LICENSE BLOCK ***** 
 90   
 91  import struct 
 92  import os 
 93  import re 
 94   
 95  import pyffi.object_models.xml 
 96  import pyffi.object_models.common 
 97  from pyffi.object_models.xml.basic import BasicBase 
 98  import pyffi.object_models 
 99  from pyffi.utils.graph import EdgeFilter 
100 101 -class DdsFormat(pyffi.object_models.xml.FileFormat):
102 """This class implements the DDS format.""" 103 xml_file_name = 'dds.xml' 104 # where to look for dds.xml and in what order: 105 # DDSXMLPATH env var, or DdsFormat module directory 106 xml_file_path = [os.getenv('DDSXMLPATH'), os.path.dirname(__file__)] 107 # file name regular expression match 108 RE_FILENAME = re.compile(r'^.*\.dds$', re.IGNORECASE) 109 # used for comparing floats 110 _EPSILON = 0.0001 111 112 # basic types 113 int = pyffi.object_models.common.Int 114 uint = pyffi.object_models.common.UInt 115 byte = pyffi.object_models.common.Byte 116 ubyte = pyffi.object_models.common.UByte 117 char = pyffi.object_models.common.Char 118 short = pyffi.object_models.common.Short 119 ushort = pyffi.object_models.common.UShort 120 float = pyffi.object_models.common.Float 121 PixelData = pyffi.object_models.common.UndecodedData 122 123 # implementation of dds-specific basic types 124
125 - class HeaderString(BasicBase):
126 """Basic type which implements the header of a DDS file."""
127 - def __init__(self, **kwargs):
128 BasicBase.__init__(self, **kwargs)
129
130 - def __str__(self):
131 return 'DDS'
132
133 - def get_detail_display(self):
134 return self.__str__()
135
136 - def get_hash(self, data=None):
137 """Return a hash value for this value. 138 139 :return: An immutable object that can be used as a hash. 140 """ 141 return None
142
143 - def read(self, stream, data):
144 """Read header string from stream and check it. 145 146 :param stream: The stream to read from. 147 :type stream: file 148 """ 149 hdrstr = stream.read(4) 150 # check if the string is correct 151 if hdrstr != "DDS ".encode("ascii"): 152 raise ValueError( 153 "invalid DDS header: expected 'DDS ' but got '%s'" % hdrstr)
154
155 - def write(self, stream, data):
156 """Write the header string to stream. 157 158 :param stream: The stream to write to. 159 :type stream: file 160 """ 161 stream.write("DDS ".encode("ascii"))
162
163 - def get_size(self, data=None):
164 """Return number of bytes the header string occupies in a file. 165 166 :return: Number of bytes. 167 """ 168 return 4
169 170 @staticmethod
171 - def version_number(version_str):
172 """Converts version string into an integer. 173 174 :param version_str: The version string. 175 :type version_str: str 176 :return: A version integer. 177 178 >>> hex(DdsFormat.version_number('DX10')) 179 '0xa000000' 180 """ 181 return {'DX9': 0x09000000, 'DX10': 0x0A000000}[version_str]
182
183 - class Data(pyffi.object_models.FileFormat.Data):
184 """A class to contain the actual dds data."""
185 - def __init__(self, version=0x09000000):
186 self.version = version 187 self.header = DdsFormat.Header() 188 self.pixeldata = DdsFormat.PixelData() 189 190 # TODO refactor xml model so we can get rid of these 191 self.user_version = 0 192 self.user_version2 = 0
193
194 - def inspect_quick(self, stream):
195 """Quickly checks if stream contains DDS data, and gets the 196 version, by looking at the first 8 bytes. 197 198 :param stream: The stream to inspect. 199 :type stream: file 200 """ 201 pos = stream.tell() 202 try: 203 hdrstr = stream.read(4) 204 if hdrstr != "DDS ".encode("ascii"): 205 raise ValueError("Not a DDS file.") 206 size = struct.unpack("<I", stream.read(4)) 207 if size == 124: 208 self.version = 0x09000000 # DX9 209 elif size == 144: 210 self.version = 0x0A000000 # DX10 211 finally: 212 stream.seek(pos)
213 214 # overriding pyffi.object_models.FileFormat.Data methods 215
216 - def inspect(self, stream):
217 """Quickly checks if stream contains DDS data, and reads the 218 header. 219 220 :param stream: The stream to inspect. 221 :type stream: file 222 """ 223 pos = stream.tell() 224 try: 225 self.inspect_quick(stream) 226 self.header.read(stream, data=self) 227 finally: 228 stream.seek(pos)
229 230
231 - def read(self, stream, verbose=0):
232 """Read a dds file. 233 234 :param stream: The stream from which to read. 235 :type stream: ``file`` 236 :param verbose: The level of verbosity. 237 :type verbose: ``int`` 238 """ 239 # read the file 240 self.inspect_quick(stream) 241 self.header.read(stream, data=self) 242 self.pixeldata.read(stream, data=self) 243 244 # check if we are at the end of the file 245 if stream.read(1): 246 raise ValueError( 247 'end of file not reached: corrupt dds file?')
248
249 - def write(self, stream, verbose=0):
250 """Write a dds file. 251 252 :param stream: The stream to which to write. 253 :type stream: ``file`` 254 :param verbose: The level of verbosity. 255 :type verbose: ``int`` 256 """ 257 # TODO: make sure pixel data has correct length 258 259 # write the file 260 # first header 261 self.header.write(stream, data=self) 262 # next the pixel data 263 self.pixeldata.write(stream, data=self)
264 265 # DetailNode 266
267 - def get_detail_child_nodes(self, edge_filter=EdgeFilter()):
268 return self.header.get_detail_child_nodes(edge_filter=edge_filter)
269
270 - def get_detail_child_names(self, edge_filter=EdgeFilter()):
271 return self.header.get_detail_child_names(edge_filter=edge_filter)
272