1 //******************************************************************************
2 ///
3 /// @file base/image/encoding.cpp
4 ///
5 /// Implementations related to generic image data encoding (quantization) and
6 /// decoding.
7 ///
8 /// @copyright
9 /// @parblock
10 ///
11 /// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
12 /// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
13 ///
14 /// POV-Ray is free software: you can redistribute it and/or modify
15 /// it under the terms of the GNU Affero General Public License as
16 /// published by the Free Software Foundation, either version 3 of the
17 /// License, or (at your option) any later version.
18 ///
19 /// POV-Ray is distributed in the hope that it will be useful,
20 /// but WITHOUT ANY WARRANTY; without even the implied warranty of
21 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 /// GNU Affero General Public License for more details.
23 ///
24 /// You should have received a copy of the GNU Affero General Public License
25 /// along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 ///
27 /// ----------------------------------------------------------------------------
28 ///
29 /// POV-Ray is based on the popular DKB raytracer version 2.12.
30 /// DKBTrace was originally written by David K. Buck.
31 /// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
32 ///
33 /// @endparblock
34 ///
35 //******************************************************************************
36 
37 // Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config)
38 #include "base/image/encoding.h"
39 
40 // POV-Ray header files (base module)
41 #include "base/image/dither.h"
42 #include "base/image/image.h"
43 
44 // this must be the last file included
45 #include "base/povdebug.h"
46 
47 namespace pov_base
48 {
49 
50 /*******************************************************************************/
51 
52 #define ALPHA_EPSILON 1.0e-6 ///< Smallest alpha value we dare to safely use with premultiplied alpha.
53 
54 /*******************************************************************************/
55 
AlphaPremultiply(float & fGray,float fAlpha)56 inline void AlphaPremultiply(float& fGray, float fAlpha)
57 {
58     fGray *= fAlpha;
59 }
AlphaPremultiply(float & fRed,float & fGreen,float & fBlue,float fAlpha)60 inline void AlphaPremultiply(float& fRed, float& fGreen, float& fBlue, float fAlpha)
61 {
62     fRed   *= fAlpha;
63     fGreen *= fAlpha;
64     fBlue  *= fAlpha;
65 }
AlphaUnPremultiply(float & fGray,float fAlpha)66 inline void AlphaUnPremultiply(float& fGray, float fAlpha)
67 {
68     if (fAlpha == 0)
69         // This special case has no perfectly sane solution. We'll just pretend that fAlpha is very, very small but non-zero.
70         fAlpha = ALPHA_EPSILON;
71     fGray /= fAlpha;
72 }
AlphaUnPremultiply(float & fRed,float & fGreen,float & fBlue,float fAlpha)73 inline void AlphaUnPremultiply(float& fRed, float& fGreen, float& fBlue, float fAlpha)
74 {
75     if (fAlpha == 0)
76         // This special case has no perfectly sane solution. We'll just pretend that fAlpha is very, very small but non-zero.
77         fAlpha = ALPHA_EPSILON;
78     fRed   /= fAlpha;
79     fGreen /= fAlpha;
80     fBlue  /= fAlpha;
81 }
82 
AlphaPremultiply(RGBColour & colour,float fAlpha)83 void AlphaPremultiply(RGBColour& colour, float fAlpha)
84 {
85     AlphaPremultiply(colour.red(), colour.green(), colour.blue(), fAlpha);
86 }
87 
AlphaUnPremultiply(RGBColour & colour,float fAlpha)88 void AlphaUnPremultiply(RGBColour& colour, float fAlpha)
89 {
90     AlphaUnPremultiply(colour.red(), colour.green(), colour.blue(), fAlpha);
91 }
92 
AlphaPremultiply(RGBFTColour & colour)93 void AlphaPremultiply(RGBFTColour& colour)
94 {
95     AlphaPremultiply(colour.red(), colour.green(), colour.blue(), colour.FTtoA());
96 }
97 
AlphaUnPremultiply(RGBFTColour & colour)98 void AlphaUnPremultiply(RGBFTColour& colour)
99 {
100     AlphaUnPremultiply(colour.red(), colour.green(), colour.blue(), colour.FTtoA());
101 }
102 
AlphaPremultiply(RGBTColour & colour)103 void AlphaPremultiply(RGBTColour& colour)
104 {
105     AlphaPremultiply(colour.red(), colour.green(), colour.blue(), colour.alpha());
106 }
107 
AlphaUnPremultiply(RGBTColour & colour)108 void AlphaUnPremultiply(RGBTColour& colour)
109 {
110     AlphaUnPremultiply(colour.red(), colour.green(), colour.blue(), colour.alpha());
111 }
112 
113 
SetEncodedGrayValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int gray)114 void SetEncodedGrayValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int gray)
115 {
116     if (!img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
117         // avoid potential re-quantization in case we have a pretty match between encoded data and container
118         img->SetGrayValue(x, y, gray);
119     else
120         img->SetGrayValue(x, y, IntDecode(g,gray,max));
121 }
SetEncodedGrayAValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int gray,unsigned int alpha,bool premul)122 void SetEncodedGrayAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int gray, unsigned int alpha, bool premul)
123 {
124     bool doPremultiply   = (alpha != max) && !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be
125     bool doUnPremultiply = (alpha != max) && premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
126     if (!doPremultiply && !doUnPremultiply && !img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
127         // avoid potential re-quantization in case we have a pretty match between encoded data and container
128         img->SetGrayAValue(x, y, gray, alpha);
129     else
130     {
131         float fAlpha = IntDecode(alpha,max);
132         float fGray  = IntDecode(g,gray,max);
133         if (doPremultiply)
134             AlphaPremultiply(fGray, fAlpha);
135         else if (doUnPremultiply)
136             AlphaUnPremultiply(fGray, fAlpha);
137         // else no need to worry about premultiplication
138         img->SetGrayAValue(x, y, fGray, fAlpha);
139     }
140 }
SetEncodedRGBValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int red,unsigned int green,unsigned int blue)141 void SetEncodedRGBValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int red, unsigned int green, unsigned int blue)
142 {
143     if (!img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
144         // avoid potential re-quantization in case we have a pretty match between encoded data and container
145         img->SetRGBValue(x, y, red, green, blue);
146     else
147         img->SetRGBValue(x, y, IntDecode(g,red,max), IntDecode(g,green,max), IntDecode(g,blue,max));
148 }
SetEncodedRGBAValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int red,unsigned int green,unsigned int blue,unsigned int alpha,bool premul)149 void SetEncodedRGBAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha, bool premul)
150 {
151     bool doPremultiply   = (alpha != max) && !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be
152     bool doUnPremultiply = (alpha != max) && premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
153     if (!doPremultiply && !doUnPremultiply && !img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
154         // avoid potential re-quantization in case we have a pretty match between encoded data and container
155         img->SetRGBAValue(x, y, red, green, blue, alpha);
156     else
157     {
158         float fAlpha = IntDecode(alpha,  max);
159         float fRed   = IntDecode(g,red,  max);
160         float fGreen = IntDecode(g,green,max);
161         float fBlue  = IntDecode(g,blue, max);
162         if (doPremultiply)
163             AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
164         else if (doUnPremultiply)
165             AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha);
166         // else no need to worry about premultiplication
167         img->SetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
168     }
169 }
SetEncodedGrayValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float fGray)170 void SetEncodedGrayValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float fGray)
171 {
172     img->SetGrayValue(x, y, GammaCurve::Decode(g,fGray));
173 }
SetEncodedGrayAValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float fGray,float fAlpha,bool premul)174 void SetEncodedGrayAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float fGray, float fAlpha, bool premul)
175 {
176     bool doPremultiply   = !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be
177     bool doUnPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
178     fGray = GammaCurve::Decode(g,fGray);
179     if (doPremultiply)
180         AlphaPremultiply(fGray, fAlpha);
181     else if (doUnPremultiply)
182         AlphaUnPremultiply(fGray, fAlpha);
183     // else no need to worry about premultiplication
184     img->SetGrayAValue(x, y, fGray, fAlpha);
185 }
SetEncodedRGBValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float red,float green,float blue)186 void SetEncodedRGBValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float red, float green, float blue)
187 {
188     img->SetRGBValue(x, y, GammaCurve::Decode(g,red), GammaCurve::Decode(g,green), GammaCurve::Decode(g,blue));
189 }
SetEncodedRGBAValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float fRed,float fGreen,float fBlue,float fAlpha,bool premul)190 void SetEncodedRGBAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float fRed, float fGreen, float fBlue, float fAlpha, bool premul)
191 {
192     bool doPremultiply   = !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be
193     bool doUnPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
194     fRed   = GammaCurve::Decode(g,fRed);
195     fGreen = GammaCurve::Decode(g,fGreen);
196     fBlue  = GammaCurve::Decode(g,fBlue);
197     if (doPremultiply)
198         AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
199     else if (doUnPremultiply)
200         AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha);
201     // else no need to worry about premultiplication
202     img->SetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
203 }
SetEncodedRGBValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,const RGBColour & col)204 void SetEncodedRGBValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, const RGBColour& col)
205 {
206     img->SetRGBValue(x, y, GammaCurve::Decode(g,col.red()), GammaCurve::Decode(g,col.green()), GammaCurve::Decode(g,col.blue()));
207 }
208 
GetEncodedGrayValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,DitherStrategy & dh)209 unsigned int GetEncodedGrayValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, DitherStrategy& dh)
210 {
211     float fGray;
212     if (!img->IsPremultiplied() && img->HasTransparency())
213     {
214         // data has transparency and is stored non-premultiplied; precompose against a black background
215         float fAlpha;
216         img->GetGrayAValue(x, y, fGray, fAlpha);
217         AlphaPremultiply(fGray, fAlpha);
218     }
219     else
220     {
221         // no need to worry about premultiplication
222         fGray = img->GetGrayValue(x, y);
223     }
224     DitherStrategy::ColourOffset linOff, encOff;
225     dh.GetOffset(x,y,linOff,encOff);
226     unsigned int iGray = IntEncode(g, fGray, max, encOff.gray, linOff.gray);
227     dh.SetError(x,y,linOff);
228     return iGray;
229 }
GetEncodedGrayAValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int & gray,unsigned int & alpha,DitherStrategy & dh,bool premul)230 void GetEncodedGrayAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int& gray, unsigned int& alpha, DitherStrategy& dh, bool premul)
231 {
232     bool doPremultiply   = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't
233     bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
234     float fGray, fAlpha;
235     img->GetGrayAValue(x, y, fGray, fAlpha);
236     if (doPremultiply)
237     {
238         AlphaPremultiply(fGray, fAlpha);
239     }
240     else if (doUnPremultiply)
241     {
242         // Data has been stored premultiplied, but should be encoded non-premultiplied.
243         // Clipping will happen /before/ re-multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping
244         // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects;
245         // compensate for this by boosting opacity of any exceptionally bright pixels.
246         if (fGray > fAlpha)
247             fAlpha = min(1.0f, fGray);
248         // Need to convert from premultiplied to non-premultiplied encoding.
249         AlphaUnPremultiply(fGray, fAlpha);
250     }
251     else if (!premul)
252     {
253         // Data has been stored un-premultiplied and should be encoded that way.
254         // Clipping will happen /before/ multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping
255         // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects;
256         // compensate for this by boosting opacity of any exceptionally bright pixels.
257         if (fGray > 1.0)
258         {
259             float fFactor = fGray;
260             if (fFactor * fAlpha > 1.0)
261                 fFactor = 1.0/fAlpha;
262             // this keeps the product of alpha*color constant
263             fAlpha *= fFactor;
264             fGray  /= fFactor;
265         }
266         // No need for converting between premultiplied and un-premultiplied encoding.
267     }
268     // else no need to worry about premultiplication
269     DitherStrategy::ColourOffset linOff, encOff;
270     dh.GetOffset(x,y,linOff,encOff);
271     gray  = IntEncode(g, fGray,  max, encOff.gray,  linOff.gray);
272     alpha = IntEncode(   fAlpha, max, encOff.alpha, linOff.alpha);
273     dh.SetError(x,y,linOff);
274 }
GetEncodedRGBValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int & red,unsigned int & green,unsigned int & blue,DitherStrategy & dh)275 void GetEncodedRGBValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int& red, unsigned int& green, unsigned int& blue, DitherStrategy& dh)
276 {
277     float fRed, fGreen, fBlue;
278     if (!img->IsPremultiplied() && img->HasTransparency())
279     {
280         float fAlpha;
281         // data has transparency and is stored non-premultiplied; precompose against a black background
282         img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
283         AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
284     }
285     else
286     {
287         // no need to worry about premultiplication
288         img->GetRGBValue(x, y, fRed, fGreen, fBlue);
289     }
290     DitherStrategy::ColourOffset linOff, encOff;
291     dh.GetOffset(x,y,linOff,encOff);
292     red   = IntEncode(g, fRed,   max, encOff.red,   linOff.red);
293     green = IntEncode(g, fGreen, max, encOff.green, linOff.green);
294     blue  = IntEncode(g, fBlue,  max, encOff.blue,  linOff.blue);
295     dh.SetError(x,y,linOff);
296 }
GetEncodedRGBAValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int & red,unsigned int & green,unsigned int & blue,unsigned int & alpha,DitherStrategy & dh,bool premul)297 void GetEncodedRGBAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int& red, unsigned int& green, unsigned int& blue, unsigned int& alpha, DitherStrategy& dh, bool premul)
298 {
299     bool doPremultiply   = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't
300     bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
301     float fRed, fGreen, fBlue, fAlpha;
302     img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
303     if (doPremultiply)
304     {
305         // Data has been stored premultiplied, but should be encoded non-premultiplied.
306         // No need for special handling of color components greater than alpha.
307         // Need to convert from premultiplied to non-premultiplied encoding.
308         AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
309     }
310     else if (doUnPremultiply)
311     {
312         // Data has been stored premultiplied, but should be encoded non-premultiplied.
313         // Clipping will happen /before/ re-multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping
314         // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects;
315         // compensate for this by boosting opacity of any exceptionally bright pixels.
316         float fBright = RGBColour(fRed, fGreen, fBlue).Greyscale();
317         if (fBright > fAlpha)
318             fAlpha = min(1.0f, fBright);
319         // Need to convert from premultiplied to non-premultiplied encoding.
320         AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha);
321     }
322     else if (!premul)
323     {
324         // Data has been stored un-premultiplied and should be encoded that way.
325         // Clipping will happen /before/ multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping
326         // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects;
327         // compensate for this by boosting opacity of any exceptionally bright pixels.
328         float fBright = RGBColour(fRed, fGreen, fBlue).Greyscale();
329         if (fBright > 1.0)
330         {
331             float fFactor = fBright;
332             if (fFactor * fAlpha > 1.0)
333                 fFactor = 1.0/fAlpha;
334             // this keeps the product of alpha*color constant
335             fAlpha *= fFactor;
336             fRed   /= fFactor;
337             fGreen /= fFactor;
338             fBlue  /= fFactor;
339         }
340         // No need for converting between premultiplied and un-premultiplied encoding.
341     }
342     // else no need to worry about premultiplication
343     DitherStrategy::ColourOffset linOff, encOff;
344     dh.GetOffset(x,y,linOff,encOff);
345     red   = IntEncode(g, fRed,   max, encOff.red,   linOff.red);
346     green = IntEncode(g, fGreen, max, encOff.green, linOff.green);
347     blue  = IntEncode(g, fBlue,  max, encOff.blue,  linOff.blue);
348     alpha = IntEncode(   fAlpha, max, encOff.alpha, linOff.alpha);
349     dh.SetError(x,y,linOff);
350 }
351 
GetEncodedGrayValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g)352 float GetEncodedGrayValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g)
353 {
354     float fGray;
355     if (!img->IsPremultiplied() && img->HasTransparency())
356     {
357         // data has transparency and is stored non-premultiplied; precompose against a black background
358         float fAlpha;
359         img->GetGrayAValue(x, y, fGray, fAlpha);
360         AlphaPremultiply(fGray, fAlpha);
361     }
362     else
363     {
364         // no need to worry about premultiplication
365         fGray = img->GetGrayValue(x, y);
366     }
367     return GammaCurve::Encode(g,fGray);
368 }
GetEncodedGrayAValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float & fGray,float & fAlpha,bool premul)369 void GetEncodedGrayAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float& fGray, float& fAlpha, bool premul)
370 {
371     bool doPremultiply   = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't
372     bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
373     img->GetGrayAValue(x, y, fGray, fAlpha);
374     if (doPremultiply)
375         AlphaPremultiply(fGray, fAlpha);
376     else if (doUnPremultiply)
377         AlphaUnPremultiply(fGray, fAlpha);
378     // else no need to worry about premultiplication
379     fGray = GammaCurve::Encode(g,fGray);
380 }
GetEncodedRGBValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float & fRed,float & fGreen,float & fBlue)381 void GetEncodedRGBValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float& fRed, float& fGreen, float& fBlue)
382 {
383     if (!img->IsPremultiplied() && img->HasTransparency())
384     {
385         // data has transparency and is stored non-premultiplied; precompose against a black background
386         float fAlpha;
387         img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
388         AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
389     }
390     else
391     {
392         // no need to worry about premultiplication
393         img->GetRGBValue(x, y, fRed, fGreen, fBlue);
394     }
395     fRed   = GammaCurve::Encode(g,fRed);
396     fGreen = GammaCurve::Encode(g,fGreen);
397     fBlue  = GammaCurve::Encode(g,fBlue);
398 }
GetEncodedRGBAValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float & fRed,float & fGreen,float & fBlue,float & fAlpha,bool premul)399 void GetEncodedRGBAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float& fRed, float& fGreen, float& fBlue, float& fAlpha, bool premul)
400 {
401     bool doPremultiply   = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't
402     bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
403     img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
404     if (doPremultiply)
405         AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
406     else if (doUnPremultiply)
407         AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha);
408     // else no need to worry about premultiplication
409     fRed   = GammaCurve::Encode(g,fRed);
410     fGreen = GammaCurve::Encode(g,fGreen);
411     fBlue  = GammaCurve::Encode(g,fBlue);
412 }
GetEncodedRGBValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,RGBColour & col)413 void GetEncodedRGBValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, RGBColour& col)
414 {
415     if (!img->IsPremultiplied() && img->HasTransparency())
416     {
417         // data has transparency and is stored non-premultiplied; precompose against a black background
418         float fAlpha;
419         img->GetRGBAValue(x, y, col.red(), col.green(), col.blue(), fAlpha);
420         AlphaPremultiply(col, fAlpha);
421     }
422     else
423     {
424         // no need to worry about premultiplication
425         img->GetRGBValue(x, y, col.red(), col.green(), col.blue());
426     }
427     col.red()   = GammaCurve::Encode(g,col.red());
428     col.green() = GammaCurve::Encode(g,col.green());
429     col.blue()  = GammaCurve::Encode(g,col.blue());
430 }
431 
432 }
433