Mercurial > hg > dmlib
comparison tools/gfxconv.c @ 2481:f3d9cdb0a295
Some work towards more flexible palette remapping.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 28 Apr 2020 00:47:06 +0300 |
parents | 16c57206cef7 |
children | 2d60e112255e |
comparison
equal
deleted
inserted
replaced
2480:c7a0913e1032 | 2481:f3d9cdb0a295 |
---|---|
75 SCALE_RELATIVE, | 75 SCALE_RELATIVE, |
76 SCALE_AUTO, | 76 SCALE_AUTO, |
77 }; | 77 }; |
78 | 78 |
79 | 79 |
80 enum | |
81 { | |
82 REMAP_NONE = 0, | |
83 REMAP_AUTO, | |
84 REMAP_MAPPED, | |
85 }; | |
86 | |
80 typedef struct | 87 typedef struct |
81 { | 88 { |
82 char *name; // Descriptive name of the format | 89 char *name; // Descriptive name of the format |
83 char *fext; // File extension | 90 char *fext; // File extension |
84 int flags; // DM_FMT_* flags, see libgfx.h | 91 int flags; // DM_FMT_* flags, see libgfx.h |
132 optCropX0, optCropY0, | 139 optCropX0, optCropY0, |
133 optCropW, optCropH; | 140 optCropW, optCropH; |
134 | 141 |
135 BOOL optInMulticolor = FALSE, | 142 BOOL optInMulticolor = FALSE, |
136 optSequential = FALSE, | 143 optSequential = FALSE, |
137 optRemapColors = FALSE, | |
138 optRemapRemove = FALSE, | 144 optRemapRemove = FALSE, |
145 optRemapMatchAlpha = FALSE, | |
139 optUsePalette = FALSE; | 146 optUsePalette = FALSE; |
147 int optRemapMode = REMAP_NONE; | |
140 int optNRemapTable = 0, | 148 int optNRemapTable = 0, |
141 optScaleMode = SCALE_AUTO; | 149 optScaleMode = SCALE_AUTO; |
150 float optRemapMaxDist = 0; | |
151 int optRemapFail = 0; | |
142 DMMapValue optRemapTable[DM_MAX_COLORS]; | 152 DMMapValue optRemapTable[DM_MAX_COLORS]; |
143 int optColorMap[D64_NCOLORS]; | 153 int optColorMap[D64_NCOLORS]; |
144 char *optCharROMFilename = NULL; | 154 char *optCharROMFilename = NULL; |
145 DMC64Palette *optC64Palette = NULL; | 155 DMC64Palette *optC64Palette = NULL; |
146 char *optPaletteFile = NULL; | 156 char *optPaletteFile = NULL; |
182 { 34, 'B', "bpp" , "Bits per plane (some output formats)", OPT_ARGREQ }, | 192 { 34, 'B', "bpp" , "Bits per plane (some output formats)", OPT_ARGREQ }, |
183 { 36, 'I', "interleave" , "Interleaved/planar output (some output formats)", OPT_NONE }, | 193 { 36, 'I', "interleave" , "Interleaved/planar output (some output formats)", OPT_NONE }, |
184 { 38, 'C', "compress" , "Use compression -C <0-9>, 0 = disable, default is 9", OPT_ARGREQ }, | 194 { 38, 'C', "compress" , "Use compression -C <0-9>, 0 = disable, default is 9", OPT_ARGREQ }, |
185 { 42, 'R', "remap" , "Remap output image colors (-R <(#RRGGBB|index):index>[,<..>][+remove] | -R auto[+remove] | -R @map.txt[+remove])", OPT_ARGREQ }, | 195 { 42, 'R', "remap" , "Remap output image colors (-R <(#RRGGBB|index):index>[,<..>][+remove] | -R auto[+remove] | -R @map.txt[+remove])", OPT_ARGREQ }, |
186 { 44, 0, "char-rom" , "Set character ROM file to be used.", OPT_ARGREQ }, | 196 { 44, 0, "char-rom" , "Set character ROM file to be used.", OPT_ARGREQ }, |
187 { 46, 'p', "palette" , "Set C64 palette to be used (see list with -p help).", OPT_ARGREQ }, | 197 { 46, 'p', "palette" , "Set palette to be used (see list with -p help). " |
198 "For paletted image file input, this will replace the " | |
199 "image's original palette. Color remapping will not be " | |
200 "done unless -R option is also specified.", OPT_ARGREQ }, | |
188 }; | 201 }; |
189 | 202 |
190 static const int optListN = sizeof(optList) / sizeof(optList[0]); | 203 static const int optListN = sizeof(optList) / sizeof(optList[0]); |
191 | 204 |
192 | 205 |
930 } | 943 } |
931 } | 944 } |
932 break; | 945 break; |
933 | 946 |
934 case 42: | 947 case 42: |
935 if ((tmpStr = dm_strrcasecmp(optArg, "+remove")) != NULL) | 948 while ((tmpStr = strchr(optArg, '+')) != NULL) |
936 { | 949 { |
937 optRemapRemove = TRUE; | 950 char *topt = tmpStr + 1, |
951 *tend = strchr(topt, '+'); | |
952 | |
938 *tmpStr = 0; | 953 *tmpStr = 0; |
939 } | 954 if (tend != NULL) |
940 | 955 { |
941 if (optArg[0] == '@') | 956 *tend = 0; |
942 { | 957 tmpStr = tend + 1; |
943 if (optArg[1] != 0) | 958 } |
944 { | 959 else |
945 int res; | 960 tmpStr = tend; |
946 if ((res = dmParseColorRemapFile(optArg + 1, | 961 |
947 optRemapTable, &optNRemapTable, DM_MAX_COLORS)) != DMERR_OK) | 962 if (strcasecmp(topt, "remove") == 0) |
963 optRemapRemove = TRUE; | |
964 else | |
965 if (strcasecmp(topt, "alpha") == 0) | |
966 optRemapMatchAlpha = TRUE; | |
967 else | |
968 { | |
969 dmErrorMsg("Unknown -R option flag '%s'.\n", topt); | |
970 return FALSE; | |
971 } | |
972 } | |
973 | |
974 if (strcasecmp(optArg, "auto") == 0) | |
975 { | |
976 if (optRemapMode != REMAP_NONE && optRemapMode != REMAP_AUTO) | |
977 { | |
978 dmErrorMsg("Remap mode already set to something else than 'auto'. You can only have one remapping mode.\n"); | |
979 return FALSE; | |
980 } | |
981 | |
982 // Check if we have more? | |
983 if (optArg[4] == ':') | |
984 { | |
985 } | |
986 | |
987 optRemapMode = REMAP_AUTO; | |
988 } | |
989 else | |
990 { | |
991 if (optRemapMode != REMAP_NONE && optRemapMode != REMAP_MAPPED) | |
992 { | |
993 dmErrorMsg("Remap mode already set to something else than 'mapped'. You can only have one remapping mode.\n"); | |
994 return FALSE; | |
995 } | |
996 | |
997 if (optArg[0] == '@') | |
998 { | |
999 if (optArg[1] != 0) | |
1000 { | |
1001 int res; | |
1002 if ((res = dmParseColorRemapFile(optArg + 1, | |
1003 optRemapTable, &optNRemapTable, DM_MAX_COLORS)) != DMERR_OK) | |
1004 return FALSE; | |
1005 } | |
1006 else | |
1007 { | |
1008 dmErrorMsg("No remap filename given.\n"); | |
948 return FALSE; | 1009 return FALSE; |
1010 } | |
949 } | 1011 } |
950 else | 1012 else |
951 { | 1013 { |
952 dmErrorMsg("No remap filename given.\n"); | 1014 if (!dmParseMapOptionString(optArg, optRemapTable, |
953 return FALSE; | 1015 &optNRemapTable, DM_MAX_COLORS, TRUE, "color remap option")) |
954 } | 1016 return FALSE; |
955 } | 1017 } |
956 else | 1018 |
957 if (dm_strcasecmp(optArg, "auto") != 0) | 1019 optRemapMode = REMAP_MAPPED; |
958 { | 1020 } |
959 if (!dmParseMapOptionString(optArg, optRemapTable, | 1021 |
960 &optNRemapTable, DM_MAX_COLORS, TRUE, "color remap option")) | 1022 if (optRemapMatchAlpha) |
961 return FALSE; | 1023 { |
962 } | 1024 dmErrorMsg("WARNING: Palette alpha matching may have unexpected results.\n"); |
963 | 1025 } |
964 optRemapColors = TRUE; | |
965 break; | 1026 break; |
966 | 1027 |
967 case 44: | 1028 case 44: |
968 optCharROMFilename = optArg; | 1029 optCharROMFilename = optArg; |
969 break; | 1030 break; |
1076 else | 1137 else |
1077 return FALSE; | 1138 return FALSE; |
1078 } | 1139 } |
1079 | 1140 |
1080 | 1141 |
1081 int dmRemoveUnusedImageColors(DMImage **pdst, const DMImage *src) | 1142 float dmGetColorDist(const DMColor *c1, const DMColor *c2, const BOOL alpha) |
1082 { | 1143 { |
1083 DMPalette *tmpPal = NULL; | 1144 const float |
1084 int *mapping = dmMalloc(src->pal->ncolors * sizeof(int)); | 1145 dr = (c1->r - c2->r) / 255.0, |
1085 BOOL *mapped = dmMalloc(src->pal->ncolors * sizeof(BOOL)); | 1146 dg = (c1->g - c2->g) / 255.0, |
1086 BOOL *used = dmMalloc(src->pal->ncolors * sizeof(BOOL)); | 1147 db = (c1->b - c2->b) / 255.0; |
1087 int n, index, xc, yc, ncolors, res = DMERR_OK; | 1148 |
1088 DMImage *dst; | 1149 if (alpha) |
1089 | 1150 { |
1090 if (mapping == NULL || mapped == NULL || used == NULL) | 1151 const float da = (c1->a - c2->a) / 255.0; |
1152 return (dr * dr + dg * dg + db * db + da * da) / 4.0; | |
1153 } | |
1154 else | |
1155 return (dr * dr + dg * dg + db * db) / 3.0; | |
1156 } | |
1157 | |
1158 | |
1159 int dmScanUsedColors(const DMImage *src, const BOOL warn, BOOL *used, int *nused) | |
1160 { | |
1161 BOOL warned = FALSE; | |
1162 *nused = 0; | |
1163 | |
1164 if (src == NULL || used == NULL || nused == NULL) | |
1165 return DMERR_NULLPTR; | |
1166 | |
1167 if (src->pal == NULL || src->pixfmt != DM_PIXFMT_PALETTE) | |
1168 { | |
1169 return dmError(DMERR_INVALID_DATA, | |
1170 "Source image is not paletted.\n"); | |
1171 } | |
1172 | |
1173 for (int index = 0; index < src->pal->ncolors; index++) | |
1174 used[index] = FALSE; | |
1175 | |
1176 for (int yc = 0; yc < src->height; yc++) | |
1177 { | |
1178 const Uint8 *dp = src->data + src->pitch * yc; | |
1179 for (int xc = 0; xc < src->width; xc++) | |
1180 { | |
1181 Uint8 col = dp[xc]; | |
1182 if (col < src->pal->ncolors) | |
1183 { | |
1184 if (!used[col]) | |
1185 { | |
1186 used[col] = TRUE; | |
1187 (*nused)++; | |
1188 } | |
1189 } | |
1190 else | |
1191 if (warn && !warned) | |
1192 { | |
1193 dmErrorMsg("Image contains color indices that are out of bounds of the palette.\n"); | |
1194 warned = TRUE; | |
1195 } | |
1196 } | |
1197 } | |
1198 | |
1199 return DMERR_OK; | |
1200 } | |
1201 | |
1202 | |
1203 int dmDoRemapImageColors(DMImage **pdst, const DMImage *src, | |
1204 const int *mapping, const DMPalette *dpal) | |
1205 { | |
1206 DMImage *dst = NULL; | |
1207 int res = DMERR_OK; | |
1208 | |
1209 if (pdst == NULL || src == NULL || mapping == NULL) | |
1210 return DMERR_NULLPTR; | |
1211 | |
1212 // Allocate target image | |
1213 if ((dst = *pdst = dmImageAlloc(src->width, src->height, | |
1214 src->pixfmt, -1)) == NULL) | |
1091 { | 1215 { |
1092 res = dmError(DMERR_MALLOC, | 1216 res = dmError(DMERR_MALLOC, |
1093 "Could not allocate memory for reused palette.\n"); | 1217 "Could not allocate image for re-mapped data.\n"); |
1094 goto out; | 1218 goto out; |
1095 } | 1219 } |
1096 | 1220 |
1097 if ((res = dmPaletteAlloc(&tmpPal, src->pal->ncolors, -1)) != DMERR_OK) | 1221 for (int yc = 0; yc < src->height; yc++) |
1222 { | |
1223 Uint8 *sp = src->data + src->pitch * yc; | |
1224 Uint8 *dp = dst->data + dst->pitch * yc; | |
1225 for (int xc = 0; xc < src->width; xc++) | |
1226 { | |
1227 dp[xc] = mapping[sp[xc]]; | |
1228 } | |
1229 } | |
1230 | |
1231 if ((res = dmPaletteCopy(&dst->pal, dpal)) != DMERR_OK) | |
1232 { | |
1233 dmErrorMsg("Error installing remapped palette to destination image: %s\n", | |
1234 dmErrorStr(res)); | |
1235 goto out; | |
1236 } | |
1237 | |
1238 out: | |
1239 return res; | |
1240 } | |
1241 | |
1242 | |
1243 int dmRemapImageColors(DMImage **pdst, const DMImage *src, | |
1244 const DMPalette *dpal, const BOOL alpha, | |
1245 const float maxDist, const int noMatch, | |
1246 const BOOL removeUnused) | |
1247 { | |
1248 DMPalette *tpal = NULL; | |
1249 const DMPalette *ppal; | |
1250 BOOL *mapped = NULL, *used = NULL; | |
1251 int *mapping = NULL; | |
1252 int res = DMERR_OK; | |
1253 | |
1254 if (pdst == NULL || src == NULL || dpal == NULL) | |
1255 return DMERR_NULLPTR; | |
1256 | |
1257 if (src->pal == NULL || src->pixfmt != DM_PIXFMT_PALETTE) | |
1258 { | |
1259 res = dmError(DMERR_INVALID_DATA, | |
1260 "Source image is not paletted.\n"); | |
1261 goto out; | |
1262 } | |
1263 | |
1264 dmMsg(1, "Remapping image from %d to %d colors @ tolerance=%1.2f, %s%s.\n", | |
1265 src->pal->ncolors, | |
1266 dpal->ncolors, | |
1267 maxDist < 0 ? 0 : maxDist, | |
1268 alpha ? "match alpha" : "disregard alpha", | |
1269 noMatch < 0 ? ", fail on 'no match'" : ""); | |
1270 | |
1271 // Allocate remapping tables | |
1272 if ((mapped = dmMalloc0(src->pal->ncolors * sizeof(*mapped))) == NULL || | |
1273 (used = dmMalloc0(src->pal->ncolors * sizeof(*used))) == NULL || | |
1274 (mapping = dmMalloc(src->pal->ncolors * sizeof(*mapping))) == NULL) | |
1275 { | |
1276 res = dmError(DMERR_MALLOC, | |
1277 "Could not allocate memory for color remap tables.\n"); | |
1278 goto out; | |
1279 } | |
1280 | |
1281 for (int index = 0; index < src->pal->ncolors; index++) | |
1282 mapping[index] = -1; | |
1283 | |
1284 // Populate remap table | |
1285 for (int sc = 0; sc < src->pal->ncolors; sc++) | |
1286 { | |
1287 // Check if we can find a match in destination palette dpal | |
1288 float closestDist = 1000000, dist = 0; | |
1289 | |
1290 for (int dc = 0; dc < dpal->ncolors; dc++) | |
1291 { | |
1292 if (maxDist < 0) | |
1293 { | |
1294 if (dmExactCompareColor(&src->pal->colors[sc], &dpal->colors[dc], alpha)) | |
1295 { | |
1296 mapping[sc] = dc; | |
1297 break; | |
1298 } | |
1299 } | |
1300 else | |
1301 { | |
1302 dist = dmGetColorDist(&src->pal->colors[sc], &dpal->colors[dc], alpha); | |
1303 if (dist < maxDist && dist < closestDist) | |
1304 { | |
1305 closestDist = dist; | |
1306 mapping[sc] = dc; | |
1307 } | |
1308 } | |
1309 } | |
1310 | |
1311 // Did we find a match? | |
1312 if (mapping[sc] < 0) | |
1313 { | |
1314 // No, either error out or use noMatch color index | |
1315 if (noMatch < 0) | |
1316 { | |
1317 res = dmError(DMERR_INVALID_DATA, | |
1318 "Could not remap source color #%d, no matches found.\n", | |
1319 sc); | |
1320 goto out; | |
1321 } | |
1322 else | |
1323 { | |
1324 mapping[sc] = noMatch; | |
1325 } | |
1326 } | |
1327 else | |
1328 { | |
1329 DMColor *cs = &src->pal->colors[sc], | |
1330 *cd = &dpal->colors[mapping[sc]]; | |
1331 | |
1332 dmPrint(3, "Palette match #%d (%02x %02x %02x) -> #%d (%02x %02x %02x) [dist=%1.2f]\n", | |
1333 sc, cs->r, cs->g, cs->b, | |
1334 mapping[sc], cd->r, cd->g, cd->b, | |
1335 closestDist); | |
1336 } | |
1337 } | |
1338 | |
1339 // Remove unused colors if requested | |
1340 if (removeUnused) | |
1341 { | |
1342 // Get the actually used colors | |
1343 int nused, nc; | |
1344 | |
1345 if ((res = dmScanUsedColors(src, TRUE, used, &nused)) != DMERR_OK) | |
1346 goto out; | |
1347 | |
1348 dmMsg(2, "Found %d used colors.\n", nused); | |
1349 | |
1350 if ((res = dmPaletteAlloc(&tpal, nused, -1)) != DMERR_OK) | |
1351 { | |
1352 dmErrorMsg("Could not allocate memory for remap palette.\n"); | |
1353 goto out; | |
1354 } | |
1355 | |
1356 // Now, copy colors from dpal to tpal, re-mapping palette data | |
1357 // and reorder the mapping indices to match it. | |
1358 nc = 0; | |
1359 for (int index = 0; index < src->pal->ncolors; index++) | |
1360 if (used[index]) | |
1361 { | |
1362 memcpy(&tpal->colors[nc], &dpal->colors[mapping[index]], sizeof(DMColor)); | |
1363 mapping[index] = nc; | |
1364 nc++; | |
1365 } | |
1366 | |
1367 ppal = tpal; | |
1368 } | |
1369 else | |
1370 ppal = dpal; | |
1371 | |
1372 // Perform image data remapping | |
1373 res = dmDoRemapImageColors(pdst, src, mapping, ppal); | |
1374 | |
1375 out: | |
1376 dmFree(tpal); | |
1377 dmFree(mapping); | |
1378 dmFree(mapped); | |
1379 dmFree(used); | |
1380 return res; | |
1381 } | |
1382 | |
1383 | |
1384 int dmMapImageColors(DMImage **pdst, const DMImage *src, | |
1385 const DMMapValue *mapTable, const int nmapTable, | |
1386 const BOOL removeUnused) | |
1387 { | |
1388 DMPalette *tpal = NULL; | |
1389 BOOL *mapped = NULL, *used = NULL; | |
1390 int *mapping = NULL; | |
1391 int nused, res = DMERR_OK; | |
1392 | |
1393 if (pdst == NULL || src == NULL || mapTable == NULL) | |
1394 return DMERR_NULLPTR; | |
1395 | |
1396 if (src->pal == NULL || src->pixfmt != DM_PIXFMT_PALETTE) | |
1397 { | |
1398 res = dmError(DMERR_INVALID_DATA, | |
1399 "Source image is not paletted.\n"); | |
1400 goto out; | |
1401 } | |
1402 | |
1403 // Allocate remapping tables | |
1404 if ((mapped = dmMalloc(src->pal->ncolors * sizeof(*mapped))) == NULL || | |
1405 (used = dmMalloc(src->pal->ncolors * sizeof(*used))) == NULL || | |
1406 (mapping = dmMalloc(src->pal->ncolors * sizeof(*mapping))) == NULL) | |
1407 { | |
1408 res = dmError(DMERR_MALLOC, | |
1409 "Could not allocate memory for color remap tables.\n"); | |
1410 goto out; | |
1411 } | |
1412 | |
1413 for (int index = 0; index < src->pal->ncolors; index++) | |
1414 { | |
1415 mapping[index] = -1; | |
1416 mapped[index] = FALSE; | |
1417 } | |
1418 | |
1419 if ((res = dmPaletteAlloc(&tpal, src->pal->ncolors, -1)) != DMERR_OK) | |
1098 { | 1420 { |
1099 dmErrorMsg("Could not allocate memory for remap palette.\n"); | 1421 dmErrorMsg("Could not allocate memory for remap palette.\n"); |
1100 goto out; | 1422 goto out; |
1101 } | 1423 } |
1102 | 1424 |
1103 if ((dst = *pdst = dmImageAlloc(src->width, src->height, src->pixfmt, src->bpp)) == NULL) | 1425 dmMsg(1, "Remapping %d input image of %d colors.\n", |
1104 { | |
1105 res = dmError(DMERR_MALLOC, | |
1106 "Could not allocate memory for remapped image.\n"); | |
1107 goto out; | |
1108 } | |
1109 | |
1110 dmMsg(1, "Remapping %d output image colors of %d colors.\n", | |
1111 optNRemapTable, src->pal->ncolors); | 1426 optNRemapTable, src->pal->ncolors); |
1112 | 1427 |
1113 | |
1114 // Find used colors | |
1115 dmMsg(2, "Scanning image for used colors...\n"); | |
1116 for (index = 0; index < src->pal->ncolors; index++) | |
1117 { | |
1118 mapping[index] = -1; | |
1119 mapped[index] = used[index] = FALSE; | |
1120 } | |
1121 | |
1122 for (ncolors = yc = 0; yc < src->height; yc++) | |
1123 { | |
1124 const Uint8 *dp = src->data + src->pitch * yc; | |
1125 for (xc = 0; xc < src->width; xc++) | |
1126 { | |
1127 Uint8 col = dp[xc]; | |
1128 if (col < src->pal->ncolors && !used[col]) | |
1129 { | |
1130 used[col] = TRUE; | |
1131 ncolors++; | |
1132 } | |
1133 } | |
1134 } | |
1135 dmMsg(2, "Found %d used colors, creating remap-table.\n", ncolors); | |
1136 | |
1137 // Match and mark mapped colors | 1428 // Match and mark mapped colors |
1138 for (index = 0; index < optNRemapTable; index++) | 1429 for (int index = 0; index < nmapTable; index++) |
1139 { | 1430 { |
1140 DMMapValue *map = &optRemapTable[index]; | 1431 const DMMapValue *map = &mapTable[index]; |
1141 if (map->triplet) | 1432 if (map->triplet) |
1142 { | 1433 { |
1143 BOOL found = FALSE; | 1434 BOOL found = FALSE; |
1144 for (n = 0; n < src->pal->ncolors; n++) | 1435 for (int n = 0; n < src->pal->ncolors; n++) |
1145 { | 1436 { |
1146 if (dmExactCompareColor(&(src->pal->colors[n]), &(map->color), map->alpha)) | 1437 if (dmExactCompareColor(&src->pal->colors[n], &map->color, map->alpha)) |
1147 { | 1438 { |
1148 dmMsg(3, "RGBA match #%02x%02x%02x%02x: %d -> %d\n", | 1439 dmMsg(3, "RGBA match #%02x%02x%02x%02x: %d -> %d\n", |
1149 map->color.r, map->color.g, map->color.b, map->color.a, | 1440 map->color.r, map->color.g, map->color.b, map->color.a, |
1150 n, | 1441 n, |
1151 map->to); | 1442 map->to); |
1171 mapping[map->from] = map->to; | 1462 mapping[map->from] = map->to; |
1172 mapped[map->to] = TRUE; | 1463 mapped[map->to] = TRUE; |
1173 } | 1464 } |
1174 } | 1465 } |
1175 | 1466 |
1176 // Fill in the rest | 1467 // Fill the unmapped colors |
1177 if (optRemapRemove) | 1468 if (removeUnused) |
1178 dmMsg(2, "Removing unused colors.\n"); | 1469 { |
1179 | 1470 dmMsg(2, "Scanning for used colors.\n"); |
1180 for (index = 0; index < src->pal->ncolors; index++) | 1471 |
1472 if ((res = dmScanUsedColors(src, TRUE, used, &nused)) != DMERR_OK) | |
1473 goto out; | |
1474 | |
1475 dmMsg(2, "Removing unused colors: %d -> %d.\n", | |
1476 src->pal->ncolors, nused); | |
1477 } | |
1478 | |
1479 for (int index = 0; index < src->pal->ncolors; index++) | |
1181 if (mapping[index] < 0 && | 1480 if (mapping[index] < 0 && |
1182 (!optRemapRemove || (optRemapRemove && used[index]))) | 1481 (!removeUnused || used[index])) |
1183 { | 1482 { |
1184 for (n = 0; n < src->pal->ncolors; n++) | 1483 for (int n = 0; n < src->pal->ncolors; n++) |
1185 if (!mapped[n]) | 1484 if (!mapped[n]) |
1186 { | 1485 { |
1187 mapping[index] = n; | 1486 mapping[index] = n; |
1188 mapped[n] = TRUE; | 1487 mapped[n] = TRUE; |
1189 break; | 1488 break; |
1190 } | 1489 } |
1191 } | 1490 } |
1192 | 1491 |
1193 // Calculate final number of palette colors | 1492 // Copy mapped palette entries |
1194 ncolors = 0; | 1493 for (int index = 0; index < src->pal->ncolors; index++) |
1195 for (index = 0; index < src->pal->ncolors; index++) | 1494 if (mapping[index] >= 0) |
1196 { | 1495 { |
1197 if (mapping[index] + 1 > ncolors) | 1496 memcpy(&tpal->colors[mapping[index]], &src->pal->colors[index], sizeof(DMColor)); |
1198 ncolors = mapping[index] + 1; | 1497 } |
1199 } | 1498 |
1200 | 1499 // Perform image data remapping |
1201 // Copy palette entries | 1500 res = dmDoRemapImageColors(pdst, src, mapping, tpal); |
1202 for (index = 0; index < src->pal->ncolors; index++) | |
1203 { | |
1204 if (mapping[index] >= 0) | |
1205 { | |
1206 memcpy(&tmpPal->colors[mapping[index]], &(src->pal->colors[index]), sizeof(DMColor)); | |
1207 } | |
1208 } | |
1209 | |
1210 // Remap image | |
1211 dmMsg(1, "Remapping image to %d colors...\n", ncolors); | |
1212 for (yc = 0; yc < src->height; yc++) | |
1213 { | |
1214 Uint8 *sp = src->data + src->pitch * yc; | |
1215 Uint8 *dp = dst->data + dst->pitch * yc; | |
1216 for (xc = 0; xc < src->width; xc++) | |
1217 { | |
1218 Uint8 col = sp[xc]; | |
1219 if (col < src->pal->ncolors && | |
1220 mapping[col] >= 0 && | |
1221 mapping[col] < src->pal->ncolors) | |
1222 dp[xc] = mapping[col]; | |
1223 else | |
1224 dp[xc] = 0; | |
1225 } | |
1226 } | |
1227 | |
1228 // Set new palette, free memory | |
1229 if ((res = dmPaletteCopy(&dst->pal, tmpPal)) != DMERR_OK) | |
1230 { | |
1231 res = dmError(DMERR_MALLOC, | |
1232 "Could not allocate memory for final remapped palette.\n"); | |
1233 goto out; | |
1234 } | |
1235 | 1501 |
1236 out: | 1502 out: |
1503 dmPaletteFree(tpal); | |
1237 dmFree(mapping); | 1504 dmFree(mapping); |
1238 dmFree(mapped); | 1505 dmFree(mapped); |
1239 dmFree(used); | 1506 dmFree(used); |
1240 return res; | 1507 return res; |
1241 } | 1508 } |
1464 int res; | 1731 int res; |
1465 | 1732 |
1466 if ((res = dmf_open_stdio(hdrFilename, "wb", &fp)) != DMERR_OK) | 1733 if ((res = dmf_open_stdio(hdrFilename, "wb", &fp)) != DMERR_OK) |
1467 { | 1734 { |
1468 return dmError(res, | 1735 return dmError(res, |
1469 "RAW: Could not open file '%s' for writing: %s\n", | 1736 "Could not open file '%s' for writing: %s\n", |
1470 hdrFilename, dmErrorStr(res)); | 1737 hdrFilename, dmErrorStr(res)); |
1471 } | 1738 } |
1472 | 1739 |
1473 res = dmWriteIFFMasterRAWHeader(fp, dataFilename, prefix, img, spec); | 1740 res = dmWriteIFFMasterRAWHeader(fp, dataFilename, prefix, img, spec); |
1474 | 1741 |
1479 | 1746 |
1480 int dmWriteImage(const char *filename, DMImage *pimage, | 1747 int dmWriteImage(const char *filename, DMImage *pimage, |
1481 DMImageWriteSpec *spec, const DMImageFormat *fmt) | 1748 DMImageWriteSpec *spec, const DMImageFormat *fmt) |
1482 { | 1749 { |
1483 int res = DMERR_OK; | 1750 int res = DMERR_OK; |
1751 DMImage *image = pimage; | |
1752 BOOL allocated = FALSE; | |
1484 | 1753 |
1485 // Check if writing is even supported | 1754 // Check if writing is even supported |
1486 if (fmt->write == NULL || (fmt->flags & DM_FMT_WR) == 0) | 1755 if (fmt->write == NULL || (fmt->flags & DM_FMT_WR) == 0) |
1487 { | 1756 { |
1488 return dmError(DMERR_NOT_SUPPORTED, | 1757 return dmError(DMERR_NOT_SUPPORTED, |
1494 fmt->name, | 1763 fmt->name, |
1495 pimage->width, pimage->height, | 1764 pimage->width, pimage->height, |
1496 pimage->width * spec->scaleX, pimage->height * spec->scaleY, | 1765 pimage->width * spec->scaleX, pimage->height * spec->scaleY, |
1497 spec->scaleX, spec->scaleY); | 1766 spec->scaleX, spec->scaleY); |
1498 | 1767 |
1499 // Perform color remapping | 1768 if (image->pixfmt == DM_PIXFMT_PALETTE) |
1500 DMImage *image = pimage; | 1769 { |
1501 BOOL allocated = FALSE; | 1770 switch (optRemapMode) |
1502 if (optRemapColors && image->pixfmt == DM_PIXFMT_PALETTE) | 1771 { |
1503 { | 1772 case REMAP_NONE: |
1504 if ((res = dmRemoveUnusedImageColors(&image, pimage)) != DMERR_OK) | 1773 if (optPaletteData != NULL) |
1505 return res; | 1774 { |
1506 | 1775 DMPalette *tpal; |
1507 allocated = TRUE; | 1776 |
1777 dmMsg(1, "Replacing image palette %d colors with %d colors.\n", | |
1778 image->pal->ncolors, optPaletteData->ncolors); | |
1779 | |
1780 if ((res = dmPaletteCopy(&tpal, optPaletteData)) != DMERR_OK) | |
1781 return res; | |
1782 | |
1783 if (image->pal->ncolors != optPaletteData->ncolors) | |
1784 { | |
1785 dmMsg(1, "Trying to resize to %d colors.\n", | |
1786 image->pal->ncolors); | |
1787 | |
1788 if ((res = dmPaletteResize(&tpal, image->pal->ncolors)) != DMERR_OK) | |
1789 return res; | |
1790 } | |
1791 | |
1792 dmPaletteFree(image->pal); | |
1793 image->pal = tpal; | |
1794 } | |
1795 break; | |
1796 | |
1797 case REMAP_MAPPED: | |
1798 if ((res = dmMapImageColors(&image, pimage, | |
1799 optRemapTable, optNRemapTable, optRemapRemove)) != DMERR_OK) | |
1800 goto out; | |
1801 allocated = TRUE; | |
1802 break; | |
1803 | |
1804 case REMAP_AUTO: | |
1805 if (optPaletteData == NULL) | |
1806 { | |
1807 dmErrorMsg( | |
1808 "Color auto-remapping requested, but target palette not set? (-p option)\n"); | |
1809 goto out; | |
1810 } | |
1811 | |
1812 if ((res = dmRemapImageColors(&image, pimage, | |
1813 optPaletteData, optRemapMatchAlpha, | |
1814 //optRemapMaxDist, optRemapFail, | |
1815 0.5, -1, | |
1816 optRemapRemove)) != DMERR_OK) | |
1817 goto out; | |
1818 | |
1819 allocated = TRUE; | |
1820 break; | |
1821 } | |
1822 } | |
1823 else | |
1824 if (optRemapMode != REMAP_NONE) | |
1825 { | |
1826 dmErrorMsg("Color remapping requested, but image is not paletted?\n"); | |
1827 goto out; | |
1508 } | 1828 } |
1509 | 1829 |
1510 // Determine number of planes, if paletted | 1830 // Determine number of planes, if paletted |
1511 if (spec->nplanes == 0) | 1831 if (spec->nplanes == 0) |
1512 { | 1832 { |