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 }