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