1060 |
1060 |
1061 "Created: / 29.7.1998 / 00:21:25 / cg" |
1061 "Created: / 29.7.1998 / 00:21:25 / cg" |
1062 ! ! |
1062 ! ! |
1063 |
1063 |
1064 !Depth8Image methodsFor:'dither helpers'! |
1064 !Depth8Image methodsFor:'dither helpers'! |
|
1065 |
|
1066 floydSteinbergDitheredDepth8BitsColors:colors map:aMapOrNil |
|
1067 "return a floyd-steinberg dithered bitmap from the receiver picture, |
|
1068 which must be a depth-8 image. |
|
1069 This method expects an array of colors to be used for dithering |
|
1070 (which need not be a colorCubes colors)." |
|
1071 |
|
1072 |pseudoBits |
|
1073 rgbBytes |
|
1074 ditherRGBBytes ditherColors |
|
1075 w "{Class: SmallInteger }" |
|
1076 h "{Class: SmallInteger }" |
|
1077 index "{Class: SmallInteger }" |
|
1078 ditherIds failed lastColor qScramble |
|
1079 clrLookup lookupPos |
|
1080 error clr| |
|
1081 |
|
1082 "/ |
|
1083 "/ collect color components as integer values (for integer arithmetic) |
|
1084 "/ |
|
1085 rgbBytes := ByteArray uninitializedNew:256 * 3. |
|
1086 |
|
1087 photometric == #palette ifTrue:[ |
|
1088 lastColor := colorMap size - 1 |
|
1089 ] ifFalse:[ |
|
1090 lastColor := 255. |
|
1091 ]. |
|
1092 index := 1. |
|
1093 0 to:lastColor do:[:pix | |
|
1094 clr := self colorFromValue:pix. |
|
1095 rgbBytes at:index put:(clr redByte). |
|
1096 rgbBytes at:index+1 put:(clr greenByte). |
|
1097 rgbBytes at:index+2 put:(clr blueByte). |
|
1098 |
|
1099 index := index + 3. |
|
1100 ]. |
|
1101 |
|
1102 "/ collect valid ditherColors ... |
|
1103 aMapOrNil isNil ifTrue:[ |
|
1104 ditherColors := colors select:[:clr | clr notNil]. |
|
1105 ] ifFalse:[ |
|
1106 ditherColors := colors |
|
1107 ]. |
|
1108 |
|
1109 "/ ... and sort by manhatten distance from black |
|
1110 |
|
1111 qScramble := #( |
|
1112 "/ 2rX00X00X00X00 |
|
1113 |
|
1114 2r000000000000 "/ 0 |
|
1115 2r000000000100 "/ 1 |
|
1116 2r000000100000 "/ 2 |
|
1117 2r000000100100 "/ 3 |
|
1118 2r000100000000 "/ 4 |
|
1119 2r000100000100 "/ 5 |
|
1120 2r000100100000 "/ 6 |
|
1121 2r000100100100 "/ 7 |
|
1122 2r100000000000 "/ 8 |
|
1123 2r100000000100 "/ 9 |
|
1124 2r100000100000 "/ a |
|
1125 2r100000100100 "/ b |
|
1126 2r100100000000 "/ c |
|
1127 2r100100000100 "/ d |
|
1128 2r100100100000 "/ e |
|
1129 2r100100100100 "/ f |
|
1130 ). |
|
1131 |
|
1132 ditherColors := ditherColors sort:[:a :b | |
|
1133 |cr "{Class: SmallInteger }" |
|
1134 cg "{Class: SmallInteger }" |
|
1135 cb "{Class: SmallInteger }" |
|
1136 i1 "{Class: SmallInteger }" |
|
1137 i2 "{Class: SmallInteger }"| |
|
1138 |
|
1139 cr := a redByte. |
|
1140 cg := a greenByte. |
|
1141 cb := a blueByte. |
|
1142 i1 := qScramble at:((cr bitShift:-4) bitAnd:16r0F) + 1. |
|
1143 i1 := i1 + ((qScramble at:((cg bitShift:-4) bitAnd:16r0F) + 1) bitShift:-1). |
|
1144 i1 := i1 + ((qScramble at:((cb bitShift:-4) bitAnd:16r0F) + 1) bitShift:-2). |
|
1145 |
|
1146 cr := b redByte. |
|
1147 cg := b greenByte. |
|
1148 cb := b blueByte. |
|
1149 i2 := qScramble at:((cr bitShift:-4) bitAnd:16r0F) + 1. |
|
1150 i2 := i2 + ((qScramble at:((cg bitShift:-4) bitAnd:16r0F) + 1) bitShift:-1). |
|
1151 i2 := i2 + ((qScramble at:((cb bitShift:-4) bitAnd:16r0F) + 1) bitShift:-2). |
|
1152 |
|
1153 i1 < i2 |
|
1154 ]. |
|
1155 aMapOrNil isNil ifTrue:[ |
|
1156 ditherIds := (ditherColors asArray collect:[:clr | clr colorId]) asByteArray. |
|
1157 ] ifFalse:[ |
|
1158 ditherIds := aMapOrNil asByteArray |
|
1159 ]. |
|
1160 |
|
1161 "/ build an index table, for fast lookup from manhatten-r-g-b distance |
|
1162 "/ to the position in the colorList |
|
1163 |
|
1164 clrLookup := ByteArray new:(4096). |
|
1165 index := 0. |
|
1166 ditherColors keysAndValuesDo:[:clrPosition :clr | |
|
1167 |r g b i| |
|
1168 |
|
1169 r := clr redByte. |
|
1170 g := clr greenByte. |
|
1171 b := clr blueByte. |
|
1172 i := qScramble at:((r bitShift:-4) bitAnd:16r0F) + 1. |
|
1173 i := i + ((qScramble at:((g bitShift:-4) bitAnd:16r0F) + 1) bitShift:-1). |
|
1174 i := i + ((qScramble at:((b bitShift:-4) bitAnd:16r0F) + 1) bitShift:-2). |
|
1175 lookupPos := i. |
|
1176 |
|
1177 [index < lookupPos] whileTrue:[ |
|
1178 clrLookup at:(index+1) put:(clrPosition-1-1). |
|
1179 index := index + 1 |
|
1180 ] |
|
1181 ]. |
|
1182 clrLookup from:index+1 to:4096 put:(ditherColors size - 1). |
|
1183 |
|
1184 "/ [index <= (4095)] whileTrue:[ |
|
1185 "/ clrLookup at:(index+1) put:(ditherColors size - 1). |
|
1186 "/ index := index + 1. |
|
1187 "/ ]. |
|
1188 |
|
1189 "/ collect ditherColor components |
|
1190 |
|
1191 lastColor := ditherColors size. |
|
1192 ditherIds := ByteArray uninitializedNew:lastColor. |
|
1193 ditherRGBBytes := ByteArray uninitializedNew:(lastColor * 3). |
|
1194 index := 1. |
|
1195 1 to:lastColor do:[:pix | |
|
1196 clr := ditherColors at:pix. |
|
1197 ditherRGBBytes at:index put:(clr redByte). |
|
1198 ditherRGBBytes at:index+1 put:(clr greenByte). |
|
1199 ditherRGBBytes at:index+2 put:(clr blueByte). |
|
1200 aMapOrNil isNil ifTrue:[ |
|
1201 ditherIds at:pix put:clr colorId. |
|
1202 ] ifFalse:[ |
|
1203 ditherIds at:pix put:(aMapOrNil at:pix). |
|
1204 ]. |
|
1205 index := index + 3. |
|
1206 ]. |
|
1207 |
|
1208 pseudoBits := ByteArray uninitializedNew:(width * height). |
|
1209 |
|
1210 w := width + 2. |
|
1211 error := ByteArray uninitializedNew:w*(3*2). |
|
1212 |
|
1213 w := width. |
|
1214 h := height. |
|
1215 |
|
1216 failed := true. |
|
1217 |
|
1218 %{ |
|
1219 int __x, __y; |
|
1220 int __eR, __eG, __eB; |
|
1221 unsigned char *srcP, *dstP; |
|
1222 unsigned char *rgbP; |
|
1223 unsigned char *idP; |
|
1224 unsigned char *dp; |
|
1225 unsigned char *__clrLookup; |
|
1226 short *errP, *eP; |
|
1227 int __fR, __fG, __fB; |
|
1228 int iR, iG, iB; |
|
1229 int idx; |
|
1230 int __w = __intVal(w); |
|
1231 int __h = __intVal(h); |
|
1232 int __nColors = __intVal(lastColor); |
|
1233 int __wR = -1, __wG, __wB; |
|
1234 static int __qScramble[16] = { |
|
1235 0x000 /* 2r000000000000 0 */, |
|
1236 0x004 /* 2r000000000100 1 */, |
|
1237 0x020 /* 2r000000100000 2 */, |
|
1238 0x024 /* 2r000000100100 3 */, |
|
1239 0x100 /* 2r000100000000 4 */, |
|
1240 0x104 /* 2r000100000100 5 */, |
|
1241 0x120 /* 2r000100100000 6 */, |
|
1242 0x124 /* 2r000100100100 7 */, |
|
1243 0x800 /* 2r100000000000 8 */, |
|
1244 0x804 /* 2r100000000100 9 */, |
|
1245 0x820 /* 2r100000100000 a */, |
|
1246 0x824 /* 2r100000100100 b */, |
|
1247 0x900 /* 2r100100000000 c */, |
|
1248 0x904 /* 2r100100000100 d */, |
|
1249 0x920 /* 2r100100100000 e */, |
|
1250 0x924 /* 2r100100100100 f */, |
|
1251 }; |
|
1252 |
|
1253 if (__isByteArray(__INST(bytes)) |
|
1254 && __isByteArray(pseudoBits) |
|
1255 && __isByteArray(rgbBytes) |
|
1256 && __isByteArray(ditherRGBBytes) |
|
1257 && __isByteArray(ditherIds) |
|
1258 && __isByteArray(clrLookup) |
|
1259 && __isByteArray(error)) { |
|
1260 failed = false; |
|
1261 |
|
1262 srcP = __ByteArrayInstPtr(_INST(bytes))->ba_element; |
|
1263 dstP = __ByteArrayInstPtr(pseudoBits)->ba_element; |
|
1264 rgbP = __ByteArrayInstPtr(rgbBytes)->ba_element; |
|
1265 idP = __ByteArrayInstPtr(ditherIds)->ba_element; |
|
1266 __clrLookup = __ByteArrayInstPtr(clrLookup)->ba_element; |
|
1267 errP = (short *) __ByteArrayInstPtr(error)->ba_element; |
|
1268 |
|
1269 /* |
|
1270 * clear error accumulator |
|
1271 */ |
|
1272 eP = errP; |
|
1273 bzero(eP, (__w+2) * 2 * 3); |
|
1274 |
|
1275 for (__y=__h; __y>0; __y--) { |
|
1276 __eR = __eG = __eB = 0; |
|
1277 |
|
1278 eP = &(errP[3]); |
|
1279 __eR = eP[0]; |
|
1280 __eG = eP[1]; |
|
1281 __eB = eP[2]; |
|
1282 |
|
1283 for (__x=__w; __x>0; __x--) { |
|
1284 int __want; |
|
1285 int pix; |
|
1286 int __wantR, __wantG, __wantB; |
|
1287 int idx; |
|
1288 int tR, tG, tB; |
|
1289 int nR, nG, nB; |
|
1290 int dR, dG, dB; |
|
1291 int minDelta, bestIdx; |
|
1292 int cnt; |
|
1293 |
|
1294 pix = *srcP++; |
|
1295 |
|
1296 /* |
|
1297 * wR, wG and wB is the wanted r/g/b value; |
|
1298 */ |
|
1299 idx = pix+pix+pix; /* pix * 3 */ |
|
1300 |
|
1301 __wantR = rgbP[idx] + __eR; |
|
1302 __wantG = rgbP[idx+1] + __eG; |
|
1303 __wantB = rgbP[idx+2] + __eB; |
|
1304 |
|
1305 #define RED_SCALE 30 |
|
1306 #define GREEN_SCALE 59 |
|
1307 #define BLUE_SCALE 11 |
|
1308 #define GOOD_DELTA 30 |
|
1309 |
|
1310 #define xRED_SCALE 1 |
|
1311 #define xGREEN_SCALE 1 |
|
1312 #define xBLUE_SCALE 1 |
|
1313 #define xGOOD_DELTA 3 |
|
1314 |
|
1315 #define FAST_LOOKUP |
|
1316 /* #define ONE_SHOT */ |
|
1317 #define NPROBE 8 |
|
1318 |
|
1319 #ifndef FAST_LOOKUP |
|
1320 if ((__wantR == __wR) |
|
1321 && (__wantG == __wG) |
|
1322 && (__wantB == __wB)) { |
|
1323 /* |
|
1324 * same color again - reuse last bestMatch |
|
1325 */ |
|
1326 } else |
|
1327 #endif |
|
1328 { |
|
1329 __wR = __wantR; |
|
1330 __wG = __wantG; |
|
1331 __wB = __wantB; |
|
1332 |
|
1333 #ifdef FAST_LOOKUP |
|
1334 if(__wR > 255) __wR = 255; |
|
1335 else if (__wR < 0) __wR = 0; |
|
1336 if(__wG > 255) __wG = 255; |
|
1337 else if (__wG < 0) __wG = 0; |
|
1338 if(__wB > 255) __wB = 255; |
|
1339 else if (__wB < 0) __wB = 0; |
|
1340 |
|
1341 { |
|
1342 int lookupIndex; |
|
1343 int idx, idx0; |
|
1344 int d, delta; |
|
1345 unsigned char *dp0; |
|
1346 |
|
1347 dp = __ByteArrayInstPtr(ditherRGBBytes)->ba_element; |
|
1348 lookupIndex = __qScramble[((__wR & 0xF0)>>4)]; |
|
1349 lookupIndex |= __qScramble[((__wG & 0xF0)>>4)] >> 1; |
|
1350 lookupIndex |= __qScramble[((__wB & 0xF0)>>4)] >> 2; |
|
1351 idx = bestIdx =__clrLookup[lookupIndex]; |
|
1352 dp += (idx+idx+idx); |
|
1353 |
|
1354 /* try color at lookupIndex */ |
|
1355 |
|
1356 d = dp[0]; |
|
1357 delta = (__wR - d) * RED_SCALE; |
|
1358 if (delta < 0) delta = -delta; |
|
1359 |
|
1360 d = dp[1]; |
|
1361 if (__wG > d) |
|
1362 delta += (__wG - d) * GREEN_SCALE; |
|
1363 else |
|
1364 delta += (d - __wG) * GREEN_SCALE; |
|
1365 d = dp[2]; |
|
1366 if (__wB > d) |
|
1367 delta += (__wB - d) * BLUE_SCALE; |
|
1368 else |
|
1369 delta += (d - __wB) * BLUE_SCALE; |
|
1370 |
|
1371 if (delta <= GOOD_DELTA) { |
|
1372 goto foundBest; |
|
1373 } |
|
1374 minDelta = delta; |
|
1375 # ifndef ONE_SHOT |
|
1376 idx0 = idx; dp0 = dp; |
|
1377 cnt = 0; |
|
1378 while ((++cnt <= NPROBE) && (idx > 0)) { |
|
1379 /* try previous color(s) */ |
|
1380 |
|
1381 idx--; dp -= 3; |
|
1382 d = dp[0]; |
|
1383 delta = (__wR - d) * RED_SCALE; |
|
1384 if (delta < 0) delta = -delta; |
|
1385 d = dp[1]; |
|
1386 if (__wG > d) |
|
1387 delta += (__wG - d) * GREEN_SCALE; |
|
1388 else |
|
1389 delta += (d - __wG) * GREEN_SCALE; |
|
1390 d = dp[2]; |
|
1391 if (__wB > d) |
|
1392 delta += (__wB - d) * BLUE_SCALE; |
|
1393 else |
|
1394 delta += (d - __wB) * BLUE_SCALE; |
|
1395 |
|
1396 if (delta < minDelta) { |
|
1397 bestIdx = idx; |
|
1398 if (delta <= GOOD_DELTA) { |
|
1399 goto foundBest; |
|
1400 } |
|
1401 minDelta = delta; |
|
1402 } |
|
1403 } |
|
1404 |
|
1405 idx = idx0; dp = dp0; |
|
1406 cnt = 0; |
|
1407 while ((++cnt <= NPROBE) && (++idx < __nColors)) { |
|
1408 /* try next color */ |
|
1409 |
|
1410 dp += 3; |
|
1411 d = dp[0]; |
|
1412 delta = (__wR - d) * RED_SCALE; |
|
1413 if (delta < 0) delta = -delta; |
|
1414 d = dp[1]; |
|
1415 if (__wG > d) |
|
1416 delta += (__wG - d) * GREEN_SCALE; |
|
1417 else |
|
1418 delta += (d - __wG) * GREEN_SCALE; |
|
1419 d = dp[2]; |
|
1420 if (__wB > d) |
|
1421 delta += (__wB - d) * BLUE_SCALE; |
|
1422 else |
|
1423 delta += (d - __wB) * BLUE_SCALE; |
|
1424 |
|
1425 if (delta < minDelta) { |
|
1426 bestIdx = idx; |
|
1427 if (delta <= GOOD_DELTA) { |
|
1428 goto foundBest; |
|
1429 } |
|
1430 minDelta = delta; |
|
1431 } |
|
1432 } |
|
1433 # endif |
|
1434 } |
|
1435 foundBest: ; |
|
1436 #else |
|
1437 /* |
|
1438 if(__wR > 255) __wR = 255; |
|
1439 else if (__wR < 0) __wR = 0; |
|
1440 if(__wG > 255) __wG = 255; |
|
1441 else if (__wG < 0) __wG = 0; |
|
1442 if(__wB > 255) __wB = 255; |
|
1443 else if (__wB < 0) __wB = 0; |
|
1444 */ |
|
1445 |
|
1446 /* find the best matching color */ |
|
1447 |
|
1448 minDelta = 99999; |
|
1449 bestIdx = -1; |
|
1450 dp = __ByteArrayInstPtr(ditherRGBBytes)->ba_element; |
|
1451 for (idx = 0; idx<__nColors; idx++) { |
|
1452 int d, delta; |
|
1453 |
|
1454 d = dp[0]; |
|
1455 delta = (__wR - d) * RED_SCALE; |
|
1456 if (delta < 0) delta = -delta; |
|
1457 if (delta < minDelta) { |
|
1458 d = dp[1]; |
|
1459 if (__wG > d) |
|
1460 delta += (__wG - d) * GREEN_SCALE; |
|
1461 else |
|
1462 delta += (d - __wG) * GREEN_SCALE; |
|
1463 if (delta < minDelta) { |
|
1464 d = dp[2]; |
|
1465 if (__wB > d) |
|
1466 delta += (__wB - d) * BLUE_SCALE; |
|
1467 else |
|
1468 delta += (d - __wB) * BLUE_SCALE; |
|
1469 |
|
1470 if (delta < minDelta) { |
|
1471 bestIdx = idx; |
|
1472 if (delta <= GOOD_DELTA) { |
|
1473 break; |
|
1474 } |
|
1475 minDelta = delta; |
|
1476 } |
|
1477 } |
|
1478 } |
|
1479 dp += 3; |
|
1480 } |
|
1481 #endif |
|
1482 } |
|
1483 dp = __ByteArrayInstPtr(ditherRGBBytes)->ba_element; |
|
1484 dp += bestIdx * 3; |
|
1485 dR = dp[0]; |
|
1486 dG = dp[1]; |
|
1487 dB = dp[2]; |
|
1488 |
|
1489 /* |
|
1490 printf("want: %d/%d/%d (%d/%d/%d) got: %d/%d/%d\n", |
|
1491 __wantR, __wantG, __wantB, |
|
1492 __wR, __wG, __wB, |
|
1493 dR, dG, dB); |
|
1494 */ |
|
1495 /* |
|
1496 * store the corresponding dither colors colorId |
|
1497 */ |
|
1498 *dstP++ = idP[bestIdx]; |
|
1499 |
|
1500 /* |
|
1501 * the new error & distribute the error |
|
1502 */ |
|
1503 __eR = __wantR - dR; |
|
1504 if (__eR) { |
|
1505 tR = __eR >> 4; /* 16th of error */ |
|
1506 nR = eP[3] + (tR * 7);/* from accu: error for (x+1 / y) */ |
|
1507 eP[0] = tR*5; /* 5/16th for (x / y+1) */ |
|
1508 eP[-3] = tR*3; /* 3/16th for (x-1 / y+1) */ |
|
1509 eP[3] = __eR - (tR*15); /* 1/16th for (x+1 / y+1) */ |
|
1510 __eR = nR; |
|
1511 } else { |
|
1512 __eR = eP[3]; |
|
1513 eP[0] = eP[-3] = eP[3] = 0; |
|
1514 } |
|
1515 |
|
1516 __eG = __wantG - dG; |
|
1517 if (__eG) { |
|
1518 tG = __eG >> 4; |
|
1519 nG = eP[4] + (tG * 7);/* plus 7/16'th of this error */ |
|
1520 eP[1] = tG*5; |
|
1521 eP[-2] = tG*3; |
|
1522 eP[4] = __eG - (tG*15); |
|
1523 __eG = nG; |
|
1524 } else { |
|
1525 __eG = eP[4]; |
|
1526 eP[1] = eP[-2] = eP[4] = 0; |
|
1527 } |
|
1528 |
|
1529 __eB = __wantB - dB; |
|
1530 if (__eB) { |
|
1531 tB = __eB >> 4; |
|
1532 nB = eP[5] + (tB * 7); |
|
1533 eP[2] = tB*5; |
|
1534 eP[-1] = tB*3; |
|
1535 eP[5] = __eB - (tB*15); |
|
1536 __eB = nB; |
|
1537 } else { |
|
1538 __eB = eP[5]; |
|
1539 eP[2] = eP[-1] = eP[5] = 0; |
|
1540 } |
|
1541 |
|
1542 eP += 3; |
|
1543 } |
|
1544 } |
|
1545 } |
|
1546 %}. |
|
1547 failed ifTrue:[ |
|
1548 self primitiveFailed. |
|
1549 ^ nil |
|
1550 ]. |
|
1551 |
|
1552 ^ pseudoBits |
|
1553 ! |
1065 |
1554 |
1066 orderedDitheredGrayBitsWithDitherMatrix:ditherMatrix ditherWidth:dW depth:depth |
1555 orderedDitheredGrayBitsWithDitherMatrix:ditherMatrix ditherWidth:dW depth:depth |
1067 "return the bitmap for a dithered depth-bitmap from the image; |
1556 "return the bitmap for a dithered depth-bitmap from the image; |
1068 with a constant ditherMatrix, this can be used for thresholding. |
1557 with a constant ditherMatrix, this can be used for thresholding. |
1069 Redefined to make use of knowing that pixels are 8-bit values." |
1558 Redefined to make use of knowing that pixels are 8-bit values." |