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