Panda3D
pnmImage.cxx
1 // Filename: pnmImage.cxx
2 // Created by: drose (14Jun00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pnmImage.h"
16 #include "pnmReader.h"
17 #include "pnmWriter.h"
18 #include "pnmBrush.h"
19 #include "pfmFile.h"
20 #include "config_pnmimage.h"
21 #include "perlinNoise2.h"
22 #include "stackedPerlinNoise2.h"
23 #include <algorithm>
24 
25 ////////////////////////////////////////////////////////////////////
26 // Function: PNMImage::Constructor
27 // Access: Published
28 // Description:
29 ////////////////////////////////////////////////////////////////////
30 PNMImage::
31 PNMImage(const Filename &filename, PNMFileType *type) {
32  _array = NULL;
33  _alpha = NULL;
34  _has_read_size = false;
35 
36  bool result = read(filename, type);
37  if (!result) {
38  pnmimage_cat.error()
39  << "Could not read image " << filename << "\n";
40  }
41 }
42 
43 ////////////////////////////////////////////////////////////////////
44 // Function: PNMImage::clear
45 // Access: Published
46 // Description: Frees all memory allocated for the image, and clears
47 // all its parameters (size, color, type, etc).
48 ////////////////////////////////////////////////////////////////////
49 void PNMImage::
50 clear() {
51  if (_array != (xel *)NULL) {
52  PANDA_FREE_ARRAY(_array);
53  _array = (xel *)NULL;
54  }
55  if (_alpha != (xelval *)NULL) {
56  PANDA_FREE_ARRAY(_alpha);
57  _alpha = (xelval *)NULL;
58  }
59  _x_size = 0;
60  _y_size = 0;
61  _num_channels = 0;
62  _maxval = 255;
63  _inv_maxval = 1.0 / 255.0;
64  _color_space = CS_linear;
65  _comment.clear();
66  _type = (PNMFileType *)NULL;
67  _has_read_size = false;
68  _xel_encoding = XE_generic;
69 }
70 
71 ////////////////////////////////////////////////////////////////////
72 // Function: PNMImage::clear
73 // Access: Published
74 // Description: This flavor of clear() reinitializes the image to an
75 // empty (black) image with the given dimensions.
76 ////////////////////////////////////////////////////////////////////
77 void PNMImage::
78 clear(int x_size, int y_size, int num_channels,
79  xelval maxval, PNMFileType *type, ColorSpace color_space) {
80  clear();
81  nassertv(num_channels >= 1 && num_channels <= 4);
82  nassertv(color_space != CS_unspecified);
83 
84  _x_size = x_size;
85  _y_size = y_size;
86  _num_channels = num_channels;
87  _maxval = maxval;
88  _color_space = color_space;
89  _comment.clear();
90  _type = type;
91  _has_read_size = false;
92 
93  if (has_alpha()) {
94  allocate_alpha();
95  memset(_alpha, 0, sizeof(xelval) * _y_size * _x_size);
96  }
97 
98  allocate_array();
99  memset(_array, 0, sizeof(xel) * _y_size * _x_size);
100 
101  setup_encoding();
102  setup_rc();
103 }
104 
105 ////////////////////////////////////////////////////////////////////
106 // Function: PNMImage::copy_from
107 // Access: Published
108 // Description: Makes this image become a copy of the other image.
109 ////////////////////////////////////////////////////////////////////
110 void PNMImage::
111 copy_from(const PNMImage &copy) {
112  clear();
113  copy_header_from(copy);
114 
115  if (copy.is_valid()) {
116  if (has_alpha()) {
117  memcpy(_alpha, copy._alpha, sizeof(xelval) * _y_size * _x_size);
118  }
119  memcpy(_array, copy._array, sizeof(xel) * _y_size * _x_size);
120  }
121 }
122 
123 ////////////////////////////////////////////////////////////////////
124 // Function: PNMImage::copy_channel
125 // Access: Published
126 // Description: Copies a channel from one image into another.
127 // Images must be the same size
128 ////////////////////////////////////////////////////////////////////
129 void PNMImage::
130 copy_channel(const PNMImage &copy, int src_channel, int dest_channel) {
131  // Make sure the channels are in range
132  nassertv(src_channel >= 0 && src_channel <= 3);
133  nassertv(dest_channel >= 0 && dest_channel <= 3);
134  // Make sure that images are valid
135  if (!copy.is_valid() || !is_valid()) {
136  pnmimage_cat.error() << "One of the images is invalid!\n";
137  return;
138  }
139  // Make sure the images are the same size
140  if (_x_size != copy.get_x_size() || _y_size != copy.get_y_size()){
141  pnmimage_cat.error() << "Image size doesn't match!\n";
142  return;
143  }
144  // Do the actual copying
145  for (int x = 0; x < _x_size; x++) {
146  for (int y = 0; y < _y_size; y++) {
147  LColorf t = get_xel_a(x, y);
148  LColorf o = copy.get_xel_a(x, y);
149  t.set_cell(dest_channel, o.get_cell(src_channel));
150  set_xel_a(x, y, t);
151  }
152  }
153 }
154 
155 ////////////////////////////////////////////////////////////////////
156 // Function: PNMImage::copy_header_from
157 // Access: Published
158 // Description: Copies just the header information into this image.
159 // This will blow away any image data stored in the
160 // image. The new image data will be allocated, but
161 // left unitialized.
162 ////////////////////////////////////////////////////////////////////
163 void PNMImage::
165  clear();
166  PNMImageHeader::operator = (header);
167 
168  if (has_alpha()) {
169  allocate_alpha();
170  }
171 
172  allocate_array();
173  setup_encoding();
174  setup_rc();
175 }
176 
177 ////////////////////////////////////////////////////////////////////
178 // Function: PNMImage::take_from
179 // Access: Published
180 // Description: Move the contents of the other image into this one,
181 // and empty the other image.
182 ////////////////////////////////////////////////////////////////////
183 void PNMImage::
185  clear();
186  PNMImageHeader::operator = (orig);
187  setup_encoding();
188  setup_rc();
189 
190  if (has_alpha()) {
191  _alpha = orig._alpha;
192  orig._alpha = NULL;
193  }
194  _array = orig._array;
195  orig._array = NULL;
196 
197  orig.clear();
198 }
199 
200 ////////////////////////////////////////////////////////////////////
201 // Function: PNMImage::fill_val
202 // Access: Published
203 // Description: Sets the entire image (except the alpha channel) to
204 // the given color.
205 ////////////////////////////////////////////////////////////////////
206 void PNMImage::
207 fill_val(xelval red, xelval green, xelval blue) {
208  if (is_valid()) {
209  for (int y = 0; y < get_y_size(); y++) {
210  for (int x = 0; x < get_x_size(); x++) {
211  set_xel_val(x, y, red, green, blue);
212  }
213  }
214  }
215 }
216 
217 ////////////////////////////////////////////////////////////////////
218 // Function: PNMImage::alpha_fill_val
219 // Access: Published
220 // Description: Sets the entire alpha channel to the given level.
221 ////////////////////////////////////////////////////////////////////
222 void PNMImage::
223 alpha_fill_val(xelval alpha) {
224  if (is_valid()) {
225  if (!has_alpha()) {
226  add_alpha();
227  }
228 
229  for (int y = 0; y < get_y_size(); y++) {
230  for (int x = 0; x < get_x_size(); x++) {
231  set_alpha_val(x, y, alpha);
232  }
233  }
234  }
235 }
236 
237 ////////////////////////////////////////////////////////////////////
238 // Function: PNMImage::read
239 // Access: Published
240 // Description: Reads the indicated image filename. If type is
241 // non-NULL, it is a suggestion for the type of file it
242 // is. Returns true if successful, false on error.
243 ////////////////////////////////////////////////////////////////////
244 bool PNMImage::
245 read(const Filename &filename, PNMFileType *type, bool report_unknown_type) {
246  PNMReader *reader = make_reader(filename, type, report_unknown_type);
247  if (reader == (PNMReader *)NULL) {
248  clear();
249  return false;
250  }
251 
252  return read(reader);
253 }
254 
255 ////////////////////////////////////////////////////////////////////
256 // Function: PNMImage::read
257 // Access: Published
258 // Description: Reads the image data from the indicated stream.
259 //
260 // The filename is advisory only, and may be used
261 // to suggest a type if it has a known extension.
262 //
263 // If type is non-NULL, it is a suggestion for the type
264 // of file it is (and a non-NULL type will override any
265 // magic number test or filename extension lookup).
266 //
267 // Returns true if successful, false on error.
268 ////////////////////////////////////////////////////////////////////
269 bool PNMImage::
270 read(istream &data, const string &filename, PNMFileType *type,
271  bool report_unknown_type) {
273  (&data, false, filename, string(), type, report_unknown_type);
274  if (reader == (PNMReader *)NULL) {
275  clear();
276  return false;
277  }
278  return read(reader);
279 }
280 
281 ////////////////////////////////////////////////////////////////////
282 // Function: PNMImage::read
283 // Access: Published
284 // Description: This flavor of read() uses an already-existing
285 // PNMReader to read the image file. You can get a
286 // reader via the PNMImageHeader::make_reader() methods.
287 // This is a good way to examine the header of a file
288 // (for instance, to determine its size) before actually
289 // reading the entire image.
290 //
291 // The PNMReader is always deleted upon completion,
292 // whether successful or not.
293 ////////////////////////////////////////////////////////////////////
294 bool PNMImage::
295 read(PNMReader *reader) {
296  bool has_read_size = _has_read_size;
297  int read_x_size = _read_x_size;
298  int read_y_size = _read_y_size;
299 
300  clear();
301 
302  if (reader == NULL) {
303  return false;
304  }
305 
306  if (!reader->is_valid()) {
307  delete reader;
308  return false;
309  }
310 
311  if (has_read_size) {
312  reader->set_read_size(read_x_size, read_y_size);
313  }
314  reader->prepare_read();
315 
316  copy_header_from(*reader);
317 
318  if (reader->is_floating_point()) {
319  // Hmm, it's a floating-point file. Quietly convert it to integer.
320  PfmFile pfm;
321  if (!reader->read_pfm(pfm)) {
322  delete reader;
323  return false;
324  }
325  delete reader;
326  return pfm.store(*this);
327  }
328 
329  // We reassign y_size after reading because we might have read a
330  // truncated file.
331  _y_size = reader->read_data(_array, _alpha);
332  delete reader;
333 
334  if (_y_size == 0) {
335  clear();
336  return false;
337  }
338 
339  setup_encoding();
340  setup_rc();
341 
342  if (has_read_size && (_x_size != read_x_size || _y_size != read_y_size)) {
343  // The Reader didn't comply with our size request. Do the sizing
344  // explicitly, then.
345  PNMImage new_image(read_x_size, read_y_size, get_num_channels(),
347  new_image.quick_filter_from(*this);
348  take_from(new_image);
349  }
350 
351  return true;
352 }
353 
354 ////////////////////////////////////////////////////////////////////
355 // Function: PNMImage::write
356 // Access: Published
357 // Description: Writes the image to the indicated filename. If type
358 // is non-NULL, it is a suggestion for the type of image
359 // file to write.
360 ////////////////////////////////////////////////////////////////////
361 bool PNMImage::
362 write(const Filename &filename, PNMFileType *type) const {
363  if (!is_valid()) {
364  return false;
365  }
366 
367  PNMWriter *writer = PNMImageHeader::make_writer(filename, type);
368  if (writer == (PNMWriter *)NULL) {
369  return false;
370  }
371 
372  return write(writer);
373 }
374 
375 ////////////////////////////////////////////////////////////////////
376 // Function: PNMImage::write
377 // Access: Published
378 // Description: Writes the image to the indicated ostream.
379 //
380 // The filename is advisory only, and may be used
381 // suggest a type if it has a known extension.
382 //
383 // If type is non-NULL, it is a suggestion for the type
384 // of image file to write.
385 ////////////////////////////////////////////////////////////////////
386 bool PNMImage::
387 write(ostream &data, const string &filename, PNMFileType *type) const {
388  if (!is_valid()) {
389  return false;
390  }
391 
393  (&data, false, filename, type);
394  if (writer == (PNMWriter *)NULL) {
395  return false;
396  }
397 
398  return write(writer);
399 }
400 
401 ////////////////////////////////////////////////////////////////////
402 // Function: PNMImage::write
403 // Access: Published
404 // Description: This flavor of write() uses an already-existing
405 // PNMWriter to write the image file. You can get a
406 // writer via the PNMImageHeader::make_writer() methods.
407 //
408 // The PNMWriter is always deleted upon completion,
409 // whether successful or not.
410 ////////////////////////////////////////////////////////////////////
411 bool PNMImage::
412 write(PNMWriter *writer) const {
413  if (writer == NULL) {
414  return false;
415  }
416 
417  if (!is_valid()) {
418  delete writer;
419  return false;
420  }
421 
422  writer->copy_header_from(*this);
423 
424  if (!writer->supports_integer()) {
425  // Hmm, it's only a floating-point file type. Convert it from the
426  // integer data we have.
427  PfmFile pfm;
428  if (!pfm.load(*this)) {
429  delete writer;
430  return false;
431  }
432  bool success = writer->write_pfm(pfm);
433  delete writer;
434  return success;
435  }
436 
437  if (is_grayscale() && !writer->supports_grayscale()) {
438  // Copy the gray values to all channels to help out the writer.
439  for (int y = 0; y < get_y_size(); y++) {
440  for (int x = 0; x<get_x_size(); x++) {
441  ((PNMImage *)this)->set_xel_val(x, y, get_gray_val(x, y));
442  }
443  }
444  }
445 
446  int result = writer->write_data(_array, _alpha);
447  delete writer;
448 
449  return (result == _y_size);
450 }
451 
452 ////////////////////////////////////////////////////////////////////
453 // Function: PNMImage::set_color_type
454 // Access: Published
455 // Description: Translates the image to or from grayscale, color, or
456 // four-color mode. Grayscale images are converted to
457 // full-color images with R, G, B set to the original
458 // gray level; color images are converted to grayscale
459 // according to the value of Bright(). The alpha
460 // channel, if added, is initialized to zero.
461 ////////////////////////////////////////////////////////////////////
462 void PNMImage::
463 set_color_type(PNMImage::ColorType color_type) {
464  nassertv((int)color_type >= 1 && (int)color_type <= 4);
465  if (color_type == get_color_type()) {
466  return;
467  }
468 
469  if (!is_grayscale() && is_grayscale(color_type)) {
470  // Convert to grayscale from color
471  for (int y = 0; y < get_y_size(); y++) {
472  for (int x = 0; x < get_x_size(); x++) {
473  set_gray(x, y, get_bright(x, y));
474  }
475  }
476 
477  } else if (is_grayscale() && !is_grayscale(color_type)) {
478  // Convert to color from grayscale
479  for (int y = 0; y < get_y_size(); y++) {
480  for (int x = 0; x < get_x_size(); x++) {
481  set_xel_val(x, y, get_gray_val(x, y));
482  }
483  }
484  }
485 
486  if (has_alpha() && !has_alpha(color_type)) {
487  // Discard the alpha channel
488  if (_alpha != NULL) {
489  PANDA_FREE_ARRAY(_alpha);
490  _alpha = NULL;
491  }
492 
493  } else if (!has_alpha() && has_alpha(color_type)) {
494  // Create a new alpha channel
495  allocate_alpha();
496  memset(_alpha, 0, sizeof(xelval) * (_x_size * _y_size));
497  }
498 
499  _num_channels = (int)color_type;
500  setup_encoding();
501  setup_rc();
502 }
503 
504 ////////////////////////////////////////////////////////////////////
505 // Function: PNMImage::set_color_space
506 // Access: Published
507 // Description: Converts the colors in the image to the indicated
508 // color space. This may be a lossy operation, in
509 // particular when going from sRGB to linear.
510 // The alpha channel remains untouched.
511 //
512 // Note that, because functions like get_xel() and
513 // set_xel() work on linearized floating-point values,
514 // this conversion won't affect those values (aside
515 // from some minor discrepancies due to storage
516 // precision). It does affect the values used by
517 // get_xel_val() and set_xel_val(), though, since
518 // those operate on encoded colors.
519 //
520 // Some color spaces, particularly scRGB, may enforce
521 // the use of a particular maxval setting.
522 ////////////////////////////////////////////////////////////////////
523 void PNMImage::
524 set_color_space(ColorSpace color_space) {
525  nassertv(color_space != CS_unspecified);
526 
527  if (color_space == _color_space) {
528  return;
529  }
530 
531  if (_array != NULL) {
532  size_t array_size = _x_size * _y_size;
533 
534  // Note: only convert RGB, since alpha channel is always linear.
535  switch (color_space) {
536  case CS_linear:
537  if (_maxval == 255 && _color_space == CS_sRGB) {
538  for (size_t i = 0; i < array_size; ++i) {
539  xel &col = _array[i];
540  col.r = decode_sRGB_uchar((unsigned char) col.r);
541  col.g = decode_sRGB_uchar((unsigned char) col.g);
542  col.b = decode_sRGB_uchar((unsigned char) col.b);
543  }
544  } else {
545  for (int x = 0; x < _x_size; ++x) {
546  for (int y = 0; y < _y_size; ++y) {
547  LRGBColorf scaled = get_xel(x, y) * _maxval + 0.5f;
548  xel &col = row(y)[x];
549  col.r = clamp_val((int)scaled[0]);
550  col.g = clamp_val((int)scaled[1]);
551  col.b = clamp_val((int)scaled[2]);
552  }
553  }
554  }
555  break;
556 
557  case CS_sRGB:
558  if (_maxval == 255 && _color_space == CS_linear) {
559  for (size_t i = 0; i < array_size; ++i) {
560  xel &col = _array[i];
561  col.r = encode_sRGB_uchar((unsigned char) col.r);
562  col.g = encode_sRGB_uchar((unsigned char) col.g);
563  col.b = encode_sRGB_uchar((unsigned char) col.b);
564  }
565  } else {
566  for (int x = 0; x < _x_size; ++x) {
567  for (int y = 0; y < _y_size; ++y) {
568  xel &col = row(y)[x];
569  encode_sRGB_uchar(get_xel_a(x, y), col);
570  }
571  }
572  }
573  break;
574 
575  case CS_scRGB:
576  for (int x = 0; x < _x_size; ++x) {
577  for (int y = 0; y < _y_size; ++y) {
578  LRGBColorf scaled = get_xel(x, y) * 8192.f + 4096.5f;
579  xel &col = row(y)[x];
580  col.r = min(max(0, (int)scaled[0]), 65535);
581  col.g = min(max(0, (int)scaled[1]), 65535);
582  col.b = min(max(0, (int)scaled[2]), 65535);
583  }
584  }
585  _maxval = 65535;
586  break;
587 
588  default:
589  nassertv(false);
590  break;
591  }
592 
593  // Initialize the new encoding settings.
594  _color_space = color_space;
595  setup_encoding();
596  }
597 }
598 
599 ////////////////////////////////////////////////////////////////////
600 // Function: PNMImage::make_grayscale
601 // Access: Published
602 // Description: Converts the image from RGB to grayscale. Any alpha
603 // channel, if present, is left undisturbed. The
604 // optional rc, gc, bc values represent the relative
605 // weights to apply to each channel to convert it to
606 // grayscale.
607 ////////////////////////////////////////////////////////////////////
608 void PNMImage::
609 make_grayscale(float rc, float gc, float bc) {
610  if (is_grayscale()) {
611  return;
612  }
613 
614  for (int y = 0; y < get_y_size(); y++) {
615  for (int x = 0; x < get_x_size(); x++) {
616  set_gray(x, y, min(get_bright(x, y, rc, gc, bc), 1.0f));
617  }
618  }
619 
620  _num_channels = has_alpha() ? 2 : 1;
621  setup_encoding();
622  setup_rc();
623 }
624 
625 ////////////////////////////////////////////////////////////////////
626 // Function: PNMImage::premultiply_alpha
627 // Access: Published
628 // Description: Converts an image in-place to its "premultiplied"
629 // form, where, for every pixel in the image, the
630 // red, green, and blue components are multiplied by
631 // that pixel's alpha value.
632 //
633 // This does not modify any alpha values.
634 ////////////////////////////////////////////////////////////////////
635 void PNMImage::
637  if (!has_alpha()) {
638  return;
639  }
640 
641  for (int y = 0; y < get_y_size(); y++) {
642  for (int x = 0; x < get_x_size(); x++) {
643  float alpha = get_alpha(x, y);
644  float r = get_red(x, y) * alpha;
645  float g = get_green(x, y) * alpha;
646  float b = get_blue(x, y) * alpha;
647  set_xel(x, y, r, g, b);
648  }
649  }
650 }
651 
652 ////////////////////////////////////////////////////////////////////
653 // Function: PNMImage::unpremultiply_alpha
654 // Access: Published
655 // Description: Converts an image in-place to its "straight alpha"
656 // form (presumably from a "premultiplied" form),
657 // where, for every pixel in the image, the red,
658 // green, and blue components are divided by that
659 // pixel's alpha value.
660 //
661 // This does not modify any alpha values.
662 ////////////////////////////////////////////////////////////////////
663 void PNMImage::
665  if (!has_alpha()) {
666  return;
667  }
668 
669  for (int y = 0; y < get_y_size(); y++) {
670  for (int x = 0; x < get_x_size(); x++) {
671  float alpha = get_alpha(x, y);
672  if (alpha > 0) {
673  float r = get_red(x, y) / alpha;
674  float g = get_green(x, y) / alpha;
675  float b = get_blue(x, y) / alpha;
676  set_xel(x, y, r, g, b);
677  }
678  }
679  }
680 }
681 
682 ////////////////////////////////////////////////////////////////////
683 // Function: PNMImage::reverse_rows
684 // Access: Published
685 // Description: Performs an in-place reversal of the row (y) data.
686 ////////////////////////////////////////////////////////////////////
687 void PNMImage::
689  if (_array != NULL) {
690  xel *new_array = (xel *)PANDA_MALLOC_ARRAY(_x_size * _y_size * sizeof(xel));
691  for (int y = 0; y < _y_size; y++) {
692  int new_y = _y_size - 1 - y;
693  memcpy(new_array + new_y * _x_size, _array + y * _x_size, _x_size * sizeof(xel));
694  }
695  PANDA_FREE_ARRAY(_array);
696  _array = new_array;
697  }
698 
699  if (_alpha != NULL) {
700  xelval *new_alpha = (xelval *)PANDA_MALLOC_ARRAY(_x_size * _y_size * sizeof(xelval));
701  for (int y = 0; y < _y_size; y++) {
702  int new_y = _y_size - 1 - y;
703  memcpy(new_alpha + new_y * _x_size, _alpha + y * _x_size, _x_size * sizeof(xelval));
704  }
705  PANDA_FREE_ARRAY(_alpha);
706  _alpha = new_alpha;
707  }
708 }
709 
710 ////////////////////////////////////////////////////////////////////
711 // Function: PNMImage::flip
712 // Access: Published
713 // Description: Reverses, transposes, and/or rotates the image
714 // in-place according to the specified parameters. If
715 // flip_x is true, the x axis is reversed; if flip_y is
716 // true, the y axis is reversed. Then, if transpose is
717 // true, the x and y axes are exchanged. These
718 // parameters can be used to select any combination of
719 // 90-degree or 180-degree rotations and flips.
720 ////////////////////////////////////////////////////////////////////
721 void PNMImage::
722 flip(bool flip_x, bool flip_y, bool transpose) {
723  if (transpose) {
724  // Transposed case. X becomes Y, Y becomes X.
725  if (_array != NULL) {
726  xel *new_array = (xel *)PANDA_MALLOC_ARRAY(_x_size * _y_size * sizeof(xel));
727  for (int xi = 0; xi < _x_size; ++xi) {
728  xel *row = new_array + xi * _y_size;
729  int source_xi = !flip_x ? xi : _x_size - 1 - xi;
730  for (int yi = 0; yi < _y_size; ++yi) {
731  int source_yi = !flip_y ? yi : _y_size - 1 - yi;
732  xel *source_row = _array + source_yi * _x_size;
733  row[yi] = source_row[source_xi];
734  }
735  }
736  PANDA_FREE_ARRAY(_array);
737  _array = new_array;
738  }
739 
740  if (_alpha != NULL) {
741  xelval *new_alpha = (xelval *)PANDA_MALLOC_ARRAY(_x_size * _y_size * sizeof(xelval));
742  for (int xi = 0; xi < _x_size; ++xi) {
743  xelval *row = new_alpha + xi * _y_size;
744  int source_xi = !flip_x ? xi : _x_size - 1 - xi;
745  for (int yi = 0; yi < _y_size; ++yi) {
746  int source_yi = !flip_y ? yi : _y_size - 1 - yi;
747  xelval *source_row = _alpha + source_yi * _x_size;
748  row[yi] = source_row[source_xi];
749  }
750  }
751 
752  PANDA_FREE_ARRAY(_alpha);
753  _alpha = new_alpha;
754  }
755 
756  int t = _x_size;
757  _x_size = _y_size;
758  _y_size = t;
759 
760  } else {
761  // Non-transposed. X is X, Y is Y.
762  if (_array != NULL) {
763  xel *new_array = (xel *)PANDA_MALLOC_ARRAY(_x_size * _y_size * sizeof(xel));
764  for (int yi = 0; yi < _y_size; ++yi) {
765  xel *row = new_array + yi * _x_size;
766  int source_yi = !flip_y ? yi : _y_size - 1 - yi;
767  xel *source_row = _array + source_yi * _x_size;
768 
769  for (int xi = 0; xi < _x_size; ++xi) {
770  int source_xi = !flip_x ? xi : _x_size - 1 - xi;
771  row[xi] = source_row[source_xi];
772  }
773  }
774  PANDA_FREE_ARRAY(_array);
775  _array = new_array;
776  }
777 
778  if (_alpha != NULL) {
779  xelval *new_alpha = (xelval *)PANDA_MALLOC_ARRAY(_x_size * _y_size * sizeof(xelval));
780  for (int yi = 0; yi < _y_size; ++yi) {
781  xelval *row = new_alpha + yi * _x_size;
782  int source_yi = !flip_y ? yi : _y_size - 1 - yi;
783  xelval *source_row = _alpha + source_yi * _x_size;
784 
785  for (int xi = 0; xi < _x_size; ++xi) {
786  int source_xi = !flip_x ? xi : _x_size - 1 - xi;
787  row[xi] = source_row[source_xi];
788  }
789  }
790 
791  PANDA_FREE_ARRAY(_alpha);
792  _alpha = new_alpha;
793  }
794  }
795 }
796 
797 ////////////////////////////////////////////////////////////////////
798 // Function: PNMImage::set_maxval
799 // Access: Published
800 // Description: Rescales the image to the indicated maxval.
801 ////////////////////////////////////////////////////////////////////
802 void PNMImage::
803 set_maxval(xelval maxval) {
804  nassertv(maxval > 0);
805 
806  if (maxval != _maxval) {
807  float ratio = (float)maxval / (float)_maxval;
808 
809  if (is_grayscale()) {
810  for (int y = 0; y < get_y_size(); y++) {
811  for (int x = 0; x < get_x_size(); x++) {
812  set_gray_val(x, y, (xelval)((long)get_gray_val(x, y) * ratio));
813  }
814  }
815  } else {
816  for (int y = 0; y < get_y_size(); y++) {
817  for (int x = 0; x < get_x_size(); x++) {
818  set_red_val(x, y, (xelval)((long)get_red_val(x, y) * ratio));
819  set_green_val(x, y, (xelval)((long)get_green_val(x, y) * ratio));
820  set_blue_val(x, y, (xelval)((long)get_blue_val(x, y) * ratio));
821  }
822  }
823  }
824 
825  if (has_alpha()) {
826  for (int y = 0; y < get_y_size(); y++) {
827  for (int x = 0; x < get_x_size(); x++) {
828  set_alpha_val(x, y,
829  (xelval)((long)get_alpha_val(x, y) * ratio));
830  }
831  }
832  }
833  _maxval = maxval;
834  setup_encoding();
835  }
836 }
837 
838 ////////////////////////////////////////////////////////////////////
839 // Function: PNMImage::get_channel_val
840 // Access: Published
841 // Description: Returns the nth component color at the indicated
842 // pixel. The channel index should be in the range
843 // 0..(get_num_channels()-1). The channels are ordered B,
844 // G, R, A. This is slightly less optimal than
845 // accessing the component values directly by named
846 // methods. The value returned is in the range
847 // 0..maxval.
848 ////////////////////////////////////////////////////////////////////
849 xelval PNMImage::
850 get_channel_val(int x, int y, int channel) const {
851  switch (channel) {
852  case 0:
853  return get_blue_val(x, y);
854 
855  case 1:
856  return (_num_channels == 2) ? get_alpha_val(x, y) : get_green_val(x, y);
857 
858  case 2:
859  return get_red_val(x, y);
860 
861  case 3:
862  return get_alpha_val(x, y);
863 
864  default:
865  pnmimage_cat.error()
866  << "Invalid request for channel " << channel << " in "
867  << get_num_channels() << "-channel image.\n";
868  nassertr(false, 0);
869  return 0;
870  }
871 }
872 
873 ////////////////////////////////////////////////////////////////////
874 // Function: PNMImage::set_channel_val
875 // Access: Published
876 // Description: Sets the nth component color at the indicated
877 // pixel. The channel index should be in the range
878 // 0..(get_num_channels()-1). The channels are ordered B,
879 // G, R, A. This is slightly less optimal than
880 // setting the component values directly by named
881 // methods. The value given should be in the range
882 // 0..maxval.
883 ////////////////////////////////////////////////////////////////////
884 void PNMImage::
885 set_channel_val(int x, int y, int channel, xelval value) {
886  switch (channel) {
887  case 0:
888  set_blue_val(x, y, value);
889  break;
890 
891  case 1:
892  if (_num_channels == 2) {
893  set_alpha_val(x, y, value);
894  } else {
895  set_green_val(x, y, value);
896  }
897  break;
898 
899  case 2:
900  set_red_val(x, y, value);
901  break;
902 
903  case 3:
904  set_alpha_val(x, y, value);
905  break;
906 
907  default:
908  nassertv(false);
909  }
910 }
911 
912 ////////////////////////////////////////////////////////////////////
913 // Function: PNMImage::get_channel
914 // Access: Published
915 // Description: Returns the nth component color at the indicated
916 // pixel. The channel index should be in the range
917 // 0..(get_num_channels()-1). The channels are ordered B,
918 // G, R, A. This is slightly less optimal than
919 // accessing the component values directly by named
920 // methods. The value returned is a float in the range
921 // 0..1.
922 ////////////////////////////////////////////////////////////////////
923 float PNMImage::
924 get_channel(int x, int y, int channel) const {
925  switch (channel) {
926  case 0:
927  return get_blue(x, y);
928 
929  case 1:
930  return (_num_channels == 2) ? get_alpha(x, y) : get_green(x, y);
931 
932  case 2:
933  return get_red(x, y);
934 
935  case 3:
936  return get_alpha(x, y);
937 
938  default:
939  pnmimage_cat.error()
940  << "Invalid request for channel " << channel << " in "
941  << get_num_channels() << "-channel image.\n";
942  nassertr(false, 0);
943  return 0;
944  }
945 }
946 
947 ////////////////////////////////////////////////////////////////////
948 // Function: PNMImage::set_channel
949 // Access: Published
950 // Description: Sets the nth component color at the indicated
951 // pixel. The channel index should be in the range
952 // 0..(get_num_channels()-1). The channels are ordered B,
953 // G, R, A. This is slightly less optimal than
954 // setting the component values directly by named
955 // methods. The value given should be a float in the
956 // range 0..1.
957 ////////////////////////////////////////////////////////////////////
958 void PNMImage::
959 set_channel(int x, int y, int channel, float value) {
960  switch (channel) {
961  case 0:
962  set_blue(x, y, value);
963  break;
964 
965  case 1:
966  if (_num_channels == 2) {
967  set_alpha(x, y, value);
968  } else {
969  set_green(x, y, value);
970  }
971  break;
972 
973  case 2:
974  set_red(x, y, value);
975  break;
976 
977  case 3:
978  set_alpha(x, y, value);
979  break;
980 
981  default:
982  nassertv(false);
983  }
984 }
985 
986 ////////////////////////////////////////////////////////////////////
987 // Function: PNMImage::get_pixel
988 // Access: Published
989 // Description: Returns the (r, g, b, a) pixel value at the indicated
990 // pixel, using a PixelSpec object.
991 ////////////////////////////////////////////////////////////////////
993 get_pixel(int x, int y) const {
994  switch (_num_channels) {
995  case 1:
996  return PixelSpec(get_gray_val(x, y));
997  case 2:
998  return PixelSpec(get_gray_val(x, y), get_alpha_val(x, y));
999  case 3:
1000  return PixelSpec(get_xel_val(x, y));
1001  case 4:
1002  return PixelSpec(get_xel_val(x, y), get_alpha_val(x, y));
1003  }
1004 
1005  return PixelSpec(0);
1006 }
1007 
1008 ////////////////////////////////////////////////////////////////////
1009 // Function: PNMImage::set_pixel
1010 // Access: Published
1011 // Description: Sets the (r, g, b, a) pixel value at the indicated
1012 // pixel, using a PixelSpec object.
1013 ////////////////////////////////////////////////////////////////////
1014 void PNMImage::
1015 set_pixel(int x, int y, const PixelSpec &pixel) {
1016  xel p;
1017  PPM_ASSIGN(p, pixel._red, pixel._green, pixel._blue);
1018  set_xel_val(x, y, p);
1019  if (_alpha != NULL) {
1020  set_alpha_val(x, y, pixel._alpha);
1021  }
1022 }
1023 
1024 ////////////////////////////////////////////////////////////////////
1025 // Function: PNMImage::blend
1026 // Access: Published
1027 // Description: Smoothly blends the indicated pixel value in with
1028 // whatever was already in the image, based on the given
1029 // alpha value. An alpha of 1.0 is fully opaque and
1030 // completely replaces whatever was there previously;
1031 // alpha of 0.0 is fully transparent and does nothing.
1032 ////////////////////////////////////////////////////////////////////
1033 void PNMImage::
1034 blend(int x, int y, float r, float g, float b, float alpha) {
1035  if (alpha >= 1.0) {
1036  // Completely replace the previous color.
1037  if (has_alpha()) {
1038  set_alpha(x, y, 1.0);
1039  }
1040  set_xel(x, y, r, g, b);
1041 
1042  } else if (alpha > 0.0f) {
1043  // Blend with the previous color.
1044  float prev_alpha = has_alpha() ? get_alpha(x, y) : 1.0f;
1045 
1046  if (prev_alpha == 0.0f) {
1047  // Nothing there previously; replace with this new color.
1048  set_alpha(x, y, alpha);
1049  set_xel(x, y, r, g, b);
1050 
1051  } else {
1052  // Blend the color with the previous color.
1053  LRGBColorf prev_rgb = get_xel(x, y);
1054  r = r + (1.0f - alpha) * (get_red(x, y) - r);
1055  g = g + (1.0f - alpha) * (get_green(x, y) - g);
1056  b = b + (1.0f - alpha) * (get_blue(x, y) - b);
1057  alpha = prev_alpha + alpha * (1.0f - prev_alpha);
1058 
1059  if (has_alpha()) {
1060  set_alpha(x, y, alpha);
1061  }
1062  set_xel(x, y, r, g, b);
1063  }
1064  }
1065 }
1066 
1067 ////////////////////////////////////////////////////////////////////
1068 // Function: PNMImage::set_array
1069 // Access: Public
1070 // Description: Replaces the underlying PNMImage array with the
1071 // indicated pointer. Know what you are doing! The new
1072 // array must be the correct size and must have been
1073 // allocated via PANDA_MALLOC_ARRAY(). The PNMImage
1074 // object becomes the owner of this pointer and will
1075 // eventually free it with PANDA_FREE_ARRAY(). The
1076 // previous array, if any, will be freed with
1077 // PANDA_FREE_ARRAY() when this call is made.
1078 ////////////////////////////////////////////////////////////////////
1079 void PNMImage::
1080 set_array(xel *array) {
1081  if (_array != (xel *)NULL) {
1082  PANDA_FREE_ARRAY(_array);
1083  }
1084  _array = array;
1085 }
1086 
1087 ////////////////////////////////////////////////////////////////////
1088 // Function: PNMImage::set_alpha_array
1089 // Access: Public
1090 // Description: Replaces the underlying PNMImage alpha array with the
1091 // indicated pointer. Know what you are doing! The new
1092 // array must be the correct size and must have been
1093 // allocated via PANDA_MALLOC_ARRAY(). The PNMImage
1094 // object becomes the owner of this pointer and will
1095 // eventually free it with PANDA_FREE_ARRAY(). The
1096 // previous array, if any, will be freed with
1097 // PANDA_FREE_ARRAY() when this call is made.
1098 ////////////////////////////////////////////////////////////////////
1099 void PNMImage::
1100 set_alpha_array(xelval *alpha) {
1101  if (_alpha != (xelval *)NULL) {
1102  PANDA_FREE_ARRAY(_alpha);
1103  }
1104  _alpha = alpha;
1105 }
1106 
1107 ////////////////////////////////////////////////////////////////////
1108 // Function: PNMImage::copy_sub_image
1109 // Access: Published
1110 // Description: Copies a rectangular area of another image into a
1111 // rectangular area of this image. Both images must
1112 // already have been initialized. The upper-left corner
1113 // of the region in both images is specified, and the
1114 // size of the area; if the size is omitted, it defaults
1115 // to the entire other image, or the largest piece that
1116 // will fit.
1117 ////////////////////////////////////////////////////////////////////
1118 void PNMImage::
1119 copy_sub_image(const PNMImage &copy, int xto, int yto,
1120  int xfrom, int yfrom, int x_size, int y_size) {
1121  int xmin, ymin, xmax, ymax;
1122  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1123  xmin, ymin, xmax, ymax);
1124 
1125  if (get_maxval() == copy.get_maxval() &&
1126  get_color_space() == copy.get_color_space()) {
1127  // The simple case: no pixel value rescaling is required.
1128  int x, y;
1129  for (y = ymin; y < ymax; y++) {
1130  for (x = xmin; x < xmax; x++) {
1131  set_xel_val(x, y, copy.get_xel_val(x - xmin + xfrom, y - ymin + yfrom));
1132  }
1133  }
1134 
1135  if (has_alpha() && copy.has_alpha()) {
1136  for (y = ymin; y < ymax; y++) {
1137  for (x = xmin; x < xmax; x++) {
1138  set_alpha_val(x, y, copy.get_alpha_val(x - xmin + xfrom, y - ymin + yfrom));
1139  }
1140  }
1141  }
1142 
1143  } else {
1144  // The harder case: rescale pixel values according to maxval.
1145  int x, y;
1146  for (y = ymin; y < ymax; y++) {
1147  for (x = xmin; x < xmax; x++) {
1148  set_xel(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom));
1149  }
1150  }
1151 
1152  if (has_alpha() && copy.has_alpha()) {
1153  for (y = ymin; y < ymax; y++) {
1154  for (x = xmin; x < xmax; x++) {
1155  set_alpha(x, y, copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom));
1156  }
1157  }
1158  }
1159  }
1160 }
1161 
1162 ////////////////////////////////////////////////////////////////////
1163 // Function: PNMImage::blend_sub_image
1164 // Access: Published
1165 // Description: Behaves like copy_sub_image(), except the alpha
1166 // channel of the copy is used to blend the copy into
1167 // the destination image, instead of overwriting pixels
1168 // unconditionally.
1169 //
1170 // If pixel_scale is not 1.0, it specifies an amount to
1171 // scale each *alpha* value of the source image before
1172 // applying it to the target image.
1173 //
1174 // If pixel_scale is 1.0 and the copy has no alpha
1175 // channel, this degenerates into copy_sub_image().
1176 ////////////////////////////////////////////////////////////////////
1177 void PNMImage::
1178 blend_sub_image(const PNMImage &copy, int xto, int yto,
1179  int xfrom, int yfrom, int x_size, int y_size,
1180  float pixel_scale) {
1181  if (!copy.has_alpha() && pixel_scale == 1.0) {
1182  copy_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size);
1183  return;
1184  }
1185 
1186  int xmin, ymin, xmax, ymax;
1187  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1188  xmin, ymin, xmax, ymax);
1189 
1190  int x, y;
1191  if (copy.has_alpha()) {
1192  for (y = ymin; y < ymax; y++) {
1193  for (x = xmin; x < xmax; x++) {
1194  blend(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom),
1195  copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom) * pixel_scale);
1196  }
1197  }
1198  } else {
1199  for (y = ymin; y < ymax; y++) {
1200  for (x = xmin; x < xmax; x++) {
1201  blend(x, y, copy.get_xel(x - xmin + xfrom, y - ymin + yfrom),
1202  pixel_scale);
1203  }
1204  }
1205  }
1206 }
1207 
1208 ////////////////////////////////////////////////////////////////////
1209 // Function: PNMImage::add_sub_image
1210 // Access: Published
1211 // Description: Behaves like copy_sub_image(), except the copy pixels
1212 // are added to the pixels of the destination, after
1213 // scaling by the specified pixel_scale. Unlike
1214 // blend_sub_image(), the alpha channel is not treated
1215 // specially.
1216 ////////////////////////////////////////////////////////////////////
1217 void PNMImage::
1218 add_sub_image(const PNMImage &copy, int xto, int yto,
1219  int xfrom, int yfrom, int x_size, int y_size,
1220  float pixel_scale) {
1221  int xmin, ymin, xmax, ymax;
1222  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1223  xmin, ymin, xmax, ymax);
1224 
1225  int x, y;
1226  if (has_alpha() && copy.has_alpha()) {
1227  for (y = ymin; y < ymax; y++) {
1228  for (x = xmin; x < xmax; x++) {
1229  set_alpha(x, y, get_alpha(x, y) + copy.get_alpha(x, y) * pixel_scale);
1230  }
1231  }
1232  }
1233 
1234  for (y = ymin; y < ymax; y++) {
1235  for (x = xmin; x < xmax; x++) {
1236  LRGBColorf rgb1 = get_xel(x, y);
1237  LRGBColorf rgb2 = copy.get_xel(x, y);
1238  set_xel(x, y,
1239  rgb1[0] + rgb2[0] * pixel_scale,
1240  rgb1[1] + rgb2[1] * pixel_scale,
1241  rgb1[2] + rgb2[2] * pixel_scale);
1242  }
1243  }
1244 }
1245 
1246 ////////////////////////////////////////////////////////////////////
1247 // Function: PNMImage::mult_sub_image
1248 // Access: Published
1249 // Description: Behaves like copy_sub_image(), except the copy pixels
1250 // are multiplied to the pixels of the destination, after
1251 // scaling by the specified pixel_scale. Unlike
1252 // blend_sub_image(), the alpha channel is not treated
1253 // specially.
1254 ////////////////////////////////////////////////////////////////////
1255 void PNMImage::
1256 mult_sub_image(const PNMImage &copy, int xto, int yto,
1257  int xfrom, int yfrom, int x_size, int y_size,
1258  float pixel_scale) {
1259  int xmin, ymin, xmax, ymax;
1260  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1261  xmin, ymin, xmax, ymax);
1262 
1263  int x, y;
1264  if (has_alpha() && copy.has_alpha()) {
1265  for (y = ymin; y < ymax; y++) {
1266  for (x = xmin; x < xmax; x++) {
1267  set_alpha(x, y, get_alpha(x, y) * copy.get_alpha(x, y) * pixel_scale);
1268  }
1269  }
1270  }
1271 
1272  for (y = ymin; y < ymax; y++) {
1273  for (x = xmin; x < xmax; x++) {
1274  LRGBColorf rgb1 = get_xel(x, y);
1275  LRGBColorf rgb2 = copy.get_xel(x, y);
1276  set_xel(x, y,
1277  rgb1[0] * rgb2[0] * pixel_scale,
1278  rgb1[1] * rgb2[1] * pixel_scale,
1279  rgb1[2] * rgb2[2] * pixel_scale);
1280  }
1281  }
1282 }
1283 
1284 ////////////////////////////////////////////////////////////////////
1285 // Function: PNMImage::darken_sub_image
1286 // Access: Published
1287 // Description: Behaves like copy_sub_image(), but the resulting
1288 // color will be the darker of the source and
1289 // destination colors at each pixel (and at each R, G,
1290 // B, A component value).
1291 //
1292 // If pixel_scale is not 1.0, it specifies an amount to
1293 // scale each pixel value of the source image before
1294 // applying it to the target image. The scale is
1295 // applied with the center at 1.0: scaling the pixel
1296 // value smaller brings it closer to 1.0.
1297 ////////////////////////////////////////////////////////////////////
1298 void PNMImage::
1299 darken_sub_image(const PNMImage &copy, int xto, int yto,
1300  int xfrom, int yfrom, int x_size, int y_size,
1301  float pixel_scale) {
1302  int xmin, ymin, xmax, ymax;
1303  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1304  xmin, ymin, xmax, ymax);
1305 
1306  if (get_maxval() == copy.get_maxval() && pixel_scale == 1.0f &&
1307  get_color_space() == CS_linear && copy.get_color_space() == CS_linear) {
1308  // The simple case: no pixel value rescaling is required.
1309  int x, y;
1310  for (y = ymin; y < ymax; y++) {
1311  for (x = xmin; x < xmax; x++) {
1312  xel c = copy.get_xel_val(x - xmin + xfrom, y - ymin + yfrom);
1313  xel o = get_xel_val(x, y);
1314  xel p;
1315  PPM_ASSIGN(p, min(c.r, o.r), min(c.g, o.g), min(c.b, o.b));
1316  set_xel_val(x, y, p);
1317  }
1318  }
1319 
1320  if (has_alpha() && copy.has_alpha()) {
1321  for (y = ymin; y < ymax; y++) {
1322  for (x = xmin; x < xmax; x++) {
1323  xelval c = copy.get_alpha_val(x - xmin + xfrom, y - ymin + yfrom);
1324  xelval o = get_alpha_val(x, y);
1325  set_alpha_val(x, y, min(c, o));
1326  }
1327  }
1328  }
1329 
1330  } else {
1331  // The harder case: rescale pixel values according to maxval.
1332  int x, y;
1333  for (y = ymin; y < ymax; y++) {
1334  for (x = xmin; x < xmax; x++) {
1335  LRGBColorf c = copy.get_xel(x - xmin + xfrom, y - ymin + yfrom);
1336  LRGBColorf o = get_xel(x, y);
1337  LRGBColorf p;
1338  p.set(min(1.0f - ((1.0f - c[0]) * pixel_scale), o[0]),
1339  min(1.0f - ((1.0f - c[1]) * pixel_scale), o[1]),
1340  min(1.0f - ((1.0f - c[2]) * pixel_scale), o[2]));
1341  set_xel(x, y, p);
1342  }
1343  }
1344 
1345  if (has_alpha() && copy.has_alpha()) {
1346  for (y = ymin; y < ymax; y++) {
1347  for (x = xmin; x < xmax; x++) {
1348  float c = copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom);
1349  float o = get_alpha(x, y);
1350  set_alpha(x, y, min(1.0f - ((1.0f - c) * pixel_scale), o));
1351  }
1352  }
1353  }
1354  }
1355 }
1356 
1357 ////////////////////////////////////////////////////////////////////
1358 // Function: PNMImage::lighten_sub_image
1359 // Access: Published
1360 // Description: Behaves like copy_sub_image(), but the resulting
1361 // color will be the lighter of the source and
1362 // destination colors at each pixel (and at each R, G,
1363 // B, A component value).
1364 //
1365 // If pixel_scale is not 1.0, it specifies an amount to
1366 // scale each pixel value of the source image before
1367 // applying it to the target image.
1368 ////////////////////////////////////////////////////////////////////
1369 void PNMImage::
1370 lighten_sub_image(const PNMImage &copy, int xto, int yto,
1371  int xfrom, int yfrom, int x_size, int y_size,
1372  float pixel_scale) {
1373  int xmin, ymin, xmax, ymax;
1374  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1375  xmin, ymin, xmax, ymax);
1376 
1377  if (get_maxval() == copy.get_maxval() && pixel_scale == 1.0f &&
1378  get_color_space() == CS_linear && copy.get_color_space() == CS_linear) {
1379  // The simple case: no pixel value rescaling is required.
1380  int x, y;
1381  for (y = ymin; y < ymax; y++) {
1382  for (x = xmin; x < xmax; x++) {
1383  xel c = copy.get_xel_val(x - xmin + xfrom, y - ymin + yfrom);
1384  xel o = get_xel_val(x, y);
1385  xel p;
1386  PPM_ASSIGN(p, max(c.r, o.r), max(c.g, o.g), max(c.b, o.b));
1387  set_xel_val(x, y, p);
1388  }
1389  }
1390 
1391  if (has_alpha() && copy.has_alpha()) {
1392  for (y = ymin; y < ymax; y++) {
1393  for (x = xmin; x < xmax; x++) {
1394  xelval c = copy.get_alpha_val(x - xmin + xfrom, y - ymin + yfrom);
1395  xelval o = get_alpha_val(x, y);
1396  set_alpha_val(x, y, max(c, o));
1397  }
1398  }
1399  }
1400 
1401  } else {
1402  // The harder case: rescale pixel values according to maxval.
1403  int x, y;
1404  for (y = ymin; y < ymax; y++) {
1405  for (x = xmin; x < xmax; x++) {
1406  LRGBColorf c = copy.get_xel(x - xmin + xfrom, y - ymin + yfrom);
1407  LRGBColorf o = get_xel(x, y);
1408  LRGBColorf p;
1409  p.set(max(c[0] * pixel_scale, o[0]),
1410  max(c[1] * pixel_scale, o[1]),
1411  max(c[2] * pixel_scale, o[2]));
1412  set_xel(x, y, p);
1413  }
1414  }
1415 
1416  if (has_alpha() && copy.has_alpha()) {
1417  for (y = ymin; y < ymax; y++) {
1418  for (x = xmin; x < xmax; x++) {
1419  float c = copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom);
1420  float o = get_alpha(x, y);
1421  set_alpha(x, y, max(c * pixel_scale, o));
1422  }
1423  }
1424  }
1425  }
1426 }
1427 
1428 ////////////////////////////////////////////////////////////////////
1429 // Function: PNMImage::threshold
1430 // Access: Published
1431 // Description: Selectively copies each pixel from either one source
1432 // or another source, depending on the pixel value of
1433 // the indicated channel of select_image.
1434 //
1435 // For each pixel (x, y):
1436 //
1437 // s = select_image.get_channel(x, y, channel). Set this
1438 // image's (x, y) to:
1439 //
1440 // lt.get_xel(x, y) if s < threshold, or
1441 //
1442 // ge.get_xel(x, y) if s >= threshold
1443 //
1444 // Any of select_image, lt, or ge may be the same
1445 // PNMImge object as this image, or the same as each
1446 // other; or they may all be different. All images must
1447 // be the same size. As a special case, lt and ge may
1448 // both be 1x1 images instead of the source image size.
1449 ////////////////////////////////////////////////////////////////////
1450 void PNMImage::
1451 threshold(const PNMImage &select_image, int channel, float threshold,
1452  const PNMImage &lt, const PNMImage &ge) {
1453  nassertv(get_x_size() <= select_image.get_x_size() && get_y_size() <= select_image.get_y_size());
1454  nassertv(channel >= 0 && channel < select_image.get_num_channels());
1455 
1456  xelval threshold_val = select_image.to_val(threshold);
1457 
1458  if (lt.get_x_size() == 1 && lt.get_y_size() == 1 &&
1459  ge.get_x_size() == 1 && ge.get_y_size() == 1) {
1460  // FIXME: what if select_image has different color space?
1461  // 1x1 source images.
1462  xel lt_val = lt.get_xel_val(0, 0);
1463  xelval lt_alpha = 0;
1464  if (lt.has_alpha()) {
1465  lt_alpha = lt.get_alpha_val(0, 0);
1466  }
1467  if (lt.get_maxval() != get_maxval()) {
1468  float scale = (float)get_maxval() / (float)lt.get_maxval();
1469  PPM_ASSIGN(lt_val,
1470  (xelval)(PPM_GETR(lt_val) * scale + 0.5),
1471  (xelval)(PPM_GETG(lt_val) * scale + 0.5),
1472  (xelval)(PPM_GETB(lt_val) * scale + 0.5));
1473  lt_alpha = (xelval)(lt_alpha * scale + 0.5);
1474  }
1475 
1476  xel ge_val = ge.get_xel_val(0, 0);
1477  xelval ge_alpha = 0;
1478  if (ge.has_alpha()) {
1479  ge_alpha = ge.get_alpha_val(0, 0);
1480  }
1481  if (ge.get_maxval() != get_maxval()) {
1482  float scale = (float)get_maxval() / (float)ge.get_maxval();
1483  PPM_ASSIGN(ge_val,
1484  (xelval)(PPM_GETR(ge_val) * scale + 0.5),
1485  (xelval)(PPM_GETG(ge_val) * scale + 0.5),
1486  (xelval)(PPM_GETB(ge_val) * scale + 0.5));
1487  ge_alpha = (xelval)(ge_alpha * scale + 0.5);
1488  }
1489 
1490  int x, y;
1491 
1492  if (channel == 3) {
1493  // Further special case: the alpha channel.
1494  if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
1495  // Copy alpha channel too.
1496  for (y = 0; y < get_y_size(); y++) {
1497  for (x = 0; x < get_x_size(); x++) {
1498  if (select_image.get_alpha_val(x, y) < threshold_val) {
1499  set_xel_val(x, y, lt_val);
1500  set_alpha_val(x, y, lt_alpha);
1501  } else {
1502  set_xel_val(x, y, ge_val);
1503  set_alpha_val(x, y, ge_alpha);
1504  }
1505  }
1506  }
1507 
1508  } else {
1509  // Don't copy alpha channel.
1510  for (y = 0; y < get_y_size(); y++) {
1511  for (x = 0; x < get_x_size(); x++) {
1512  if (select_image.get_alpha_val(x, y) < threshold_val) {
1513  set_xel_val(x, y, lt_val);
1514  } else {
1515  set_xel_val(x, y, ge_val);
1516  }
1517  }
1518  }
1519  }
1520  } else {
1521  // Any generic channel.
1522  if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
1523  // Copy alpha channel too.
1524  for (y = 0; y < get_y_size(); y++) {
1525  for (x = 0; x < get_x_size(); x++) {
1526  if (select_image.get_channel_val(x, y, channel) < threshold_val) {
1527  set_xel_val(x, y, lt_val);
1528  set_alpha_val(x, y, lt_alpha);
1529  } else {
1530  set_xel_val(x, y, ge_val);
1531  set_alpha_val(x, y, ge_alpha);
1532  }
1533  }
1534  }
1535 
1536  } else {
1537  // Don't copy alpha channel.
1538  for (y = 0; y < get_y_size(); y++) {
1539  for (x = 0; x < get_x_size(); x++) {
1540  if (select_image.get_channel_val(x, y, channel) < threshold_val) {
1541  set_xel_val(x, y, lt_val);
1542  } else {
1543  set_xel_val(x, y, ge_val);
1544  }
1545  }
1546  }
1547  }
1548  }
1549 
1550  } else {
1551  // Same-sized source images.
1552  nassertv(get_x_size() <= lt.get_x_size() && get_y_size() <= lt.get_y_size());
1553  nassertv(get_x_size() <= ge.get_x_size() && get_y_size() <= ge.get_y_size());
1554 
1555  if (get_maxval() == lt.get_maxval() && get_maxval() == ge.get_maxval() &&
1556  get_color_space() == lt.get_color_space() &&
1557  get_color_space() == ge.get_color_space()) {
1558  // Simple case: the maxvals are all equal. Copy by integer value.
1559  int x, y;
1560 
1561  if (channel == 3) {
1562  // Further special case: the alpha channel.
1563  if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
1564  // Copy alpha channel too.
1565  for (y = 0; y < get_y_size(); y++) {
1566  for (x = 0; x < get_x_size(); x++) {
1567  if (select_image.get_alpha_val(x, y) < threshold_val) {
1568  set_xel_val(x, y, lt.get_xel_val(x, y));
1569  set_alpha_val(x, y, lt.get_alpha_val(x, y));
1570  } else {
1571  set_xel_val(x, y, ge.get_xel_val(x, y));
1572  set_alpha_val(x, y, ge.get_alpha_val(x, y));
1573  }
1574  }
1575  }
1576 
1577  } else {
1578  // Don't copy alpha channel.
1579  for (y = 0; y < get_y_size(); y++) {
1580  for (x = 0; x < get_x_size(); x++) {
1581  if (select_image.get_alpha_val(x, y) < threshold_val) {
1582  set_xel_val(x, y, lt.get_xel_val(x, y));
1583  } else {
1584  set_xel_val(x, y, ge.get_xel_val(x, y));
1585  }
1586  }
1587  }
1588  }
1589  } else {
1590  // Any generic channel.
1591  if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
1592  // Copy alpha channel too.
1593  for (y = 0; y < get_y_size(); y++) {
1594  for (x = 0; x < get_x_size(); x++) {
1595  if (select_image.get_channel_val(x, y, channel) < threshold_val) {
1596  set_xel_val(x, y, lt.get_xel_val(x, y));
1597  set_alpha_val(x, y, lt.get_alpha_val(x, y));
1598  } else {
1599  set_xel_val(x, y, ge.get_xel_val(x, y));
1600  set_alpha_val(x, y, ge.get_alpha_val(x, y));
1601  }
1602  }
1603  }
1604 
1605  } else {
1606  // Don't copy alpha channel.
1607  for (y = 0; y < get_y_size(); y++) {
1608  for (x = 0; x < get_x_size(); x++) {
1609  if (select_image.get_channel_val(x, y, channel) < threshold_val) {
1610  set_xel_val(x, y, lt.get_xel_val(x, y));
1611  } else {
1612  set_xel_val(x, y, ge.get_xel_val(x, y));
1613  }
1614  }
1615  }
1616  }
1617  }
1618 
1619  } else {
1620  // General case: the maxvals are different. Copy by floating-point value.
1621  int x, y;
1622 
1623  if (has_alpha() && lt.has_alpha() && ge.has_alpha()) {
1624  for (y = 0; y < get_y_size(); y++) {
1625  for (x = 0; x < get_x_size(); x++) {
1626  if (select_image.get_channel_val(x, y, channel) < threshold_val) {
1627  set_xel(x, y, lt.get_xel(x, y));
1628  set_alpha(x, y, lt.get_alpha(x, y));
1629  } else {
1630  set_xel(x, y, ge.get_xel(x, y));
1631  set_alpha(x, y, ge.get_alpha(x, y));
1632  }
1633  }
1634  }
1635  } else {
1636  for (y = 0; y < get_y_size(); y++) {
1637  for (x = 0; x < get_x_size(); x++) {
1638  if (select_image.get_channel_val(x, y, channel) < threshold_val) {
1639  set_xel(x, y, lt.get_xel(x, y));
1640  } else {
1641  set_xel(x, y, ge.get_xel(x, y));
1642  }
1643  }
1644  }
1645  }
1646  }
1647  }
1648 }
1649 
1650 ////////////////////////////////////////////////////////////////////
1651 // Function: PNMImage::fill_distance_inside
1652 // Access: Published
1653 // Description: Replaces this image with a grayscale image whose gray
1654 // channel represents the linear Manhattan distance from
1655 // the nearest dark pixel in the given mask image, up to
1656 // the specified radius value (which also becomes the
1657 // new maxval). radius may range from 0 to maxmaxval;
1658 // smaller values will compute faster. A dark pixel is
1659 // defined as one whose pixel value is < threshold.
1660 //
1661 // If shrink_from_border is true, then the mask image is
1662 // considered to be surrounded by a border of dark
1663 // pixels; otherwise, the border isn't considered.
1664 //
1665 // This can be used, in conjunction with threshold, to
1666 // shrink a mask image inwards by a certain number of
1667 // pixels.
1668 //
1669 // The mask image may be the same image as this one, in
1670 // which case it is destructively modified by this
1671 // process.
1672 ////////////////////////////////////////////////////////////////////
1673 void PNMImage::
1674 fill_distance_inside(const PNMImage &mask, float threshold, int radius, bool shrink_from_border) {
1675  nassertv(radius <= PNM_MAXMAXVAL);
1676  PNMImage dist(mask.get_x_size(), mask.get_y_size(), 1, radius, NULL, CS_linear);
1677  dist.fill_val(radius);
1678 
1679  xelval threshold_val = mask.to_val(threshold);
1680 
1681  for (int yi = 0; yi < mask.get_y_size(); ++yi) {
1682  for (int xi = 0; xi < mask.get_x_size(); ++xi) {
1683  if (mask.get_gray_val(xi, yi) < threshold_val) {
1684  dist.do_fill_distance(xi, yi, 0);
1685  }
1686  }
1687  }
1688 
1689  if (shrink_from_border) {
1690  // Also measure from the image border.
1691  for (int yi = 0; yi < mask.get_y_size(); ++yi) {
1692  dist.do_fill_distance(0, yi, 1);
1693  dist.do_fill_distance(mask.get_x_size() - 1, yi, 1);
1694  }
1695  for (int xi = 0; xi < mask.get_x_size(); ++xi) {
1696  dist.do_fill_distance(xi, 0, 1);
1697  dist.do_fill_distance(xi, mask.get_y_size() - 1, 1);
1698  }
1699  }
1700 
1701  take_from(dist);
1702 }
1703 
1704 ////////////////////////////////////////////////////////////////////
1705 // Function: PNMImage::fill_distance_outside
1706 // Access: Published
1707 // Description: Replaces this image with a grayscale image whose gray
1708 // channel represents the linear Manhattan distance from
1709 // the nearest white pixel in the given mask image, up to
1710 // the specified radius value (which also becomes the
1711 // new maxval). radius may range from 0 to maxmaxval;
1712 // smaller values will compute faster. A white pixel is
1713 // defined as one whose pixel value is >= threshold.
1714 //
1715 // This can be used, in conjunction with threshold, to
1716 // grow a mask image outwards by a certain number of
1717 // pixels.
1718 //
1719 // The mask image may be the same image as this one, in
1720 // which case it is destructively modified by this
1721 // process.
1722 ////////////////////////////////////////////////////////////////////
1723 void PNMImage::
1724 fill_distance_outside(const PNMImage &mask, float threshold, int radius) {
1725  nassertv(radius <= PNM_MAXMAXVAL);
1726  PNMImage dist(mask.get_x_size(), mask.get_y_size(), 1, radius, NULL, CS_linear);
1727  dist.fill_val(radius);
1728 
1729  xelval threshold_val = mask.to_val(threshold);
1730 
1731  for (int yi = 0; yi < mask.get_y_size(); ++yi) {
1732  for (int xi = 0; xi < mask.get_x_size(); ++xi) {
1733  if (mask.get_gray_val(xi, yi) >= threshold_val) {
1734  dist.do_fill_distance(xi, yi, 0);
1735  }
1736  }
1737  }
1738 
1739  take_from(dist);
1740 }
1741 
1742 ////////////////////////////////////////////////////////////////////
1743 // Function: PNMImage::rescale
1744 // Access: Published
1745 // Description: Rescales the RGB channel values so that any values in
1746 // the original image between min_val and max_val are
1747 // expanded to the range 0 .. 1. Values below min_val
1748 // are set to 0, and values above max_val are set to 1.
1749 // Does not affect the alpha channel, if any.
1750 ////////////////////////////////////////////////////////////////////
1751 void PNMImage::
1752 rescale(float min_val, float max_val) {
1753  float scale = max_val - min_val;
1754 
1755  if (_num_channels <= 2) {
1756  // Grayscale.
1757  for (int y = 0; y < get_y_size(); y++) {
1758  for (int x = 0; x < get_x_size(); x++) {
1759  float val = get_gray(x, y);
1760  set_gray(x, y, (val - min_val) / scale);
1761  }
1762  }
1763  } else {
1764  // RGB(A).
1765  for (int y = 0; y < get_y_size(); y++) {
1766  for (int x = 0; x < get_x_size(); x++) {
1767  LRGBColorf xel = get_xel(x, y);
1768  set_xel(x, y,
1769  (xel[0] - min_val) / scale,
1770  (xel[1] - min_val) / scale,
1771  (xel[2] - min_val) / scale);
1772  }
1773  }
1774  }
1775 }
1776 
1777 ////////////////////////////////////////////////////////////////////
1778 // Function: PNMImage::copy_channel
1779 // Access: Published
1780 // Description: Copies just a single channel from the source image
1781 // into a single channel of this image, leaving the
1782 // remaining channels alone.
1783 ////////////////////////////////////////////////////////////////////
1784 void PNMImage::
1785 copy_channel(const PNMImage &copy, int xto, int yto, int cto,
1786  int xfrom, int yfrom, int cfrom,
1787  int x_size, int y_size) {
1788  int xmin, ymin, xmax, ymax;
1789  setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
1790  xmin, ymin, xmax, ymax);
1791 
1792  if (get_maxval() == copy.get_maxval() &&
1793  get_color_space() == copy.get_color_space()) {
1794  // The simple case: no pixel value rescaling is required.
1795  int x, y;
1796  for (y = ymin; y < ymax; y++) {
1797  if (cto == 3 && cfrom == 3) {
1798  // To alpha channel, from alpha channel.
1799  for (x = xmin; x < xmax; x++) {
1800  set_alpha_val(x, y, copy.get_alpha_val(x - xmin + xfrom, y - ymin + yfrom));
1801  }
1802  } else if (cto == 3 && cfrom == 0) {
1803  // To alpha channel, from blue (or gray) channel.
1804  for (x = xmin; x < xmax; x++) {
1805  set_alpha_val(x, y, copy.get_blue_val(x - xmin + xfrom, y - ymin + yfrom));
1806  }
1807  } else if (cto == 0 && cfrom == 3) {
1808  // To blue (or gray) channel, from alpha channel.
1809  for (x = xmin; x < xmax; x++) {
1810  set_blue_val(x, y, copy.get_alpha_val(x - xmin + xfrom, y - ymin + yfrom));
1811  }
1812  } else {
1813  // Generic channels.
1814  for (x = xmin; x < xmax; x++) {
1815  set_channel_val(x, y, cto, copy.get_channel_val(x - xmin + xfrom, y - ymin + yfrom, cfrom));
1816  }
1817  }
1818  }
1819 
1820  } else {
1821  // The harder case: rescale pixel values according to maxval.
1822  int x, y;
1823  for (y = ymin; y < ymax; y++) {
1824  for (x = xmin; x < xmax; x++) {
1825  set_channel(x, y, cto, copy.get_channel(x - xmin + xfrom, y - ymin + yfrom, cfrom));
1826  }
1827  }
1828  }
1829 }
1830 
1831 ////////////////////////////////////////////////////////////////////
1832 // Function: PNMImage::render_spot
1833 // Access: Published
1834 // Description: Renders a solid-color circle, with a fuzzy edge, into
1835 // the center of the PNMImage. If the PNMImage is
1836 // non-square, this actually renders an ellipse.
1837 //
1838 // The min_radius and max_radius are in the scale 0..1,
1839 // where 1.0 means the full width of the image. If
1840 // min_radius == max_radius, the edge is sharp (but
1841 // still antialiased); otherwise, the pixels between
1842 // min_radius and max_radius are smoothly blended
1843 // between fg and bg colors.
1844 ////////////////////////////////////////////////////////////////////
1845 void PNMImage::
1846 render_spot(const LColorf &fg, const LColorf &bg,
1847  float min_radius, float max_radius) {
1848  if (_x_size == 0 || _y_size == 0) {
1849  return;
1850  }
1851 
1852  float x_scale = 2.0 / _x_size;
1853  float y_scale = 2.0 / _y_size;
1854 
1855  // If the width is even, x_center1 == x_center0. If the width is
1856  // odd, x_center1 == x_center0 + 1.
1857  int x_center0 = _x_size / 2;
1858  int y_center0 = _y_size / 2;
1859  int x_center1 = (_x_size + 1) / 2;
1860  int y_center1 = (_y_size + 1) / 2;
1861 
1862  float min_r2 = min_radius * min_radius;
1863  float max_r2 = max_radius * max_radius;
1864 
1865  for (int yi = 0; yi < y_center1; ++yi) {
1866  float y = yi * y_scale;
1867  float y2_inner = y * y;
1868  float y2_outer = (y + y_scale) * (y + y_scale);
1869  for (int xi = 0; xi < x_center1; ++xi) {
1870  float x = xi * x_scale;
1871  float d2_inner = (x * x + y2_inner);
1872  float d2_outer = ((x + x_scale) * (x + x_scale) + y2_outer);
1873  float d2_a = ((x + x_scale) * (x + x_scale) + y2_inner);
1874  float d2_b = (x * x + y2_outer);
1875 
1876  if ((d2_inner <= min_r2) &&
1877  (d2_outer <= min_r2) &&
1878  (d2_a <= min_r2) &&
1879  (d2_b <= min_r2)) {
1880  // This pixel is solidly in the center of the spot.
1881  set_xel_a(x_center1 - 1 - xi, y_center1 - 1 - yi, fg);
1882  set_xel_a(x_center0 + xi, y_center1 - 1 - yi, fg);
1883  set_xel_a(x_center1 - 1 - xi, y_center0 + yi, fg);
1884  set_xel_a(x_center0 + xi, y_center0 + yi, fg);
1885 
1886  } else if ((d2_inner > max_r2) &&
1887  (d2_outer > max_r2) &&
1888  (d2_a > max_r2) &&
1889  (d2_b > max_r2)) {
1890  // This pixel is solidly outside the spot.
1891  set_xel_a(x_center1 - 1 - xi, y_center1 - 1 - yi, bg);
1892  set_xel_a(x_center0 + xi, y_center1 - 1 - yi, bg);
1893  set_xel_a(x_center1 - 1 - xi, y_center0 + yi, bg);
1894  set_xel_a(x_center0 + xi, y_center0 + yi, bg);
1895 
1896  } else {
1897  // This pixel is in a feathered area or along the antialiased edge.
1898  LColorf c_outer, c_inner, c_a, c_b;
1899  compute_spot_pixel(c_outer, d2_outer, min_radius, max_radius, fg, bg);
1900  compute_spot_pixel(c_inner, d2_inner, min_radius, max_radius, fg, bg);
1901  compute_spot_pixel(c_a, d2_a, min_radius, max_radius, fg, bg);
1902  compute_spot_pixel(c_b, d2_b, min_radius, max_radius, fg, bg);
1903 
1904  // Now average all four pixels for the antialiased result.
1905  LColorf c;
1906  c = (c_outer + c_inner + c_a + c_b) * 0.25;
1907 
1908  set_xel_a(x_center1 - 1 - xi, y_center1 - 1 - yi, c);
1909  set_xel_a(x_center0 + xi, y_center1 - 1 - yi, c);
1910  set_xel_a(x_center1 - 1 - xi, y_center0 + yi, c);
1911  set_xel_a(x_center0 + xi, y_center0 + yi, c);
1912  }
1913  }
1914  }
1915 }
1916 
1917 ////////////////////////////////////////////////////////////////////
1918 // Function: PNMImage::expand_border
1919 // Access: Published
1920 // Description: Expands the image by the indicated number of pixels
1921 // on each edge. The new pixels are set to the
1922 // indicated color.
1923 //
1924 // If any of the values is negative, this actually crops
1925 // the image.
1926 ////////////////////////////////////////////////////////////////////
1927 void PNMImage::
1928 expand_border(int left, int right, int bottom, int top,
1929  const LColorf &color) {
1930  PNMImage new_image(get_x_size() + left + right,
1931  get_y_size() + bottom + top,
1933  get_type(), get_color_space());
1934  new_image.fill(color[0], color[1], color[2]);
1935  if (has_alpha()) {
1936  new_image.alpha_fill(color[3]);
1937  }
1938  new_image.copy_sub_image(*this, left, top);
1939 
1940  take_from(new_image);
1941 }
1942 
1943 ////////////////////////////////////////////////////////////////////
1944 // Function: PNMImage::unfiltered_stretch_from
1945 // Access: Published
1946 // Description: Resizes from the indicated image into this one by
1947 // performing a nearest-point sample.
1948 ////////////////////////////////////////////////////////////////////
1949 void PNMImage::
1951  for (int yt = 0; yt < get_y_size(); yt++) {
1952  int ys = yt * copy.get_y_size() / get_y_size();
1953  for (int xt = 0; xt < get_x_size(); xt++) {
1954  int xs = xt * copy.get_x_size() / get_x_size();
1955  set_xel(xt, yt, copy.get_xel(xs, ys));
1956  }
1957  }
1958 
1959  if (has_alpha() && copy.has_alpha()) {
1960  for (int yt = 0; yt < get_y_size(); yt++) {
1961  int ys = yt * copy.get_y_size() / get_y_size();
1962  for (int xt = 0; xt < get_x_size(); xt++) {
1963  int xs = xt * copy.get_x_size() / get_x_size();
1964  set_alpha(xt, yt, copy.get_alpha(xs, ys));
1965  }
1966  }
1967  }
1968 }
1969 
1970 ////////////////////////////////////////////////////////////////////
1971 // Function: PNMImage::make_histogram
1972 // Access: Published
1973 // Description: Computes a histogram of the colors used in the
1974 // image.
1975 ////////////////////////////////////////////////////////////////////
1976 void PNMImage::
1978  HistMap hist_map;
1979  PixelCount pixels;
1980 
1981  int num_pixels = _x_size * _y_size;
1982 
1983  compute_histogram(hist_map, _array, _alpha);
1984 
1985  pixels.reserve(hist_map.size());
1986  HistMap::const_iterator hi;
1987  for (hi = hist_map.begin(); hi != hist_map.end(); ++hi) {
1988  if ((*hi).second <= num_pixels) {
1989  pixels.push_back(PixelSpecCount((*hi).first, (*hi).second));
1990  }
1991  }
1992  ::sort(pixels.begin(), pixels.end());
1993 
1994  histogram.swap(pixels, hist_map);
1995 }
1996 
1997 ////////////////////////////////////////////////////////////////////
1998 // Function: PNMImage::perlin_noise_fill
1999 // Access: Published
2000 // Description: Fills the image with a grayscale perlin noise
2001 // pattern based on the indicated parameters.
2002 // Uses set_xel to set the grayscale values.
2003 // The sx and sy parameters are in multiples
2004 // of the size of this image.
2005 // See also the PerlinNoise2 class in mathutil.
2006 ////////////////////////////////////////////////////////////////////
2007 void PNMImage::
2008 perlin_noise_fill(float sx, float sy, int table_size, unsigned long seed) {
2009  float x, y;
2010  float noise;
2011  PerlinNoise2 perlin (sx * _x_size, sy * _y_size, table_size, seed);
2012  for (x = 0; x < _x_size; ++x) {
2013  for (y = 0; y < _y_size; ++y) {
2014  noise = perlin.noise(x, y);
2015  set_xel(x, y, 0.5 * (noise + 1.0));
2016  }
2017  }
2018 }
2019 
2020 ////////////////////////////////////////////////////////////////////
2021 // Function: PNMImage::perlin_noise_fill
2022 // Access: Published
2023 // Description: Variant of perlin_noise_fill that uses an
2024 // existing StackedPerlinNoise2 object.
2025 ////////////////////////////////////////////////////////////////////
2026 void PNMImage::
2028  float x, y;
2029  float noise;
2030  for (x = 0; x < _x_size; ++x) {
2031  for (y = 0; y < _y_size; ++y) {
2032  noise = perlin.noise(x / (float) _x_size, y / (float) _y_size);
2033  set_xel(x, y, 0.5 * (noise + 1.0));
2034  }
2035  }
2036 }
2037 
2038 ////////////////////////////////////////////////////////////////////
2039 // Function: PNMImage::remix_channels
2040 // Access: Published
2041 // Description: Transforms every pixel using the operation
2042 // (Ro,Go,Bo) = conv.xform_point(Ri,Gi,Bi);
2043 // Input must be a color image.
2044 ////////////////////////////////////////////////////////////////////
2045 void PNMImage::
2046 remix_channels(const LMatrix4 &conv) {
2047  int nchannels = get_num_channels();
2048  nassertv((nchannels >= 3) && (nchannels <= 4));
2049  for (int y = 0; y < get_y_size(); y++) {
2050  for (int x = 0; x < get_x_size(); x++) {
2051  LVector3 inv(get_red(x,y), get_green(x,y), get_blue(x,y));
2052  LVector3 outv(conv.xform_point(inv));
2053  set_xel(x, y, outv[0], outv[1], outv[2]);
2054  }
2055  }
2056 }
2057 
2058 ////////////////////////////////////////////////////////////////////
2059 // Function: PNMImage::apply_exponent
2060 // Access: Published
2061 // Description: Adjusts each channel of the image by raising the
2062 // corresponding component value to the indicated
2063 // exponent, such that L' = L ^ exponent. For a
2064 // grayscale image, the blue_exponent value is used for
2065 // the grayscale value, and red_exponent and
2066 // green_exponent are unused.
2067 ////////////////////////////////////////////////////////////////////
2068 void PNMImage::
2069 apply_exponent(float red_exponent, float green_exponent, float blue_exponent,
2070  float alpha_exponent) {
2071  int num_channels = _num_channels;
2072  if (has_alpha() && alpha_exponent == 1.0f) {
2073  // If the alpha_exponent is 1, don't bother to apply it.
2074  --num_channels;
2075  }
2076 
2077  int x, y;
2078 
2079  if (red_exponent == 1.0f && green_exponent == 1.0f && blue_exponent == 1.0f) {
2080  // If the RGB components are all 1, apply only to the alpha channel.
2081  switch (num_channels) {
2082  case 1:
2083  case 3:
2084  break;
2085 
2086  case 2:
2087  case 4:
2088  for (y = 0; y < _y_size; ++y) {
2089  for (x = 0; x < _x_size; ++x) {
2090  float alpha = get_alpha(x, y);
2091  alpha = cpow(alpha, blue_exponent);
2092  set_alpha(x, y, alpha);
2093  }
2094  }
2095  break;
2096  }
2097 
2098  } else {
2099  // Apply to the color and/or alpha channels.
2100 
2101  switch (num_channels) {
2102  case 1:
2103  for (y = 0; y < _y_size; ++y) {
2104  for (x = 0; x < _x_size; ++x) {
2105  float gray = get_gray(x, y);
2106  gray = cpow(gray, blue_exponent);
2107  set_gray(x, y, gray);
2108  }
2109  }
2110  break;
2111 
2112  case 2:
2113  for (y = 0; y < _y_size; ++y) {
2114  for (x = 0; x < _x_size; ++x) {
2115  float gray = get_gray(x, y);
2116  gray = cpow(gray, blue_exponent);
2117  set_gray(x, y, gray);
2118 
2119  float alpha = get_alpha(x, y);
2120  alpha = cpow(alpha, blue_exponent);
2121  set_alpha(x, y, alpha);
2122  }
2123  }
2124  break;
2125 
2126  case 3:
2127  for (y = 0; y < _y_size; ++y) {
2128  for (x = 0; x < _x_size; ++x) {
2129  LRGBColorf color = get_xel(x, y);
2130  color[0] = cpow(color[0], red_exponent);
2131  color[1] = cpow(color[1], green_exponent);
2132  color[2] = cpow(color[2], blue_exponent);
2133  set_xel(x, y, color);
2134  }
2135  }
2136  break;
2137 
2138  case 4:
2139  for (y = 0; y < _y_size; ++y) {
2140  for (x = 0; x < _x_size; ++x) {
2141  LColorf color = get_xel_a(x, y);
2142  color[0] = cpow(color[0], red_exponent);
2143  color[1] = cpow(color[1], green_exponent);
2144  color[2] = cpow(color[2], blue_exponent);
2145  color[3] = cpow(color[3], alpha_exponent);
2146  set_xel_a(x, y, color);
2147  }
2148  }
2149  break;
2150  }
2151  }
2152 }
2153 
2154 ////////////////////////////////////////////////////////////////////
2155 // Function: PNMImage::setup_rc
2156 // Access: Private
2157 // Description: Sets the _default_rc,bc,gc values appropriately
2158 // according to the color type of the image, so that
2159 // get_bright() will return a meaningful value for both
2160 // color and grayscale images.
2161 ////////////////////////////////////////////////////////////////////
2162 void PNMImage::
2163 setup_rc() {
2164  if (is_grayscale()) {
2165  _default_rc = 0.0;
2166  _default_gc = 0.0;
2167  _default_bc = 1.0;
2168  } else {
2169  _default_rc = lumin_red;
2170  _default_gc = lumin_grn;
2171  _default_bc = lumin_blu;
2172  }
2173 }
2174 
2175 ////////////////////////////////////////////////////////////////////
2176 // Function: PNMImage::setup_encoding
2177 // Access: Private
2178 // Description: Sets the _xel_encoding value apppropriately
2179 // according to the color space, maxval and whether
2180 // the image has an alpha channel, so that to_val and
2181 // from_val will work correctly (and possibly more
2182 // efficiently).
2183 // Should be called after any call to set_maxval.
2184 ////////////////////////////////////////////////////////////////////
2185 void PNMImage::
2186 setup_encoding() {
2187  if (_maxval == 0) {
2188  _inv_maxval = 0.0f;
2189  } else {
2190  _inv_maxval = 1.0f / (float)_maxval;
2191  }
2192 
2193  if (has_alpha()) {
2194  switch (_color_space) {
2195  case CS_linear:
2196  _xel_encoding = XE_generic_alpha;
2197  break;
2198 
2199  case CS_sRGB:
2200  if (get_maxval() == 255) {
2201  if (has_sse2_sRGB_encode()) {
2202  _xel_encoding = XE_uchar_sRGB_alpha_sse2;
2203  } else {
2204  _xel_encoding = XE_uchar_sRGB_alpha;
2205  }
2206  } else {
2207  _xel_encoding = XE_generic_sRGB_alpha;
2208  }
2209  break;
2210 
2211  case CS_scRGB:
2212  _xel_encoding = XE_scRGB_alpha;
2213  nassertv(get_maxval() == 65535);
2214  break;
2215 
2216  default:
2217  nassertv(false);
2218  break;
2219  }
2220  } else {
2221  switch (_color_space) {
2222  case CS_linear:
2223  _xel_encoding = XE_generic;
2224  break;
2225 
2226  case CS_sRGB:
2227  if (get_maxval() == 255) {
2228  if (has_sse2_sRGB_encode()) {
2229  _xel_encoding = XE_uchar_sRGB_sse2;
2230  } else {
2231  _xel_encoding = XE_uchar_sRGB;
2232  }
2233  } else {
2234  _xel_encoding = XE_generic_sRGB;
2235  }
2236  break;
2237 
2238  case CS_scRGB:
2239  _xel_encoding = XE_scRGB;
2240  nassertv(get_maxval() == 65535);
2241  break;
2242 
2243  default:
2244  nassertv(false);
2245  break;
2246  }
2247  }
2248 }
2249 
2250 ////////////////////////////////////////////////////////////////////
2251 // Function: PNMImage::do_fill_distance
2252 // Access: Private
2253 // Description: Recursively fills in the minimum distance measured
2254 // from a certain set of points into the gray channel.
2255 ////////////////////////////////////////////////////////////////////
2256 void PNMImage::
2257 do_fill_distance(int xi, int yi, int d) {
2258  if (xi < 0 || xi >= get_x_size() ||
2259  yi < 0 || yi >= get_y_size()) {
2260  return;
2261  }
2262  if (get_gray_val(xi, yi) <= d) {
2263  return;
2264  }
2265  set_gray_val(xi, yi, d);
2266 
2267  do_fill_distance(xi + 1, yi, d + 1);
2268  do_fill_distance(xi - 1, yi, d + 1);
2269  do_fill_distance(xi, yi + 1, d + 1);
2270  do_fill_distance(xi, yi - 1, d + 1);
2271 }
2272 
2273 ////////////////////////////////////////////////////////////////////
2274 // Function: PNMImage::get_average_xel
2275 // Access: Published
2276 // Description: Returns the average color of all of the pixels
2277 // in the image.
2278 ////////////////////////////////////////////////////////////////////
2282  if (_x_size == 0 || _y_size == 0) {
2283  return color;
2284  }
2285 
2286  float factor = 1.0f / (float)(_x_size * _y_size);
2287 
2288  int x, y;
2289  for (x = 0; x < _x_size; ++x) {
2290  for (y = 0; y < _y_size; ++y) {
2291  color += get_xel(x, y) * factor;
2292  }
2293  }
2294 
2295  return color;
2296 }
2297 
2298 ////////////////////////////////////////////////////////////////////
2299 // Function: PNMImage::get_average_xel_a
2300 // Access: Published
2301 // Description: Returns the average color of all of the pixels
2302 // in the image, including the alpha channel.
2303 ////////////////////////////////////////////////////////////////////
2307  if (_x_size == 0 || _y_size == 0) {
2308  return color;
2309  }
2310 
2311  float factor = 1.0f / (float)(_x_size * _y_size);
2312 
2313  int x, y;
2314  for (x = 0; x < _x_size; ++x) {
2315  for (y = 0; y < _y_size; ++y) {
2316  color += get_xel_a(x, y) * factor;
2317  }
2318  }
2319 
2320  return color;
2321 }
2322 
2323 ////////////////////////////////////////////////////////////////////
2324 // Function: PNMImage::get_average_gray
2325 // Access: Published
2326 // Description: Returns the average grayscale component of all of
2327 // the pixels in the image.
2328 ////////////////////////////////////////////////////////////////////
2329 float PNMImage::
2331  float gray = 0.0;
2332  if (_x_size == 0 || _y_size == 0) {
2333  return gray;
2334  }
2335 
2336  int x, y;
2337  for (x = 0; x < _x_size; ++x) {
2338  for (y = 0; y < _y_size; ++y) {
2339  gray += get_gray(x, y);
2340  }
2341  }
2342 
2343  gray /= (float)(_x_size * _y_size);
2344  return gray;
2345 }
2346 
2347 ////////////////////////////////////////////////////////////////////
2348 // Function: PNMImage::operator ~
2349 // Access: Published
2350 // Description: Returns a new PNMImage that is the complement of
2351 // this PNMImage. This operation is not color-space
2352 // correct.
2353 ////////////////////////////////////////////////////////////////////
2355 operator ~ () const {
2356  PNMImage target (*this);
2357  size_t array_size = _x_size * _y_size;
2358 
2359  if (_array != NULL && _alpha != NULL) {
2360  for (size_t i = 0; i < array_size; ++i) {
2361  target._array[i].r = _maxval - _array[i].r;
2362  target._array[i].g = _maxval - _array[i].g;
2363  target._array[i].b = _maxval - _array[i].b;
2364  target._alpha[i] = _maxval - _alpha[i];
2365  }
2366  } else if (_array != NULL) {
2367  for (size_t i = 0; i < array_size; ++i) {
2368  target._array[i].r = _maxval - _array[i].r;
2369  target._array[i].g = _maxval - _array[i].g;
2370  target._array[i].b = _maxval - _array[i].b;
2371  }
2372  } else if (_alpha != NULL) {
2373  for (size_t i = 0; i < array_size; ++i) {
2374  target._alpha[i] = _maxval - _alpha[i];
2375  }
2376  }
2377  return target;
2378 }
2379 
2380 ////////////////////////////////////////////////////////////////////
2381 // Function: PNMImage::operator +=
2382 // Access: Published
2383 // Description: Sets each pixel value to the sum of the corresponding
2384 // pixel values in the two given images. Only valid
2385 // when both images have the same size.
2386 ////////////////////////////////////////////////////////////////////
2387 void PNMImage::
2388 operator += (const PNMImage &other) {
2389  nassertv(is_valid() && other.is_valid());
2390  nassertv(_x_size == other._x_size && _y_size == other._y_size);
2391 
2392  if (get_maxval() == other.get_maxval() &&
2393  get_color_space() == CS_linear &&
2394  other.get_color_space() == CS_linear) {
2395  size_t array_size = _x_size * _y_size;
2396 
2397  // Simple case: add vals directly.
2398  if (_alpha != NULL && other._alpha != NULL) {
2399  for (size_t i = 0; i < array_size; ++i) {
2400  _array[i].r = clamp_val((int)_array[i].r + (int)other._array[i].r);
2401  _array[i].g = clamp_val((int)_array[i].g + (int)other._array[i].g);
2402  _array[i].b = clamp_val((int)_array[i].b + (int)other._array[i].b);
2403  _alpha[i] = clamp_val((int)_alpha[i] + (int)other._alpha[i]);
2404  }
2405  } else {
2406  for (size_t i = 0; i < array_size; ++i) {
2407  _array[i].r = clamp_val((int)_array[i].r + (int)other._array[i].r);
2408  _array[i].g = clamp_val((int)_array[i].g + (int)other._array[i].g);
2409  _array[i].b = clamp_val((int)_array[i].b + (int)other._array[i].b);
2410  }
2411  }
2412  } else {
2413  // Not a linear color space: convert back and forth.
2414  int x, y;
2415  for (x = 0; x < _x_size; ++x) {
2416  for (y = 0; y < _y_size; ++y) {
2417  set_xel_a(x, y, get_xel_a(x, y) + other.get_xel_a(x, y));
2418  }
2419  }
2420  }
2421 }
2422 
2423 ////////////////////////////////////////////////////////////////////
2424 // Function: PNMImage::operator +=
2425 // Access: Published
2426 // Description: Adds the provided color to each pixel in this image.
2427 ////////////////////////////////////////////////////////////////////
2428 void PNMImage::
2429 operator += (const LColorf &other) {
2430  nassertv(is_valid());
2431 
2432  if (get_color_space() == CS_linear) {
2433  size_t array_size = _x_size * _y_size;
2434 
2435  // Note: don't use to_val here because it clamps values below 0
2436  int add_r = (int)(other.get_x() * get_maxval() + 0.5);
2437  int add_g = (int)(other.get_y() * get_maxval() + 0.5);
2438  int add_b = (int)(other.get_z() * get_maxval() + 0.5);
2439  int add_a = (int)(other.get_w() * get_maxval() + 0.5);
2440 
2441  if (_alpha != NULL) {
2442  for (size_t i = 0; i < array_size; ++i) {
2443  _array[i].r = clamp_val((int)_array[i].r + add_r);
2444  _array[i].g = clamp_val((int)_array[i].g + add_g);
2445  _array[i].b = clamp_val((int)_array[i].b + add_b);
2446  _alpha[i] = clamp_val((int)_alpha[i] + add_a);
2447  }
2448 
2449  } else {
2450  for (size_t i = 0; i < array_size; ++i) {
2451  _array[i].r = clamp_val((int)_array[i].r + add_r);
2452  _array[i].g = clamp_val((int)_array[i].g + add_g);
2453  _array[i].b = clamp_val((int)_array[i].b + add_b);
2454  }
2455  }
2456  } else {
2457  // Not a linear color space: convert back and forth.
2458  int x, y;
2459  for (x = 0; x < _x_size; ++x) {
2460  for (y = 0; y < _y_size; ++y) {
2461  set_xel_a(x, y, get_xel_a(x, y) + other);
2462  }
2463  }
2464  }
2465 }
2466 
2467 ////////////////////////////////////////////////////////////////////
2468 // Function: PNMImage::operator -=
2469 // Access: Published
2470 // Description: Subtracts each pixel from the right image from each
2471 // pixel value in this image. Only valid when both
2472 // images have the same size.
2473 ////////////////////////////////////////////////////////////////////
2474 void PNMImage::
2475 operator -= (const PNMImage &other) {
2476  nassertv(is_valid() && other.is_valid());
2477  nassertv(_x_size == other._x_size && _y_size == other._y_size);
2478 
2479  if (get_maxval() == other.get_maxval() &&
2480  get_color_space() == CS_linear &&
2481  other.get_color_space() == CS_linear) {
2482  size_t array_size = _x_size * _y_size;
2483 
2484  // Simple case: subtract vals directly.
2485  if (_alpha != NULL && other._alpha != NULL) {
2486  for (size_t i = 0; i < array_size; ++i) {
2487  _array[i].r = clamp_val((int)_array[i].r - (int)other._array[i].r);
2488  _array[i].g = clamp_val((int)_array[i].g - (int)other._array[i].g);
2489  _array[i].b = clamp_val((int)_array[i].b - (int)other._array[i].b);
2490  _alpha[i] = clamp_val((int)_alpha[i] - (int)other._alpha[i]);
2491  }
2492  } else {
2493  for (size_t i = 0; i < array_size; ++i) {
2494  _array[i].r = clamp_val((int)_array[i].r - (int)other._array[i].r);
2495  _array[i].g = clamp_val((int)_array[i].g - (int)other._array[i].g);
2496  _array[i].b = clamp_val((int)_array[i].b - (int)other._array[i].b);
2497  }
2498  }
2499  } else {
2500  // Not a linear color space: convert back and forth.
2501  int x, y;
2502  for (x = 0; x < _x_size; ++x) {
2503  for (y = 0; y < _y_size; ++y) {
2504  set_xel_a(x, y, get_xel_a(x, y) - other.get_xel_a(x, y));
2505  }
2506  }
2507  }
2508 }
2509 
2510 ////////////////////////////////////////////////////////////////////
2511 // Function: PNMImage::operator -=
2512 // Access: Published
2513 // Description: Subtracts the provided color from each pixel in this
2514 // image.
2515 ////////////////////////////////////////////////////////////////////
2516 void PNMImage::
2517 operator -= (const LColorf &other) {
2518  (*this) += -other;
2519 }
2520 
2521 ////////////////////////////////////////////////////////////////////
2522 // Function: PNMImage::operator *=
2523 // Access: Published
2524 // Description: Multiples each pixel in this image by each
2525 // pixel value from the right image. Note that the
2526 // floating-point values in the 0..1 range are
2527 // multiplied, not in the 0..maxval range.
2528 // Only valid when both images have the same size.
2529 ////////////////////////////////////////////////////////////////////
2530 void PNMImage::
2531 operator *= (const PNMImage &other) {
2532  nassertv(is_valid() && other.is_valid());
2533  nassertv(_x_size == other._x_size && _y_size == other._y_size);
2534 
2535  int x, y;
2536  for (x = 0; x < _x_size; ++x) {
2537  for (y = 0; y < _y_size; ++y) {
2538  set_xel_a(x, y, get_xel_a(x, y) - other.get_xel_a(x, y));
2539  }
2540  }
2541 }
2542 
2543 ////////////////////////////////////////////////////////////////////
2544 // Function: PNMImage::operator *=
2545 // Access: Published
2546 // Description: Multiplies every pixel value in the image by
2547 // a constant floating-point multiplier value. This
2548 // affects all channels.
2549 ////////////////////////////////////////////////////////////////////
2550 void PNMImage::
2551 operator *= (float multiplier) {
2552  nassertv(is_valid());
2553 
2554  if (get_color_space() == CS_linear) {
2555  size_t array_size = _x_size * _y_size;
2556 
2557  if (_alpha != NULL) {
2558  for (size_t i = 0; i < array_size; ++i) {
2559  _array[i].r = clamp_val((int)(_array[i].r * multiplier + 0.5f));
2560  _array[i].g = clamp_val((int)(_array[i].g * multiplier + 0.5f));
2561  _array[i].b = clamp_val((int)(_array[i].b * multiplier + 0.5f));
2562  _alpha[i] = clamp_val((int)(_alpha[i] * multiplier + 0.5f));
2563  }
2564 
2565  } else {
2566  for (size_t i = 0; i < array_size; ++i) {
2567  _array[i].r = clamp_val((int)(_array[i].r * multiplier + 0.5f));
2568  _array[i].g = clamp_val((int)(_array[i].g * multiplier + 0.5f));
2569  _array[i].b = clamp_val((int)(_array[i].b * multiplier + 0.5f));
2570  }
2571  }
2572  } else {
2573  // Not a linear color space: convert back and forth.
2574  int x, y;
2575  for (x = 0; x < _x_size; ++x) {
2576  for (y = 0; y < _y_size; ++y) {
2577  set_xel_a(x, y, get_xel_a(x, y) * multiplier);
2578  }
2579  }
2580  }
2581 }
2582 
2583 ////////////////////////////////////////////////////////////////////
2584 // Function: PNMImage::operator *=
2585 // Access: Published
2586 // Description: Multiplies the provided color to each pixel in this
2587 // image. This is a component-wise multiplication.
2588 ////////////////////////////////////////////////////////////////////
2589 void PNMImage::
2590 operator *= (const LColorf &other) {
2591  nassertv(is_valid());
2592 
2593  if (get_color_space() == CS_linear) {
2594  size_t array_size = _x_size * _y_size;
2595 
2596  if (_alpha != NULL) {
2597  for (size_t i = 0; i < array_size; ++i) {
2598  _array[i].r = clamp_val((int)(_array[i].r * other[0] + 0.5f));
2599  _array[i].g = clamp_val((int)(_array[i].g * other[1] + 0.5f));
2600  _array[i].b = clamp_val((int)(_array[i].b * other[2] + 0.5f));
2601  _alpha[i] = clamp_val((int)(_alpha[i] * other[3] + 0.5f));
2602  }
2603 
2604  } else {
2605  for (size_t i = 0; i < array_size; ++i) {
2606  _array[i].r = clamp_val((int)(_array[i].r * other[0] + 0.5f));
2607  _array[i].g = clamp_val((int)(_array[i].g * other[1] + 0.5f));
2608  _array[i].b = clamp_val((int)(_array[i].b * other[2] + 0.5f));
2609  }
2610  }
2611  } else {
2612  // Not a linear color space: convert back and forth.
2613  int x, y;
2614  for (x = 0; x < _x_size; ++x) {
2615  for (y = 0; y < _y_size; ++y) {
2616  LColorf color = get_xel_a(x, y);
2617  color.componentwise_mult(other);
2618  set_xel_a(x, y, color);
2619  }
2620  }
2621  }
2622 }
void set_gray_val(int x, int y, xelval gray)
Sets the gray component color at the indicated pixel.
Definition: pnmImage.I:568
void remix_channels(const LMatrix4 &conv)
Transforms every pixel using the operation (Ro,Go,Bo) = conv.xform_point(Ri,Gi,Bi); Input must be a c...
Definition: pnmImage.cxx:2046
void set_pixel(int x, int y, const PixelSpec &pixel)
Sets the (r, g, b, a) pixel value at the indicated pixel, using a PixelSpec object.
Definition: pnmImage.cxx:1015
double noise(double x, double y)
Returns the noise function of the three inputs.
void set_alpha_array(xelval *alpha)
Replaces the underlying PNMImage alpha array with the indicated pointer.
Definition: pnmImage.cxx:1100
void set_channel_val(int x, int y, int channel, xelval value)
Sets the nth component color at the indicated pixel.
Definition: pnmImage.cxx:885
void rescale(float min_val, float max_val)
Rescales the RGB channel values so that any values in the original image between min_val and max_val ...
Definition: pnmImage.cxx:1752
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
void set_green(int x, int y, float g)
Sets the green component color only at the indicated pixel.
Definition: pnmImage.I:965
void copy_header_from(const PNMImageHeader &header)
Initializes all the data in the header (x_size, y_size, num_channels, etc.) to the same values indica...
Definition: pnmWriter.I:103
float get_channel(int x, int y, int channel) const
Returns the nth component color at the indicated pixel.
Definition: pnmImage.cxx:924
xelval to_val(float input_value) const
A handy function to scale non-alpha values from [0..1] to [0..get_maxval()].
Definition: pnmImage.I:96
void copy_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1)
Copies a rectangular area of another image into a rectangular area of this image. ...
Definition: pnmImage.cxx:1119
xelval get_channel_val(int x, int y, int channel) const
Returns the nth component color at the indicated pixel.
Definition: pnmImage.cxx:850
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
void set_channel(int x, int y, int channel, float value)
Sets the nth component color at the indicated pixel.
Definition: pnmImage.cxx:959
void set_read_size(int x_size, int y_size)
Instructs the reader to attempt to scale the image to the indicated size while reading it...
Definition: pnmReader.I:41
ColorSpace get_color_space() const
Returns the color space in which the image is encoded.
Definition: pnmImage.I:296
void blend(int x, int y, const LRGBColorf &val, float alpha)
Smoothly blends the indicated pixel value in with whatever was already in the image, based on the given alpha value.
Definition: pnmImage.I:1060
virtual int read_data(xel *array, xelval *alpha)
Reads in an entire image all at once, storing it in the pre-allocated _x_size * _y_size array and alp...
Definition: pnmReader.cxx:111
void blend_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), except the alpha channel of the copy is used to blend the copy into th...
Definition: pnmImage.cxx:1178
xelval get_gray_val(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:490
void alpha_fill(float alpha=0.0)
Sets the entire alpha channel to the given level.
Definition: pnmImage.I:218
void set_green_val(int x, int y, xelval g)
Sets the green component color only at the indicated pixel.
Definition: pnmImage.I:533
void apply_exponent(float gray_exponent)
Adjusts each channel of the image by raising the corresponding component value to the indicated expon...
Definition: pnmImage.I:1122
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:928
virtual bool write_pfm(const PfmFile &pfm)
Writes floating-point data from the indicated PfmFile.
Definition: pnmWriter.cxx:64
bool read(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:245
virtual bool is_floating_point()
Returns true if this PNMFileType represents a floating-point image type, false if it is a normal...
Definition: pnmReader.cxx:82
void set_xel_val(int x, int y, const xel &value)
Changes the RGB color at the indicated pixel.
Definition: pnmImage.I:406
float get_blue(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:913
void perlin_noise_fill(float sx, float sy, int table_size=256, unsigned long seed=0)
Fills the image with a grayscale perlin noise pattern based on the indicated parameters.
Definition: pnmImage.cxx:2008
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:35
virtual bool supports_grayscale() const
Returns true if this particular PNMWriter understands grayscale images.
Definition: pnmWriter.cxx:137
xelval clamp_val(int input_value) const
A handy function to clamp values to [0..get_maxval()].
Definition: pnmImage.I:84
void add_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), except the copy pixels are added to the pixels of the destination...
Definition: pnmImage.cxx:1218
void set_color_type(ColorType color_type)
Translates the image to or from grayscale, color, or four-color mode.
Definition: pnmImage.cxx:463
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
void set_red(int x, int y, float r)
Sets the red component color only at the indicated pixel.
Definition: pnmImage.I:953
static const LVecBase3f & zero()
Returns a zero-length vector.
Definition: lvecBase3.h:382
void flip(bool flip_x, bool flip_y, bool transpose)
Reverses, transposes, and/or rotates the image in-place according to the specified parameters...
Definition: pnmImage.cxx:722
LRGBColorf get_average_xel() const
Returns the average color of all of the pixels in the image.
Definition: pnmImage.cxx:2280
void set_color_space(ColorSpace color_space)
Converts the colors in the image to the indicated color space.
Definition: pnmImage.cxx:524
void make_histogram(Histogram &hist)
Computes a histogram of the colors used in the image.
Definition: pnmImage.cxx:1977
void fill_distance_outside(const PNMImage &mask, float threshold, int radius)
Replaces this image with a grayscale image whose gray channel represents the linear Manhattan distanc...
Definition: pnmImage.cxx:1724
int get_y_size() const
Returns the number of pixels in the Y direction.
void quick_filter_from(const PNMImage &copy, int xborder=0, int yborder=0)
Resizes from the given image, with a fixed radius of 0.5.
int get_x_size() const
Returns the number of pixels in the X direction.
virtual bool read_pfm(PfmFile &pfm)
Reads floating-point data directly into the indicated PfmFile.
Definition: pnmReader.cxx:93
float get_red(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:889
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width...
Definition: pnmImage.I:309
xel & get_xel_val(int x, int y)
Returns the RGB color at the indicated pixel.
Definition: pnmImage.I:379
This class provides an implementation of Perlin noise for 2 variables.
Definition: perlinNoise2.h:28
void copy_channel(const PNMImage &copy, int src_channel, int dest_channel)
Copies a channel from one image into another.
Definition: pnmImage.cxx:130
void swap(PixelCount &pixels, HistMap &hist_map)
Swaps the data in the Histogram with the indicated data.
Implements a multi-layer PerlinNoise, with one or more high-frequency noise functions added to a lowe...
bool is_valid() const
Returns true if the PNMReader can be used to read data, false if something is wrong.
Definition: pnmReader.I:65
LRGBColorf get_xel(int x, int y) const
Returns the RGB color at the indicated pixel.
Definition: pnmImage.I:597
void expand_border(int left, int right, int bottom, int top, const LColorf &color)
Expands the image by the indicated number of pixels on each edge.
Definition: pnmImage.cxx:1928
LColorf get_xel_a(int x, int y) const
Returns the RGBA color at the indicated pixel.
Definition: pnmImage.I:720
PixelSpec get_pixel(int x, int y) const
Returns the (r, g, b, a) pixel value at the indicated pixel, using a PixelSpec object.
Definition: pnmImage.cxx:993
void operator-=(const PNMImage &other)
Subtracts each pixel from the right image from each pixel value in this image.
Definition: pnmImage.cxx:2475
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
float get_green(int x, int y) const
Returns the green component color at the indicated pixel.
Definition: pnmImage.I:901
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component, or with a special extension, 2- or 4-component.
Definition: pfmFile.h:34
void lighten_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), but the resulting color will be the lighter of the source and destinat...
Definition: pnmImage.cxx:1370
LVecBase3f xform_point(const LVecBase3f &v) const
The matrix transforms a 3-component point (including translation component) and returns the result...
Definition: lmatrix.h:1667
static bool has_alpha(ColorType color_type)
This static variant of has_alpha() returns true if the indicated image type includes an alpha channel...
void set_blue_val(int x, int y, xelval b)
Sets the blue component color only at the indicated pixel.
Definition: pnmImage.I:548
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
xelval get_maxval() const
Returns the maximum channel value allowable for any pixel in this image; for instance, 255 for a typical 8-bit-per-channel image.
void do_fill_distance(int xi, int yi, int d)
Recursively fills in the minimum distance measured from a certain set of points into the gray channel...
Definition: pnmImage.cxx:2257
void set_gray(int x, int y, float gray)
Sets the gray component color at the indicated pixel.
Definition: pnmImage.I:994
void copy_header_from(const PNMImageHeader &header)
Copies just the header information into this image.
Definition: pnmImage.cxx:164
xelval get_blue_val(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:474
void set_blue(int x, int y, float b)
Sets the blue component color only at the indicated pixel.
Definition: pnmImage.I:977
void fill_distance_inside(const PNMImage &mask, float threshold, int radius, bool shrink_from_border)
Replaces this image with a grayscale image whose gray channel represents the linear Manhattan distanc...
Definition: pnmImage.cxx:1674
PNMFileType * get_type() const
If the file type is known (e.g.
void set_xel_a(int x, int y, const LColorf &value)
Changes the RGBA color at the indicated pixel.
Definition: pnmImage.I:789
virtual void prepare_read()
This method will be called before read_data() or read_row() is called.
Definition: pnmReader.cxx:52
void threshold(const PNMImage &select_image, int channel, float threshold, const PNMImage &lt, const PNMImage &ge)
Selectively copies each pixel from either one source or another source, depending on the pixel value ...
Definition: pnmImage.cxx:1451
bool write(const Filename &filename, PNMFileType *type=NULL) const
Writes the image to the indicated filename.
Definition: pnmImage.cxx:362
void darken_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), but the resulting color will be the darker of the source and destinati...
Definition: pnmImage.cxx:1299
void alpha_fill_val(xelval alpha=0)
Sets the entire alpha channel to the given level.
Definition: pnmImage.cxx:223
void mult_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), except the copy pixels are multiplied to the pixels of the destination...
Definition: pnmImage.cxx:1256
PNMImage operator~() const
Returns a new PNMImage that is the complement of this PNMImage.
Definition: pnmImage.cxx:2355
This is an abstract base class that defines the interface for reading image files of various types...
Definition: pnmReader.h:31
This is an abstract base class that defines the interface for writing image files of various types...
Definition: pnmWriter.h:31
void operator+=(const PNMImage &other)
Sets each pixel value to the sum of the corresponding pixel values in the two given images...
Definition: pnmImage.cxx:2388
void set_array(xel *array)
Replaces the underlying PNMImage array with the indicated pointer.
Definition: pnmImage.cxx:1080
void unfiltered_stretch_from(const PNMImage &copy)
Resizes from the indicated image into this one by performing a nearest-point sample.
Definition: pnmImage.cxx:1950
bool load(const PNMImage &pnmimage)
Fills the PfmFile with the data from the indicated PNMImage, converted to floating-point values...
Definition: pfmFile.cxx:312
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
virtual int write_data(xel *array, xelval *alpha)
Writes out an entire image all at once, including the header, based on the image data stored in the g...
Definition: pnmWriter.cxx:91
double noise(double x, double y) const
Returns the noise function of the three inputs.
Definition: perlinNoise2.I:122
void add_alpha()
Adds an alpha channel to the image, if it does not already have one.
Definition: pnmImage.I:336
void set_red_val(int x, int y, xelval r)
Sets the red component color only at the indicated pixel.
Definition: pnmImage.I:518
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color, type, etc).
Definition: pnmImage.cxx:50
void set_alpha(int x, int y, float a)
Sets the alpha component color only at the indicated pixel.
Definition: pnmImage.I:1007
PNMWriter * make_writer(const Filename &filename, PNMFileType *type=NULL) const
Returns a newly-allocated PNMWriter of the suitable type for writing an image to the indicated filena...
void take_from(PNMImage &orig)
Move the contents of the other image into this one, and empty the other image.
Definition: pnmImage.cxx:184
bool has_alpha() const
Returns true if the image includes an alpha channel, false otherwise.
int get_num_channels() const
Returns the number of channels in the image.
ColorType get_color_type() const
Returns the image type of the image, as an enumerated value.
void render_spot(const LColorf &fg, const LColorf &bg, float min_radius, float max_radius)
Renders a solid-color circle, with a fuzzy edge, into the center of the PNMImage. ...
Definition: pnmImage.cxx:1846
This is the base class of PNMImage, PNMReader, and PNMWriter.
bool has_read_size() const
Returns true if set_read_size() has been called.
Definition: pnmImage.I:261
float get_average_gray() const
Returns the average grayscale component of all of the pixels in the image.
Definition: pnmImage.cxx:2330
void copy_from(const PNMImage &copy)
Makes this image become a copy of the other image.
Definition: pnmImage.cxx:111
void set_alpha_val(int x, int y, xelval a)
Sets the alpha component color only at the indicated pixel.
Definition: pnmImage.I:585
void set_maxval(xelval maxval)
Rescales the image to the indicated maxval.
Definition: pnmImage.cxx:803
xelval get_alpha_val(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:503
void unpremultiply_alpha()
Converts an image in-place to its "straight alpha" form (presumably from a "premultiplied" form)...
Definition: pnmImage.cxx:664
void reverse_rows()
Performs an in-place reversal of the row (y) data.
Definition: pnmImage.cxx:688
void fill(float red, float green, float blue)
Sets the entire image (except the alpha channel) to the given color.
Definition: pnmImage.I:186
bool store(PNMImage &pnmimage) const
Copies the data to the indicated PNMImage, converting to RGB values.
Definition: pfmFile.cxx:387
void make_grayscale()
Converts the image from RGB to grayscale.
Definition: pnmImage.I:357
virtual bool supports_integer()
Returns true if this PNMFileType can accept an integer image type, false if it can only accept a floa...
Definition: pnmWriter.cxx:53
float get_bright(int x, int y) const
Returns the linear brightness of the given xel, as a linearized float in the range 0...
Definition: pnmImage.I:1020
bool is_grayscale() const
Returns false if the image is a full-color image, and has red, green, and blue components; true if it...
void set_xel(int x, int y, const LRGBColorf &value)
Changes the RGB color at the indicated pixel.
Definition: pnmImage.I:641
void operator*=(const PNMImage &other)
Multiples each pixel in this image by each pixel value from the right image.
Definition: pnmImage.cxx:2531
xelval get_green_val(int x, int y) const
Returns the green component color at the indicated pixel.
Definition: pnmImage.I:462
xelval get_red_val(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:450
void fill_val(xelval red, xelval green, xelval blue)
Sets the entire image (except the alpha channel) to the given color.
Definition: pnmImage.cxx:207
static const LVecBase4f & zero()
Returns a zero-length vector.
Definition: lvecBase4.h:493
float get_alpha(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:941
PNMReader * make_reader(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true) const
Returns a newly-allocated PNMReader of the suitable type for reading from the indicated image filenam...
void premultiply_alpha()
Converts an image in-place to its "premultiplied" form, where, for every pixel in the image...
Definition: pnmImage.cxx:636
LColorf get_average_xel_a() const
Returns the average color of all of the pixels in the image, including the alpha channel.
Definition: pnmImage.cxx:2305