00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "paletteImage.h"
00020 #include "palettePage.h"
00021 #include "paletteGroup.h"
00022 #include "texturePlacement.h"
00023 #include "palettizer.h"
00024 #include "textureImage.h"
00025 #include "sourceTextureImage.h"
00026 #include "filenameUnifier.h"
00027
00028 #include <indent.h>
00029 #include <datagram.h>
00030 #include <datagramIterator.h>
00031 #include <bamReader.h>
00032 #include <bamWriter.h>
00033
00034 #include <algorithm>
00035
00036 TypeHandle PaletteImage::_type_handle;
00037
00038
00039
00040
00041
00042
00043
00044 PaletteImage::ClearedRegion::
00045 ClearedRegion() {
00046 _x = 0;
00047 _y = 0;
00048 _x_size = 0;
00049 _y_size = 0;
00050 }
00051
00052
00053
00054
00055
00056
00057 PaletteImage::ClearedRegion::
00058 ClearedRegion(TexturePlacement *placement) {
00059 _x = placement->get_placed_x();
00060 _y = placement->get_placed_y();
00061 _x_size = placement->get_placed_x_size();
00062 _y_size = placement->get_placed_y_size();
00063 }
00064
00065
00066
00067
00068
00069
00070 PaletteImage::ClearedRegion::
00071 ClearedRegion(const PaletteImage::ClearedRegion ©) :
00072 _x(copy._x),
00073 _y(copy._y),
00074 _x_size(copy._x_size),
00075 _y_size(copy._y_size)
00076 {
00077 }
00078
00079
00080
00081
00082
00083
00084 void PaletteImage::ClearedRegion::
00085 operator = (const PaletteImage::ClearedRegion ©) {
00086 _x = copy._x;
00087 _y = copy._y;
00088 _x_size = copy._x_size;
00089 _y_size = copy._y_size;
00090 }
00091
00092
00093
00094
00095
00096
00097 void PaletteImage::ClearedRegion::
00098 clear(PNMImage &image) {
00099 for (int y = _y; y < _y + _y_size; y++) {
00100 for (int x = _x; x < _x + _x_size; x++) {
00101 image.set_xel_val(x, y, 0);
00102 }
00103 }
00104 if (image.has_alpha()) {
00105 for (int y = _y; y < _y + _y_size; y++) {
00106 for (int x = _x; x < _x + _x_size; x++) {
00107 image.set_alpha_val(x, y, 0);
00108 }
00109 }
00110 }
00111 }
00112
00113
00114
00115
00116
00117
00118
00119 void PaletteImage::ClearedRegion::
00120 write_datagram(Datagram &datagram) const {
00121 datagram.add_int32(_x);
00122 datagram.add_int32(_y);
00123 datagram.add_int32(_x_size);
00124 datagram.add_int32(_y_size);
00125 }
00126
00127
00128
00129
00130
00131
00132
00133 void PaletteImage::ClearedRegion::
00134 fillin(DatagramIterator &scan) {
00135 _x = scan.get_int32();
00136 _y = scan.get_int32();
00137 _x_size = scan.get_int32();
00138 _y_size = scan.get_int32();
00139 }
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152 PaletteImage::
00153 PaletteImage() {
00154 _page = (PalettePage *)NULL;
00155 _index = 0;
00156 _new_image = false;
00157 _got_image = false;
00158 }
00159
00160
00161
00162
00163
00164
00165 PaletteImage::
00166 PaletteImage(PalettePage *page, int index) :
00167 _page(page),
00168 _index(index)
00169 {
00170 _properties = page->get_properties();
00171 _size_known = true;
00172 _x_size = pal->_pal_x_size;
00173 _y_size = pal->_pal_y_size;
00174 _new_image = true;
00175 _got_image = false;
00176
00177 ostringstream name;
00178
00179
00180
00181
00182 name << page->get_group()->get_name() << "_palette_"
00183 << page->get_name() << "_" << index + 1 << ".";
00184
00185 _basename = name.str();
00186
00187 set_filename(page->get_group(), _basename);
00188 _shadow_image.make_shadow_image(_basename);
00189 }
00190
00191
00192
00193
00194
00195
00196
00197 PalettePage *PaletteImage::
00198 get_page() const {
00199 return _page;
00200 }
00201
00202
00203
00204
00205
00206
00207
00208
00209 bool PaletteImage::
00210 is_empty() const {
00211 if (_placements.empty()) {
00212
00213 return true;
00214
00215 } else if (_placements.size() == 1) {
00216
00217
00218 return (_placements[0]->get_omit_reason() == OR_solitary);
00219
00220 } else {
00221
00222
00223 return false;
00224 }
00225 }
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235 double PaletteImage::
00236 count_utilization() const {
00237 int used_pixels = 0;
00238
00239 Placements::const_iterator pi;
00240 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00241 TexturePlacement *placement = (*pi);
00242
00243 int texture_pixels =
00244 placement->get_placed_x_size() *
00245 placement->get_placed_y_size();
00246 used_pixels += texture_pixels;
00247 }
00248
00249 int total_pixels = get_x_size() * get_y_size();
00250
00251 return (double)used_pixels / (double)total_pixels;
00252 }
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266 double PaletteImage::
00267 count_coverage() const {
00268 int coverage_pixels = 0;
00269
00270 Placements::const_iterator pi;
00271 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00272 TexturePlacement *placement = (*pi);
00273 TextureImage *texture = placement->get_texture();
00274 nassertr(texture != (TextureImage *)NULL, 0.0);
00275
00276 int orig_pixels =
00277 texture->get_x_size() *
00278 texture->get_y_size();
00279 int placed_pixels =
00280 placement->get_placed_x_size() *
00281 placement->get_placed_y_size();
00282
00283 coverage_pixels += placed_pixels - orig_pixels;
00284 }
00285
00286 int total_pixels = get_x_size() * get_y_size();
00287
00288 return (double)coverage_pixels / (double)total_pixels;
00289 }
00290
00291
00292
00293
00294
00295
00296
00297
00298 bool PaletteImage::
00299 place(TexturePlacement *placement) {
00300 nassertr(placement->is_size_known(), true);
00301 nassertr(!placement->is_placed(), true);
00302
00303 int x, y;
00304 if (find_hole(x, y, placement->get_x_size(), placement->get_y_size())) {
00305 placement->place_at(this, x, y);
00306 _placements.push_back(placement);
00307 return true;
00308 }
00309
00310 return false;
00311 }
00312
00313
00314
00315
00316
00317
00318 void PaletteImage::
00319 unplace(TexturePlacement *placement) {
00320 nassertv(placement->is_placed() && placement->get_image() == this);
00321
00322 Placements::iterator pi;
00323 pi = find(_placements.begin(), _placements.end(), placement);
00324 while (pi != _placements.end()) {
00325 _placements.erase(pi);
00326 pi = find(_placements.begin(), _placements.end(), placement);
00327 }
00328
00329 _cleared_regions.push_back(ClearedRegion(placement));
00330 }
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344 void PaletteImage::
00345 check_solitary() {
00346 if (_placements.size() == 1) {
00347
00348 TexturePlacement *placement = *_placements.begin();
00349 nassertv(placement->get_omit_reason() == OR_none ||
00350 placement->get_omit_reason() == OR_solitary);
00351
00352 if (pal->_omit_solitary || placement->get_omit_reason() == OR_solitary) {
00353
00354
00355
00356
00357 placement->omit_solitary();
00358 }
00359
00360 } else {
00361
00362 Placements::const_iterator pi;
00363 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00364 TexturePlacement *placement = (*pi);
00365
00366
00367
00368
00369
00370
00371
00372 nassertv(placement->get_omit_reason() == OR_none ||
00373 placement->get_omit_reason() == OR_solitary);
00374 placement->not_solitary();
00375 }
00376 }
00377 }
00378
00379
00380
00381
00382
00383
00384
00385 void PaletteImage::
00386 optimal_resize() {
00387 if (is_empty()) {
00388 return;
00389 }
00390
00391 bool resized_any = false;
00392 bool success;
00393 do {
00394 success = false;
00395 nassertv(_x_size > 0 && _y_size > 0);
00396
00397
00398 if (resize_image(_x_size, _y_size / 2)) {
00399 success = true;
00400 resized_any = true;
00401 }
00402 if (resize_image(_x_size / 2, _y_size)) {
00403 success = true;
00404 resized_any = true;
00405 }
00406 } while (success);
00407
00408 if (resized_any) {
00409 nout << "Resizing "
00410 << FilenameUnifier::make_user_filename(get_filename()) << " to "
00411 << _x_size << " " << _y_size << "\n";
00412 }
00413 }
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424 bool PaletteImage::
00425 resize_image(int x_size, int y_size) {
00426
00427
00428 _cleared_regions.clear();
00429 remove_image();
00430
00431
00432
00433 Placements saved;
00434 saved.swap(_placements);
00435
00436
00437 int saved_x_size = _x_size;
00438 int saved_y_size = _y_size;
00439
00440
00441
00442 sort(saved.begin(), saved.end(), SortPlacementBySize());
00443
00444
00445
00446 Placements::iterator pi;
00447 for (pi = saved.begin(); pi != saved.end(); ++pi) {
00448 (*pi)->force_replace();
00449 }
00450
00451
00452 _x_size = x_size;
00453 _y_size = y_size;
00454
00455 bool packed = true;
00456 for (pi = saved.begin(); pi != saved.end() && packed; ++pi) {
00457 if (!place(*pi)) {
00458 packed = false;
00459 }
00460 }
00461
00462 if (!packed) {
00463
00464 _x_size = saved_x_size;
00465 _y_size = saved_y_size;
00466
00467 Placements remove;
00468 remove.swap(_placements);
00469 for (pi = remove.begin(); pi != remove.end(); ++pi) {
00470 (*pi)->force_replace();
00471 }
00472
00473 bool all_packed = true;
00474 for (pi = saved.begin(); pi != saved.end(); ++pi) {
00475 if (!place(*pi)) {
00476 all_packed = false;
00477 }
00478 }
00479 nassertr(all_packed, false);
00480 }
00481
00482 return packed;
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492 void PaletteImage::
00493 write_placements(ostream &out, int indent_level) const {
00494 Placements::const_iterator pi;
00495 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00496 TexturePlacement *placement = (*pi);
00497 placement->write_placed(out, indent_level);
00498 }
00499 }
00500
00501
00502
00503
00504
00505
00506
00507 void PaletteImage::
00508 reset_image() {
00509
00510 Placements copy_placements = _placements;
00511 Placements::const_iterator pi;
00512 for (pi = copy_placements.begin(); pi != copy_placements.end(); ++pi) {
00513 TexturePlacement *placement = (*pi);
00514 placement->force_replace();
00515 }
00516
00517 _placements.clear();
00518 _cleared_regions.clear();
00519 remove_image();
00520 }
00521
00522
00523
00524
00525
00526
00527
00528
00529 void PaletteImage::
00530 setup_shadow_image() {
00531 _shadow_image.make_shadow_image(_basename);
00532 }
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542 void PaletteImage::
00543 update_image(bool redo_all) {
00544 if (is_empty() && pal->_aggressively_clean_mapdir) {
00545
00546
00547 remove_image();
00548 return;
00549 }
00550
00551 if (redo_all) {
00552
00553 remove_image();
00554 }
00555
00556
00557 bool needs_update =
00558 _new_image || !exists() ||
00559 !_cleared_regions.empty();
00560
00561 Placements::iterator pi;
00562
00563
00564
00565 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00566 TexturePlacement *placement = (*pi);
00567
00568 if (!placement->is_filled()) {
00569 needs_update = true;
00570
00571 } else {
00572 SourceTextureImage *source =
00573 placement->get_texture()->get_preferred_source();
00574
00575 if (source != (SourceTextureImage *)NULL &&
00576 source->get_filename().compare_timestamps(get_filename()) > 0) {
00577
00578
00579 placement->mark_unfilled();
00580 needs_update = true;
00581 }
00582 }
00583 }
00584
00585 if (!needs_update) {
00586
00587 return;
00588 }
00589
00590 get_image();
00591
00592
00593 ClearedRegions::iterator ci;
00594 for (ci = _cleared_regions.begin(); ci != _cleared_regions.end(); ++ci) {
00595 ClearedRegion ®ion = (*ci);
00596 region.clear(_image);
00597 }
00598 _cleared_regions.clear();
00599
00600
00601 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00602 TexturePlacement *placement = (*pi);
00603 if (!placement->is_filled()) {
00604 placement->fill_image(_image);
00605 }
00606 }
00607
00608 write(_image);
00609
00610 if (pal->_shadow_color_type != (PNMFileType *)NULL) {
00611 _shadow_image.write(_image);
00612 }
00613 }
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624 bool PaletteImage::
00625 find_hole(int &x, int &y, int x_size, int y_size) const {
00626 y = 0;
00627 while (y + y_size <= _y_size) {
00628 int next_y = _y_size;
00629
00630 x = 0;
00631 while (x + x_size <= _x_size) {
00632 int next_x = x;
00633
00634
00635 TexturePlacement *overlap = find_overlap(x, y, x_size, y_size);
00636
00637 if (overlap == (TexturePlacement *)NULL) {
00638
00639 return true;
00640 }
00641
00642 next_x = overlap->get_placed_x() + overlap->get_placed_x_size();
00643 next_y = min(next_y, overlap->get_placed_y() + overlap->get_placed_y_size());
00644 nassertr(next_x > x, false);
00645 x = next_x;
00646 }
00647
00648 nassertr(next_y > y, false);
00649 y = next_y;
00650 }
00651
00652
00653 return false;
00654 }
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667 TexturePlacement *PaletteImage::
00668 find_overlap(int x, int y, int x_size, int y_size) const {
00669 Placements::const_iterator pi;
00670 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00671 TexturePlacement *placement = (*pi);
00672 if (placement->is_placed() &&
00673 placement->intersects(x, y, x_size, y_size)) {
00674 return placement;
00675 }
00676 }
00677
00678 return (TexturePlacement *)NULL;
00679 }
00680
00681
00682
00683
00684
00685
00686
00687 void PaletteImage::
00688 get_image() {
00689 if (_got_image) {
00690 return;
00691 }
00692
00693 if (!_new_image) {
00694 if (pal->_shadow_color_type != (PNMFileType *)NULL) {
00695 if (_shadow_image.read(_image)) {
00696 _got_image = true;
00697 return;
00698 }
00699 } else {
00700 if (read(_image)) {
00701 _got_image = true;
00702 return;
00703 }
00704 }
00705 }
00706
00707 nout << "Generating new "
00708 << FilenameUnifier::make_user_filename(get_filename()) << "\n";
00709
00710
00711 _cleared_regions.clear();
00712
00713 _image.clear(get_x_size(), get_y_size(), _properties.get_num_channels());
00714 _new_image = false;
00715 _got_image = true;
00716
00717
00718 Placements::iterator pi;
00719 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00720 TexturePlacement *placement = (*pi);
00721 placement->fill_image(_image);
00722 }
00723 }
00724
00725
00726
00727
00728
00729
00730 void PaletteImage::
00731 remove_image() {
00732 unlink();
00733 if (pal->_shadow_color_type != (PNMFileType *)NULL) {
00734 _shadow_image.unlink();
00735 }
00736 _new_image = true;
00737 }
00738
00739
00740
00741
00742
00743
00744
00745 void PaletteImage::
00746 register_with_read_factory() {
00747 BamReader::get_factory()->
00748 register_factory(get_class_type(), make_PaletteImage);
00749 }
00750
00751
00752
00753
00754
00755
00756
00757
00758 void PaletteImage::
00759 write_datagram(BamWriter *writer, Datagram &datagram) {
00760 ImageFile::write_datagram(writer, datagram);
00761
00762 datagram.add_uint32(_cleared_regions.size());
00763 ClearedRegions::const_iterator ci;
00764 for (ci = _cleared_regions.begin(); ci != _cleared_regions.end(); ++ci) {
00765 (*ci).write_datagram(datagram);
00766 }
00767
00768 datagram.add_uint32(_placements.size());
00769 Placements::const_iterator pi;
00770 for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00771 writer->write_pointer(datagram, (*pi));
00772 }
00773
00774 writer->write_pointer(datagram, _page);
00775 datagram.add_uint32(_index);
00776 datagram.add_string(_basename);
00777 datagram.add_bool(_new_image);
00778
00779
00780
00781
00782
00783
00784
00785
00786 }
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797 int PaletteImage::
00798 complete_pointers(TypedWritable **p_list, BamReader *manager) {
00799 int index = ImageFile::complete_pointers(p_list, manager);
00800
00801 int i;
00802 _placements.reserve(_num_placements);
00803 for (i = 0; i < _num_placements; i++) {
00804 TexturePlacement *placement;
00805 DCAST_INTO_R(placement, p_list[index], index);
00806 _placements.push_back(placement);
00807 index++;
00808 }
00809
00810 if (p_list[index] != (TypedWritable *)NULL) {
00811 DCAST_INTO_R(_page, p_list[index], index);
00812 }
00813 index++;
00814
00815 return index;
00816 }
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826 TypedWritable *PaletteImage::
00827 make_PaletteImage(const FactoryParams ¶ms) {
00828 PaletteImage *me = new PaletteImage;
00829 DatagramIterator scan;
00830 BamReader *manager;
00831
00832 parse_params(params, scan, manager);
00833 me->fillin(scan, manager);
00834 return me;
00835 }
00836
00837
00838
00839
00840
00841
00842
00843
00844 void PaletteImage::
00845 fillin(DatagramIterator &scan, BamReader *manager) {
00846 ImageFile::fillin(scan, manager);
00847
00848 int num_cleared_regions = scan.get_uint32();
00849 _cleared_regions.reserve(num_cleared_regions);
00850 for (int i = 0; i < num_cleared_regions; i++) {
00851 _cleared_regions.push_back(ClearedRegion());
00852 _cleared_regions.back().fillin(scan);
00853 }
00854
00855 _num_placements = scan.get_uint32();
00856 manager->read_pointers(scan, _num_placements);
00857
00858 manager->read_pointer(scan);
00859
00860 _index = scan.get_uint32();
00861 _basename = scan.get_string();
00862 _new_image = scan.get_bool();
00863 }