Panda3D
 All Classes Functions Variables Enumerations
pnmReader.cxx
00001 // Filename: pnmReader.cxx
00002 // Created by:  drose (14Jun00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pnmReader.h"
00016 #include "virtualFileSystem.h"
00017 #include "thread.h"
00018 
00019 ////////////////////////////////////////////////////////////////////
00020 //     Function: PNMReader::Destructor
00021 //       Access: Public, Virtual
00022 //  Description:
00023 ////////////////////////////////////////////////////////////////////
00024 PNMReader::
00025 ~PNMReader() {
00026   if (_owns_file) {
00027     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00028 
00029     // We're assuming here that the file was opened via VFS.  That
00030     // may not necessarily be the case, but we don't make that
00031     // distinction.  However, at the moment at least, that
00032     // distinction doesn't matter, since vfs->close_read_file()
00033     // just deletes the file pointer anyway.
00034     vfs->close_read_file(_file);
00035   }
00036   _file = (istream *)NULL;
00037 }
00038 
00039 ////////////////////////////////////////////////////////////////////
00040 //     Function: PNMReader::prepare_read
00041 //       Access: Public, Virtual
00042 //  Description: This method will be called before read_data() or
00043 //               read_row() is called.  It instructs the reader to
00044 //               initialize its data structures as necessary to
00045 //               actually perform the read operation.  
00046 //
00047 //               After this call, _x_size and _y_size should reflect
00048 //               the actual size that will be filled by read_data()
00049 //               (as possibly modified by set_read_size()).
00050 ////////////////////////////////////////////////////////////////////
00051 void PNMReader::
00052 prepare_read() {
00053   if (!_is_valid) {
00054     return;
00055   }
00056 
00057   _x_shift = 0;
00058   _y_shift = 0;
00059   _orig_x_size = _x_size;
00060   _orig_y_size = _y_size;
00061 
00062   if (supports_read_row()) {
00063     // Maybe we can quick-filter each row down as we go.
00064     if (_has_read_size) {
00065       _x_shift = get_reduction_shift(_x_size, _read_x_size);
00066       _x_size = _x_size / (1 << _x_shift);
00067       _y_shift = get_reduction_shift(_y_size, _read_y_size);
00068       _y_size = _y_size / (1 << _y_shift);
00069     }
00070   }
00071 }
00072 
00073 ////////////////////////////////////////////////////////////////////
00074 //     Function: PNMReader::read_data
00075 //       Access: Public, Virtual
00076 //  Description: Reads in an entire image all at once, storing it in
00077 //               the pre-allocated _x_size * _y_size array and alpha
00078 //               pointers.  (If the image type has no alpha channel,
00079 //               alpha is ignored.)  Returns the number of rows
00080 //               correctly read.
00081 //
00082 //               Derived classes need not override this if they
00083 //               instead provide supports_read_row() and read_row(),
00084 //               below.
00085 ////////////////////////////////////////////////////////////////////
00086 int PNMReader::
00087 read_data(xel *array, xelval *alpha) {
00088   if (!is_valid()) {
00089     return 0;
00090   }
00091 
00092   if (_x_shift == 0 && _y_shift == 0) {
00093     // Read with no reduction.
00094     int y;
00095     for (y = 0; y < _y_size; ++y) {
00096       if (!read_row(array + y * _x_size, alpha + y * _x_size, _x_size, _y_size)) {
00097         Thread::consider_yield();
00098         return y;
00099       }
00100     }
00101 
00102   } else {
00103     int x_reduction = (1 << _x_shift);
00104     int y_reduction = (1 << _y_shift);
00105     
00106     int shift = _x_shift + _y_shift;
00107 
00108     // We need a temporary buffer, at least one row wide, with
00109     // full-width integers, for accumulating pixel data.
00110     int *accum_row_array = (int *)alloca(_orig_x_size * sizeof(int) * 3);
00111     int *accum_row_alpha = (int *)alloca(_orig_x_size * sizeof(int));
00112 
00113     // Each time we read a row, we will actually read the full row
00114     // here, before we filter it down into the above.
00115     xel *orig_row_array = (xel *)alloca(_orig_x_size * sizeof(xel));
00116     xelval *orig_row_alpha = (xelval *)alloca(_orig_x_size * sizeof(xelval));
00117     
00118     int y;
00119     for (y = 0; y < _y_size; ++y) {
00120       // Zero out the accumulation data, in preparation for
00121       // holding the results of the below.
00122       memset(accum_row_array, 0, _x_size * sizeof(int) * 3);
00123       if (has_alpha()) {
00124         memset(accum_row_alpha, 0, _x_size * sizeof(int));
00125       }
00126 
00127       for (int yi = 0; yi < y_reduction; ++yi) {
00128         // OK, read a row.  This reads the original, full-size row.
00129         if (!read_row(orig_row_array, orig_row_alpha, _orig_x_size, _orig_y_size)) {
00130           Thread::consider_yield();
00131           return y;
00132         }
00133 
00134         // Boil that row down to its proper, reduced size, and
00135         // accumulate it into the target row.
00136         xel *p = orig_row_array;
00137         int *q = accum_row_array;
00138         int *qstop = q + _x_size * 3;
00139         while (q < qstop) {
00140           for (int xi = 0; xi < x_reduction; ++xi) {
00141             q[0] += (*p).r;
00142             q[1] += (*p).g;
00143             q[2] += (*p).b;
00144             ++p;
00145           }
00146           q += 3;
00147         }
00148         if (has_alpha()) {
00149           // Now do it again for the alpha channel.
00150           xelval *p = orig_row_alpha;
00151           int *q = accum_row_alpha;
00152           int *qstop = q + _x_size;
00153           while (q < qstop) {
00154             for (int xi = 0; xi < x_reduction; ++xi) {
00155               (*q) += (*p);
00156               ++p;
00157             }
00158             ++q;
00159           }
00160         }
00161       }
00162 
00163       // OK, now copy the accumulated pixel data into the final
00164       // result.
00165       xel *target_row_array = array + y * _x_size;
00166       xelval *target_row_alpha = alpha + y * _x_size;
00167 
00168       int *p = accum_row_array;
00169       xel *q = target_row_array;
00170       xel *qstop = q + _x_size;
00171       while (q < qstop) {
00172         (*q).r = (*p++) >> shift;
00173         (*q).g = (*p++) >> shift;
00174         (*q).b = (*p++) >> shift;
00175         ++q;
00176       }
00177 
00178       if (has_alpha()) {
00179         int *p = accum_row_alpha;
00180         xelval *q = target_row_alpha;
00181         xelval *qstop = q + _x_size;
00182         while (q < qstop) {
00183           (*q) = (*p++) >> shift;
00184           ++q;
00185         }
00186       }
00187     }
00188   }
00189     
00190 
00191   return _y_size;
00192 }
00193 
00194 
00195 ////////////////////////////////////////////////////////////////////
00196 //     Function: PNMReader::supports_read_row
00197 //       Access: Public, Virtual
00198 //  Description: Returns true if this particular PNMReader is capable
00199 //               of returning the data one row at a time, via repeated
00200 //               calls to read_row().  Returns false if the only way
00201 //               to read from this file is all at once, via
00202 //               read_data().
00203 ////////////////////////////////////////////////////////////////////
00204 bool PNMReader::
00205 supports_read_row() const {
00206   return false;
00207 }
00208 
00209 ////////////////////////////////////////////////////////////////////
00210 //     Function: PNMReader::read_row
00211 //       Access: Public, Virtual
00212 //  Description: If supports_read_row(), above, returns true, this
00213 //               function may be called repeatedly to read the image,
00214 //               one horizontal row at a time, beginning from the top.
00215 //               Returns true if the row is successfully read, false
00216 //               if there is an error or end of file.
00217 //
00218 //               The x_size and y_size parameters are the value of
00219 //               _x_size and _y_size as originally filled in by the
00220 //               constructor; it is the actual number of pixels in the
00221 //               image.  (The _x_size and _y_size members may have
00222 //               been automatically modified by the time this method
00223 //               is called if we are scaling on load, so should not be
00224 //               used.)
00225 ////////////////////////////////////////////////////////////////////
00226 bool PNMReader::
00227 read_row(xel *, xelval *, int, int) {
00228   return false;
00229 }
00230 
00231 
00232 ////////////////////////////////////////////////////////////////////
00233 //     Function: PNMReader::supports_stream_read
00234 //       Access: Public, Virtual
00235 //  Description: Returns true if this particular PNMReader can read
00236 //               from a general stream (including pipes, etc.), or
00237 //               false if the reader must occasionally fseek() on its
00238 //               input stream, and thus only disk streams are
00239 //               supported.
00240 ////////////////////////////////////////////////////////////////////
00241 bool PNMReader::
00242 supports_stream_read() const {
00243   return false;
00244 }
00245 
00246 ////////////////////////////////////////////////////////////////////
00247 //     Function: PNMReader::get_reduction_shift
00248 //       Access: Private
00249 //  Description: Determines the reduction factor between the original
00250 //               size and the requested size, returned as an exponent
00251 //               of power of 2 (that is, a bit shift).  
00252 //
00253 //               Only power-of-two reductions are supported, since
00254 //               those are common, easy, and fast.  Other reductions
00255 //               will be handled in the higher level code.
00256 ////////////////////////////////////////////////////////////////////
00257 int PNMReader::
00258 get_reduction_shift(int orig_size, int new_size) {
00259   if (new_size == 0) {
00260     return 0;
00261   }
00262 
00263   int reduction = max(orig_size / new_size, 1);
00264 
00265   int shift = 0;
00266 
00267   int r = 2;
00268   while (r <= reduction) {
00269     shift += 1;
00270     r <<= 1;
00271   }
00272 
00273   if ((orig_size % r) != 0) {
00274     // If the reduction isn't even, never mind.
00275     shift = 0;
00276   }
00277 
00278   return shift;
00279 }
 All Classes Functions Variables Enumerations