Mercurial > hg > dmlib
comparison gfxconv.c @ 479:b768bfb0b364
Improve color remapping, add option for removing unused colors from the final output palette.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 07 Nov 2012 04:09:00 +0200 |
parents | 7c7a57590236 |
children | d7fc7e011c90 |
comparison
equal
deleted
inserted
replaced
478:7c7a57590236 | 479:b768bfb0b364 |
---|---|
116 optPlanedWidth = 1, | 116 optPlanedWidth = 1, |
117 optForcedFormat = -1; | 117 optForcedFormat = -1; |
118 int optInSkip = 0; | 118 int optInSkip = 0; |
119 BOOL optInMulticolor = FALSE, | 119 BOOL optInMulticolor = FALSE, |
120 optSequential = FALSE, | 120 optSequential = FALSE, |
121 optRemapColors = FALSE; | 121 optRemapColors = FALSE, |
122 optRemapRemove = FALSE; | |
122 int optNRemapTable = 0; | 123 int optNRemapTable = 0; |
123 DMMapValue optRemapTable[DM_MAX_COLORS]; | 124 DMMapValue optRemapTable[DM_MAX_COLORS]; |
124 int optColors[C64_MAX_COLORS]; | 125 int optColors[C64_MAX_COLORS]; |
125 | 126 |
126 DMImageSpec optSpec = | 127 DMImageSpec optSpec = |
150 { 11, 'w', "width", "Item width (number of items per row, min 1)", OPT_ARGREQ }, | 151 { 11, 'w', "width", "Item width (number of items per row, min 1)", OPT_ARGREQ }, |
151 { 12, 'P', "paletted", "Use indexed/paletted output (png, pcx output only)", OPT_NONE }, | 152 { 12, 'P', "paletted", "Use indexed/paletted output (png, pcx output only)", OPT_NONE }, |
152 { 13, 'B', "bplanes", "Bits per pixel OR # of bitplanes (certain output formats)", OPT_ARGREQ }, | 153 { 13, 'B', "bplanes", "Bits per pixel OR # of bitplanes (certain output formats)", OPT_ARGREQ }, |
153 { 14, 'I', "interleave", "Interleave scanlines (default: output whole planes)", OPT_NONE }, | 154 { 14, 'I', "interleave", "Interleave scanlines (default: output whole planes)", OPT_NONE }, |
154 { 16, 'R', "remap", "Remap output image colors (-R <(#RRGGBB|index):index>[,<..>] | -R @map.txt)", OPT_ARGREQ }, | 155 { 16, 'R', "remap", "Remap output image colors (-R <(#RRGGBB|index):index>[,<..>] | -R @map.txt)", OPT_ARGREQ }, |
156 { 18, 'r', "remap-remove", "Remove unused colors from remapped palette (requires -R)\n", OPT_NONE }, | |
155 }; | 157 }; |
156 | 158 |
157 static const int optListN = sizeof(optList) / sizeof(optList[0]); | 159 static const int optListN = sizeof(optList) / sizeof(optList[0]); |
158 | 160 |
159 | 161 |
578 } | 580 } |
579 | 581 |
580 optRemapColors = TRUE; | 582 optRemapColors = TRUE; |
581 break; | 583 break; |
582 | 584 |
585 case 18: | |
586 optRemapRemove = TRUE; | |
587 break; | |
588 | |
583 default: | 589 default: |
584 dmError("Unknown option '%s'.\n", currArg); | 590 dmError("Unknown option '%s'.\n", currArg); |
585 return FALSE; | 591 return FALSE; |
586 } | 592 } |
587 | 593 |
743 | 749 |
744 int dmRemapImageColors(DMImage *image) | 750 int dmRemapImageColors(DMImage *image) |
745 { | 751 { |
746 dmMsg(1, "Remapping %d output image colors.\n", optNRemapTable); | 752 dmMsg(1, "Remapping %d output image colors.\n", optNRemapTable); |
747 DMColor *npal = dmCalloc(image->ncolors, sizeof(DMColor)); | 753 DMColor *npal = dmCalloc(image->ncolors, sizeof(DMColor)); |
748 int *dpal = dmMalloc(image->ncolors * sizeof(int)); | 754 int *mapping = dmMalloc(image->ncolors * sizeof(int)); |
749 BOOL *spal = dmCalloc(image->ncolors, sizeof(BOOL)); | 755 BOOL *mapped = dmMalloc(image->ncolors * sizeof(BOOL)); |
750 int index, xc, yc, nncolors = image->ncolors; | 756 BOOL *used = dmMalloc(image->ncolors * sizeof(BOOL)); |
751 | 757 int n, index, xc, yc, ncolors = image->ncolors; |
752 if (npal == NULL || spal == NULL || dpal == NULL) | 758 |
753 { | 759 if (npal == NULL || mapping == NULL || mapped == NULL || used == NULL) |
754 dmError("Could not allocate memory for remapped palette.\n"); | 760 { |
761 dmError("Could not allocate memory for reused palette.\n"); | |
755 return DMERR_MALLOC; | 762 return DMERR_MALLOC; |
756 } | 763 } |
757 | 764 |
758 for (index = 0; index < image->ncolors; index++) | 765 for (index = 0; index < image->ncolors; index++) |
759 dpal[index] = -1; | 766 { |
760 | 767 mapping[index] = -1; |
761 // Find and mark mapped colors | 768 used[index] = mapped[index] = FALSE; |
769 } | |
770 | |
771 // Find used colors | |
772 dmMsg(2, "Scanning image for used colors...\n"); | |
773 for (yc = 0; yc < image->height; yc++) | |
774 { | |
775 Uint8 *dp = image->data + image->pitch * yc; | |
776 for (xc = 0; xc < image->width; xc++) | |
777 { | |
778 Uint8 col = dp[xc]; | |
779 if (col < image->ncolors) | |
780 used[col] = TRUE; | |
781 } | |
782 } | |
783 | |
784 // Match and mark mapped colors | |
762 for (index = 0; index < optNRemapTable; index++) | 785 for (index = 0; index < optNRemapTable; index++) |
763 { | 786 { |
764 DMMapValue *map = &optRemapTable[index]; | 787 DMMapValue *map = &optRemapTable[index]; |
765 if (map->triplet) | 788 if (map->triplet) |
766 { | 789 { |
767 BOOL found = FALSE; | 790 BOOL found = FALSE; |
768 int n; | |
769 for (n = 0; n < image->ncolors; n++) | 791 for (n = 0; n < image->ncolors; n++) |
770 { | 792 { |
771 if (dmCompareColor(&(image->pal[n]), &(map->color), map->alpha)) | 793 if (dmCompareColor(&(image->pal[n]), &(map->color), map->alpha)) |
772 { | 794 { |
773 dmMsg(3, "RGBA match #%02x%02x%02x%02x: %d -> %d\n", | 795 dmMsg(3, "RGBA match #%02x%02x%02x%02x: %d -> %d\n", |
774 map->color.r, map->color.g, map->color.b, map->color.a, | 796 map->color.r, map->color.g, map->color.b, map->color.a, |
775 n, | 797 n, |
776 map->to); | 798 map->to); |
777 | 799 |
778 dpal[map->to] = n; | 800 if (used[n]) |
779 spal[n] = TRUE; | 801 { |
780 found = TRUE; | 802 mapping[n] = map->to; |
781 // break; | 803 mapped[map->to] = TRUE; |
804 found = TRUE; | |
805 } | |
782 } | 806 } |
783 } | 807 } |
784 | 808 |
785 if (!found) | 809 if (!found) |
786 { | 810 { |
789 map->color.r, map->color.g, map->color.b, map->color.a); | 813 map->color.r, map->color.g, map->color.b, map->color.a); |
790 } | 814 } |
791 } | 815 } |
792 else | 816 else |
793 { | 817 { |
794 dmMsg(3, "Map index: %d -> %d\n", | 818 if (used[map->from]) |
795 map->from, map->to); | 819 { |
796 | 820 dmMsg(3, "Map index: %d -> %d\n", |
797 dpal[map->to] = map->from; | 821 map->from, map->to); |
798 spal[map->from] = TRUE; | 822 |
823 mapping[map->from] = map->to; | |
824 mapped[map->to] = TRUE; | |
825 } | |
799 } | 826 } |
800 } | 827 } |
801 | 828 |
802 // Fill in the rest | 829 // Fill in the rest |
803 dmMsg(3, "Placing non-mapped palette entries.\n"); | 830 if (optRemapRemove) |
831 { | |
832 dmMsg(2, "Removing unused colors.\n"); | |
833 for (index = 0; index < image->ncolors; index++) | |
834 if (mapping[index] < 0 && used[index]) | |
835 { | |
836 for (n = 0; n < image->ncolors; n++) | |
837 if (!mapped[n]) | |
838 { | |
839 mapping[index] = n; | |
840 mapped[n] = TRUE; | |
841 break; | |
842 } | |
843 } | |
844 } | |
845 else | |
846 { | |
847 for (index = 0; index < image->ncolors; index++) | |
848 if (mapping[index] < 0) | |
849 { | |
850 for (n = 0; n < image->ncolors; n++) | |
851 if (!mapped[n]) | |
852 { | |
853 mapping[index] = n; | |
854 mapped[n] = TRUE; | |
855 break; | |
856 } | |
857 } | |
858 } | |
859 | |
860 // Calculate final number of palette colors | |
861 ncolors = 0; | |
804 for (index = 0; index < image->ncolors; index++) | 862 for (index = 0; index < image->ncolors; index++) |
805 { | 863 { |
806 if (dpal[index] < 0) | 864 if (mapping[index] > ncolors) |
807 { | 865 ncolors = mapping[index] + 1; |
808 int src; | |
809 for (src = 0; src < image->ncolors; src++) | |
810 { | |
811 if (!spal[src]) | |
812 { | |
813 dpal[index] = src; | |
814 spal[src] = TRUE; | |
815 break; | |
816 } | |
817 } | |
818 } | |
819 } | 866 } |
820 | 867 |
821 // Copy palette entries | 868 // Copy palette entries |
822 dmMsg(3, "Creating new palette.\n"); | 869 for (index = 0; index < ncolors; index++) |
823 for (index = 0; index < image->ncolors; index++) | 870 { |
824 { | 871 if (mapping[index] >= 0) |
825 if (dpal[index] >= 0 && dpal[index] < image->ncolors) | 872 { |
826 { | 873 memcpy(&npal[index], &(image->pal[mapping[index]]), sizeof(DMColor)); |
827 memcpy(&npal[index], &(image->pal[dpal[index]]), sizeof(DMColor)); | 874 } |
828 nncolors = index; | 875 } |
829 } | |
830 } | |
831 | |
832 | 876 |
833 // Remap image | 877 // Remap image |
834 dmMsg(1, "Remapping image ...\n"); | 878 dmMsg(1, "Remapping image to %d colors...\n", ncolors); |
835 for (yc = 0; yc < image->height; yc++) | 879 for (yc = 0; yc < image->height; yc++) |
836 { | 880 { |
837 Uint8 *dp = image->data + image->pitch * yc; | 881 Uint8 *dp = image->data + image->pitch * yc; |
838 for (xc = 0; xc < image->width; xc++) | 882 for (xc = 0; xc < image->width; xc++) |
839 { | 883 { |
840 Uint8 col = dp[xc]; | 884 Uint8 col = dp[xc]; |
841 if (col < image->ncolors && dpal[col] >= 0 && dpal[col] < image->ncolors) | 885 if (col < image->ncolors && mapping[col] >= 0 && mapping[col] < image->ncolors) |
842 { | 886 dp[xc] = mapping[col]; |
843 dp[xc] = dpal[col]; | |
844 } | |
845 else | 887 else |
846 dp[xc] = 0; | 888 dp[xc] = 0; |
847 } | 889 } |
848 } | 890 } |
849 | 891 |
850 // Set new palette, free memory | 892 // Set new palette, free memory |
851 dmFree(image->pal); | 893 dmFree(image->pal); |
852 image->pal = npal; | 894 image->pal = npal; |
853 image->ncolors = nncolors; | 895 image->ncolors = ncolors; |
854 dmFree(spal); | 896 |
855 dmFree(dpal); | 897 dmFree(mapping); |
898 dmFree(mapped); | |
899 dmFree(used); | |
856 return DMERR_OK; | 900 return DMERR_OK; |
857 } | 901 } |
858 | 902 |
859 | 903 |
860 int dmWriteImage(const char *filename, DMImage *image, DMImageSpec *spec, int iformat, BOOL info) | 904 int dmWriteImage(const char *filename, DMImage *image, DMImageSpec *spec, int iformat, BOOL info) |