1 | region = (uint8_t)( ( (uint16_t)(hsv.h) * 6 ) >> 8 );
|
2 | remainder = (uint8_t)( (uint16_t)(hsv.h) * 6 - ( (uint16_t)(region) << 8 ) ) ;
|
3 |
|
4 | p = (hsv.v * (255 - hsv.s)) >> 8;
|
5 | q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
|
6 | t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;
|
Wenn ich das richtig verstehe, dann ist der 'remainder' bei dir das, was
der Faktor f (double) zb hier
http://codezentrale.bplaced.net/dcz/?p=2999
ist.
f läuft normalerweise von 0 bis 1. Bei dir durch die Integer Rechnung,
hast du das so aufgeblasen, dass ein Wert von 255 einem Wert von 1 im
Original entspricht.
Dann kommen mir allerdings die Umsetzungen der Formeln für q und t etwas
'simplifiziert' vor. Durch das zwischendurch dividieren durch 256
verlierst du jedesmal einen Haufen 'Kommastellen'. Ich habs noch nicht
durchgerechnet, aber aus dem Bauch raus, ist das etwas zu naiv nach
Schema F ohne Rücksicht auf Verluste umgesetzt.
Was ich an deiner Stelle tun würde:
Ich würde jetzt erst mal auf den PC gehen und dort ein Testprogramm
schreiben, bei dem ich die beiden Berechnungen für eine Original-double
Version und deiner int Version vergleiche. Spezielles Augenmerk: die
Werte für p, q und t.
Weiters:
Da bei dir alles ein uint8_t ist, kannst du in ein weiteres 'Problem'
reinlaufen, welches ich jetzt auch noch nicht näher untersucht habe:
Die C Rechenregeln.
Die besagen nämlich, dass eine Berechnung mindestens im Datentyp int
gemacht wird und kleiner Datentypen erst mal promoted werden.
Jetzt ist es aber so, dass zb hier
1 | p = (hsv.v * (255 - hsv.s)) >> 8;
|
255 ein int ist. hsv.s ist zwar ein uint8_t, aber die C Regeln sagen
weiters, dass diese automatische Promotion in den kleinsten Datentyp
erfolgt, der alle Werte aufnehmen kann. Für einen uint8_t ist das aber
ein int. Und zwar ein signed int. D.h. das hier
255 - hsv.s
wird bereits als vorzeichenbehaftete Subtraktion berechnet. Auch die
Multiplikation mit hsv.v ändert nichts mehr daran, dass wir es hier mit
vorzeichenbehafteten Zahlen zu tun haben. Und das kann dann ins Auge
gehen, wenn ohne Rücksicht auf Verluste um 8 Stellen nach rechts
geschoben wird. Denn wenn die Bits richtig stehen, dann werden da nicht
nur die Bits verschoben, sondern das Vorzeichenbit rückt nach. Was zu
einem, sagen wir mal unsinnigem Ergebnis führt, wenn man eigentlich ein
unsigned Ergebnis erwartet. UNter anderem aus solchen Gründen halte ich
nicht viel von diesen sog. 'Cleveren Optimierungen', bei denen
Multiplikationen oder DIvisionen durch Schieben ersetzt wird. Wenn es
möglich ist, das zu tun, dann machen das die Compiler seit 50 Jahren
ganz von alleine. Das muss mir als Programmierer keine Kopfzerbrechen
machen.
Auf jeden Fall würde ich da sicherheitshalber die 255 mal unsigned
machen: 255U, damit der ganze Ausdruck auf keinen Fall eine Chance hat,
als signed Expression ausgewertet zu werden. Man muss sich immer vor
Augen halten, dass hier absichtlich alle 16 Bit voll als unsigned Wert
ausgenutzt werden. Sobald da irgendwo, egal wie, ein gesetztes 15-tes
Bit als Vorzeichenbit bei irgendeiner Operation fehlinterpretiert wird,
gibt es sehr wahrscheinlich irgendwo Ärger. Das können auch die
Multiplikationen sein, die dann falsche Ergebnisse liefern.
Das darf auf keinen Fall passieren. Daher: festnageln auf unsigned
Arithmetik, dem Compiler kein Schlupfloch offen lassen irgendwie auf
signed zu wechseln. Speziell die Promotion-Regeln lassen da aber schnell
mal ein Schlupfloch, wenn ein Operand signed ist.