1 /*******************************************************************************
2 * encoding.cpp
3 *
4 * ---------------------------------------------------------------------------
5 * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
6 * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
7 *
8 * POV-Ray is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as
10 * published by the Free Software Foundation, either version 3 of the
11 * License, or (at your option) any later version.
12 *
13 * POV-Ray is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * ---------------------------------------------------------------------------
21 * POV-Ray is based on the popular DKB raytracer version 2.12.
22 * DKBTrace was originally written by David K. Buck.
23 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
24 * ---------------------------------------------------------------------------
25 * $File: //depot/public/povray/3.x/source/base/image/encoding.cpp $
26 * $Revision: #1 $
27 * $Change: 6069 $
28 * $DateTime: 2013/11/06 11:59:40 $
29 * $Author: chrisc $
30 *******************************************************************************/
31
32 // configbase.h must always be the first POV file included within base *.cpp files
33 #include "base/configbase.h"
34 #include "base/image/encoding.h"
35 #include "base/image/image.h"
36 #include "base/povmsgid.h"
37
38 // this must be the last file included
39 #include "base/povdebug.h"
40
41 namespace pov_base
42 {
43
44 /*******************************************************************************/
45
46 #define ALPHA_EPSILON 1.0e-6
47
48 static const unsigned int MaxBayerMatrixSize = 4;
49 typedef float BayerMatrix[MaxBayerMatrixSize][MaxBayerMatrixSize];
50
51 static const BayerMatrix BayerMatrices[MaxBayerMatrixSize+1] =
52 {
53 // dummy for 0x0
54 { { 0 } },
55 // 1x1 (of little use, but here it is)
56 { { 1/2.0-0.5 } },
57 // 2x2
58 { { 1/4.0-0.5, 3/4.0-0.5 },
59 { 4/4.0-0.5, 2/4.0-0.5 } },
60 // 3x3
61 { { 3/9.0-0.5, 7/9.0-0.5, 4/9.0-0.5 },
62 { 6/9.0-0.5, 1/9.0-0.5, 9/9.0-0.5 },
63 { 2/9.0-0.5, 8/9.0-0.5, 5/9.0-0.5 } },
64 // 4x4
65 { { 1/16.0-0.5, 9/16.0-0.5, 3/16.0-0.5, 11/16.0-0.5 },
66 { 13/16.0-0.5, 5/16.0-0.5, 15/16.0-0.5, 7/16.0-0.5 },
67 { 4/16.0-0.5, 12/16.0-0.5, 2/16.0-0.5, 10/16.0-0.5 },
68 { 16/16.0-0.5, 8/16.0-0.5, 14/16.0-0.5, 6/16.0-0.5 } }
69 };
70
71 /*******************************************************************************/
72
73 /// Class representing "no-op" dithering rules.
74 class NoDither : public DitherHandler
75 {
76 public:
77 virtual void getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt);
78 };
79
80 /// Class representing bayer dithering rules, generating a regular pattern.
81 class BayerDither : public DitherHandler
82 {
83 public:
84 BayerDither(unsigned int mxSize);
85 virtual void getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt);
getOffset(unsigned int x,unsigned int y,unsigned int ms)86 static inline float getOffset(unsigned int x, unsigned int y, unsigned int ms) { return BayerMatrices[ms][x%ms][y%ms]; }
87 protected:
88 OffsetInfo lastErr;
89 int matrixSize;
90 };
91
92 /// Class representing simple 1D error diffusion dithering rules, carrying over the error from one pixel to the next.
93 class DiffusionDither1D : public DitherHandler
94 {
95 public:
96 virtual void getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt);
97 virtual void setError(unsigned int x, unsigned int y, const OffsetInfo& err);
98 protected:
99 OffsetInfo lastErr;
100 };
101
102 /// Class representing simple 2D error diffusion dithering rules, carrying over the error from one pixel to the right, as well as the two pixels below.
103 /// @note This implementation uses an additional 1-line pixel buffer to avoid manipulating the original image.
104 class DiffusionDither2D : public DitherHandler
105 {
106 public:
107 DiffusionDither2D(unsigned int width);
108 virtual ~DiffusionDither2D();
109 virtual void getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt);
110 virtual void setError(unsigned int x, unsigned int y, const OffsetInfo& err);
111 protected:
112 unsigned int imageWidth;
113 OffsetInfo* nextRowOffset;
114 OffsetInfo* thisRowOffset;
115 };
116
117 /// Class representing Floyd-Steinberg dithering rules, carrying over the error from one pixel to the right, as well as the three pixels below.
118 /// @note This implementation uses an additional 1-line pixel buffer to avoid manipulating the original image.
119 class FloydSteinbergDither : public DitherHandler
120 {
121 public:
122 FloydSteinbergDither(unsigned int width);
123 virtual ~FloydSteinbergDither();
124 virtual void getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt);
125 virtual void setError(unsigned int x, unsigned int y, const OffsetInfo& err);
126 protected:
127 unsigned int imageWidth;
128 OffsetInfo* nextRowOffset;
129 OffsetInfo* thisRowOffset;
130 };
131
132 /*******************************************************************************/
133
getOffset(unsigned int x,unsigned int y,OffsetInfo & offLin,OffsetInfo & offQnt)134 void NoDither::getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt)
135 {
136 offLin.clear();
137 offQnt.clear();
138 }
139
140 /*******************************************************************************/
141
BayerDither(unsigned int mxSize)142 BayerDither::BayerDither(unsigned int mxSize) :
143 matrixSize(min(mxSize,MaxBayerMatrixSize))
144 {
145 ;
146 }
147
getOffset(unsigned int x,unsigned int y,OffsetInfo & offLin,OffsetInfo & offQnt)148 void BayerDither::getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt)
149 {
150 offLin.clear();
151 offQnt.setAll(getOffset(x, y, matrixSize));
152 }
153
154 /*******************************************************************************/
155
getOffset(unsigned int x,unsigned int y,OffsetInfo & offLin,OffsetInfo & offQnt)156 void DiffusionDither1D::getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt)
157 {
158 offLin = lastErr; lastErr.clear(); offQnt.clear();
159 }
160
setError(unsigned int x,unsigned int y,const OffsetInfo & err)161 void DiffusionDither1D::setError(unsigned int x, unsigned int y, const OffsetInfo& err)
162 {
163 lastErr = err;
164 }
165
166 /*******************************************************************************/
167
DiffusionDither2D(unsigned int width)168 DiffusionDither2D::DiffusionDither2D(unsigned int width) :
169 imageWidth(width),
170 thisRowOffset(new OffsetInfo[width+1]),
171 nextRowOffset(new OffsetInfo[width+1])
172 {
173 ;
174 }
175
~DiffusionDither2D()176 DiffusionDither2D::~DiffusionDither2D()
177 {
178 delete[] thisRowOffset;
179 delete[] nextRowOffset;
180 }
181
getOffset(unsigned int x,unsigned int y,OffsetInfo & offLin,OffsetInfo & offQnt)182 void DiffusionDither2D::getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt)
183 {
184 offLin = thisRowOffset[x];
185 offQnt.clear();
186 }
187
setError(unsigned int x,unsigned int y,const OffsetInfo & err)188 void DiffusionDither2D::setError(unsigned int x, unsigned int y, const OffsetInfo& err)
189 {
190 if (x == 0)
191 {
192 OffsetInfo* tmp = nextRowOffset;
193 nextRowOffset = thisRowOffset;
194 thisRowOffset = tmp;
195 for (unsigned int i = 0; i < imageWidth+1; i ++)
196 nextRowOffset[i].clear();
197 }
198 thisRowOffset[x+1] += err * (2/4.0); // pixel to the right
199 nextRowOffset[x] += err * (1/4.0); // pixel below
200 nextRowOffset[x+1] += err * (1/4.0); // pixel below right
201 }
202
203 /*******************************************************************************/
204
FloydSteinbergDither(unsigned int width)205 FloydSteinbergDither::FloydSteinbergDither(unsigned int width) :
206 imageWidth(width),
207 thisRowOffset(new OffsetInfo[width+2]),
208 nextRowOffset(new OffsetInfo[width+2])
209 {
210 ;
211 }
212
~FloydSteinbergDither()213 FloydSteinbergDither::~FloydSteinbergDither()
214 {
215 delete[] thisRowOffset;
216 delete[] nextRowOffset;
217 }
218
getOffset(unsigned int x,unsigned int y,OffsetInfo & offLin,OffsetInfo & offQnt)219 void FloydSteinbergDither::getOffset(unsigned int x, unsigned int y, OffsetInfo& offLin, OffsetInfo& offQnt)
220 {
221 offLin = thisRowOffset[x+1];
222 offQnt.clear();
223 }
224
setError(unsigned int x,unsigned int y,const OffsetInfo & err)225 void FloydSteinbergDither::setError(unsigned int x, unsigned int y, const OffsetInfo& err)
226 {
227 if (x == 0)
228 {
229 OffsetInfo* tmp = nextRowOffset;
230 nextRowOffset = thisRowOffset;
231 thisRowOffset = tmp;
232 for (unsigned int i = 0; i < imageWidth+2; i ++)
233 nextRowOffset[i].clear();
234 }
235 thisRowOffset[x+2] += err * (7/16.0); // pixel to the right
236 nextRowOffset[x] += err * (3/16.0); // pixel below left
237 nextRowOffset[x+1] += err * (5/16.0); // pixel below
238 nextRowOffset[x+2] += err * (1/16.0); // pixel below right
239 }
240
241 /*******************************************************************************/
242
GetDitherHandler(int method,unsigned int imageWidth)243 DitherHandlerPtr GetDitherHandler(int method, unsigned int imageWidth)
244 {
245 switch (method)
246 {
247 case kPOVList_DitherMethod_None: return DitherHandlerPtr(new NoDither());
248 case kPOVList_DitherMethod_Diffusion1D: return DitherHandlerPtr(new DiffusionDither1D());
249 case kPOVList_DitherMethod_Diffusion2D: return DitherHandlerPtr(new DiffusionDither2D(imageWidth));
250 case kPOVList_DitherMethod_FloydSteinberg: return DitherHandlerPtr(new FloydSteinbergDither(imageWidth));
251 case kPOVList_DitherMethod_Bayer2x2: return DitherHandlerPtr(new BayerDither(2));
252 case kPOVList_DitherMethod_Bayer3x3: return DitherHandlerPtr(new BayerDither(3));
253 case kPOVList_DitherMethod_Bayer4x4: return DitherHandlerPtr(new BayerDither(4));
254 default: throw POV_EXCEPTION_STRING("Invalid dither method for output");
255 }
256 }
257
GetNoOpDitherHandler()258 DitherHandlerPtr GetNoOpDitherHandler()
259 {
260 return DitherHandlerPtr(new NoDither());
261 }
262
263 /*******************************************************************************/
264
GetDitherOffset(unsigned int x,unsigned int y)265 float GetDitherOffset(unsigned int x, unsigned int y)
266 {
267 return BayerDither::getOffset(x,y,4);
268 }
269
270 /*******************************************************************************/
271
AlphaPremultiply(float & fGray,float fAlpha)272 inline void AlphaPremultiply(float& fGray, float fAlpha)
273 {
274 fGray *= fAlpha;
275 }
AlphaPremultiply(float & fRed,float & fGreen,float & fBlue,float fAlpha)276 inline void AlphaPremultiply(float& fRed, float& fGreen, float& fBlue, float fAlpha)
277 {
278 fRed *= fAlpha;
279 fGreen *= fAlpha;
280 fBlue *= fAlpha;
281 }
AlphaUnPremultiply(float & fGray,float fAlpha)282 inline void AlphaUnPremultiply(float& fGray, float fAlpha)
283 {
284 if (fAlpha == 0)
285 // This special case has no perfectly sane solution. We'll just pretend that fAlpha is very, very small but non-zero.
286 fAlpha = ALPHA_EPSILON;
287 fGray /= fAlpha;
288 }
AlphaUnPremultiply(float & fRed,float & fGreen,float & fBlue,float fAlpha)289 inline void AlphaUnPremultiply(float& fRed, float& fGreen, float& fBlue, float fAlpha)
290 {
291 if (fAlpha == 0)
292 // This special case has no perfectly sane solution. We'll just pretend that fAlpha is very, very small but non-zero.
293 fAlpha = ALPHA_EPSILON;
294 fRed /= fAlpha;
295 fGreen /= fAlpha;
296 fBlue /= fAlpha;
297 }
298
SetEncodedGrayValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int gray)299 void SetEncodedGrayValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int gray)
300 {
301 if (!img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
302 // avoid potential re-quantization in case we have a pretty match between encoded data and container
303 img->SetGrayValue(x, y, gray);
304 else
305 img->SetGrayValue(x, y, IntDecode(g,gray,max));
306 }
SetEncodedGrayAValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int gray,unsigned int alpha,bool premul)307 void SetEncodedGrayAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int gray, unsigned int alpha, bool premul)
308 {
309 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
310 bool doUnPremultiply = (alpha != max) && premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
311 if (!doPremultiply && !doUnPremultiply && !img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
312 // avoid potential re-quantization in case we have a pretty match between encoded data and container
313 img->SetGrayAValue(x, y, gray, alpha);
314 else
315 {
316 float fAlpha = IntDecode(alpha,max);
317 float fGray = IntDecode(g,gray,max);
318 if (doPremultiply)
319 AlphaPremultiply(fGray, fAlpha);
320 else if (doUnPremultiply)
321 AlphaUnPremultiply(fGray, fAlpha);
322 // else no need to worry about premultiplication
323 img->SetGrayAValue(x, y, fGray, fAlpha);
324 }
325 }
SetEncodedRGBValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int red,unsigned int green,unsigned int blue)326 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)
327 {
328 if (!img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
329 // avoid potential re-quantization in case we have a pretty match between encoded data and container
330 img->SetRGBValue(x, y, red, green, blue);
331 else
332 img->SetRGBValue(x, y, IntDecode(g,red,max), IntDecode(g,green,max), IntDecode(g,blue,max));
333 }
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)334 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)
335 {
336 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
337 bool doUnPremultiply = (alpha != max) && premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
338 if (!doPremultiply && !doUnPremultiply && !img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
339 // avoid potential re-quantization in case we have a pretty match between encoded data and container
340 img->SetRGBAValue(x, y, red, green, blue, alpha);
341 else
342 {
343 float fAlpha = IntDecode(alpha, max);
344 float fRed = IntDecode(g,red, max);
345 float fGreen = IntDecode(g,green,max);
346 float fBlue = IntDecode(g,blue, max);
347 if (doPremultiply)
348 AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
349 else if (doUnPremultiply)
350 AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha);
351 // else no need to worry about premultiplication
352 img->SetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
353 }
354 }
SetEncodedGrayValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float fGray)355 void SetEncodedGrayValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float fGray)
356 {
357 img->SetGrayValue(x, y, GammaCurve::Decode(g,fGray));
358 }
SetEncodedGrayAValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float fGray,float fAlpha,bool premul)359 void SetEncodedGrayAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float fGray, float fAlpha, bool premul)
360 {
361 bool doPremultiply = !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be
362 bool doUnPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
363 fGray = GammaCurve::Decode(g,fGray);
364 if (doPremultiply)
365 AlphaPremultiply(fGray, fAlpha);
366 else if (doUnPremultiply)
367 AlphaUnPremultiply(fGray, fAlpha);
368 // else no need to worry about premultiplication
369 img->SetGrayAValue(x, y, fGray, fAlpha);
370 }
SetEncodedRGBValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float red,float green,float blue)371 void SetEncodedRGBValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float red, float green, float blue)
372 {
373 img->SetRGBValue(x, y, GammaCurve::Decode(g,red), GammaCurve::Decode(g,green), GammaCurve::Decode(g,blue));
374 }
SetEncodedRGBAValue(Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float fRed,float fGreen,float fBlue,float fAlpha,bool premul)375 void SetEncodedRGBAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float fRed, float fGreen, float fBlue, float fAlpha, bool premul)
376 {
377 bool doPremultiply = !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be
378 bool doUnPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
379 fRed = GammaCurve::Decode(g,fRed);
380 fGreen = GammaCurve::Decode(g,fGreen);
381 fBlue = GammaCurve::Decode(g,fBlue);
382 if (doPremultiply)
383 AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
384 else if (doUnPremultiply)
385 AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha);
386 // else no need to worry about premultiplication
387 img->SetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
388 }
389
GetEncodedGrayValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,DitherHandler & dh)390 unsigned int GetEncodedGrayValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, DitherHandler& dh)
391 {
392 float fGray;
393 if (!img->IsPremultiplied() && img->HasTransparency())
394 {
395 // data has transparency and is stored non-premultiplied; precompose against a black background
396 float fAlpha;
397 img->GetGrayAValue(x, y, fGray, fAlpha);
398 AlphaPremultiply(fGray, fAlpha);
399 }
400 else
401 {
402 // no need to worry about premultiplication
403 fGray = img->GetGrayValue(x, y);
404 }
405 DitherHandler::OffsetInfo linOff, encOff;
406 dh.getOffset(x,y,linOff,encOff);
407 unsigned int iGray = IntEncode(g,fGray+linOff.gray,max,encOff.gray,linOff.gray);
408 dh.setError(x,y,linOff);
409 return iGray;
410 }
GetEncodedGrayAValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,unsigned int max,unsigned int & gray,unsigned int & alpha,DitherHandler & dh,bool premul)411 void GetEncodedGrayAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int& gray, unsigned int& alpha, DitherHandler& dh, bool premul)
412 {
413 bool doPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't
414 bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
415 float fGray, fAlpha;
416 img->GetGrayAValue(x, y, fGray, fAlpha);
417 if (doPremultiply)
418 {
419 AlphaPremultiply(fGray, fAlpha);
420 }
421 else if (doUnPremultiply)
422 {
423 // Data has been stored premultiplied, but should be encoded non-premultiplied.
424 // Clipping will happen /before/ re-multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping
425 // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects;
426 // compensate for this by boosting opacity of any exceptionally bright pixels.
427 if (fGray > fAlpha)
428 fAlpha = min(1.0f, fGray);
429 // Need to convert from premultiplied to non-premultiplied encoding.
430 AlphaUnPremultiply(fGray, fAlpha);
431 }
432 else if (!premul)
433 {
434 // Data has been stored un-premultiplied and should be encoded that way.
435 // Clipping will happen /before/ multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping
436 // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects;
437 // compensate for this by boosting opacity of any exceptionally bright pixels.
438 if (fGray > 1.0)
439 {
440 float fFactor = fGray;
441 if (fFactor * fAlpha > 1.0)
442 fFactor = 1.0/fAlpha;
443 // this keeps the product of alpha*color constant
444 fAlpha *= fFactor;
445 fGray /= fFactor;
446 }
447 // No need for converting between premultiplied and un-premultiplied encoding.
448 }
449 // else no need to worry about premultiplication
450 DitherHandler::OffsetInfo linOff, encOff;
451 dh.getOffset(x,y,linOff,encOff);
452 gray = IntEncode(g, fGray + linOff.gray, max, encOff.gray, linOff.gray);
453 alpha = IntEncode(fAlpha + linOff.alpha, max, encOff.alpha, linOff.alpha);
454 dh.setError(x,y,linOff);
455 }
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,DitherHandler & dh)456 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, DitherHandler& dh)
457 {
458 float fRed, fGreen, fBlue;
459 if (!img->IsPremultiplied() && img->HasTransparency())
460 {
461 float fAlpha;
462 // data has transparency and is stored non-premultiplied; precompose against a black background
463 img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
464 AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
465 }
466 else
467 {
468 // no need to worry about premultiplication
469 img->GetRGBValue(x, y, fRed, fGreen, fBlue);
470 }
471 DitherHandler::OffsetInfo linOff, encOff;
472 dh.getOffset(x,y,linOff,encOff);
473 red = IntEncode(g,fRed + linOff.red, max, encOff.red, linOff.red);
474 green = IntEncode(g,fGreen + linOff.green, max, encOff.green, linOff.green);
475 blue = IntEncode(g,fBlue + linOff.blue, max, encOff.blue, linOff.blue);
476 dh.setError(x,y,linOff);
477 }
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,DitherHandler & dh,bool premul)478 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, DitherHandler& dh, bool premul)
479 {
480 bool doPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't
481 bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
482 float fRed, fGreen, fBlue, fAlpha;
483 img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
484 if (doPremultiply)
485 {
486 // Data has been stored premultiplied, but should be encoded non-premultiplied.
487 // No need for special handling of color components greater than alpha.
488 // Need to convert from premultiplied to non-premultiplied encoding.
489 AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
490 }
491 else if (doUnPremultiply)
492 {
493 // Data has been stored premultiplied, but should be encoded non-premultiplied.
494 // Clipping will happen /before/ re-multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping
495 // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects;
496 // compensate for this by boosting opacity of any exceptionally bright pixels.
497 float fBright = RGBColour(fRed, fGreen, fBlue).greyscale();
498 if (fBright > fAlpha)
499 fAlpha = min(1.0f, fBright);
500 // Need to convert from premultiplied to non-premultiplied encoding.
501 AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha);
502 }
503 else if (!premul)
504 {
505 // Data has been stored un-premultiplied and should be encoded that way.
506 // Clipping will happen /before/ multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping
507 // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects;
508 // compensate for this by boosting opacity of any exceptionally bright pixels.
509 float fBright = RGBColour(fRed, fGreen, fBlue).greyscale();
510 if (fBright > 1.0)
511 {
512 float fFactor = fBright;
513 if (fFactor * fAlpha > 1.0)
514 fFactor = 1.0/fAlpha;
515 // this keeps the product of alpha*color constant
516 fAlpha *= fFactor;
517 fRed /= fFactor;
518 fGreen /= fFactor;
519 fBlue /= fFactor;
520 }
521 // No need for converting between premultiplied and un-premultiplied encoding.
522 }
523 // else no need to worry about premultiplication
524 DitherHandler::OffsetInfo linOff, encOff;
525 dh.getOffset(x,y,linOff,encOff);
526 red = IntEncode(g,fRed + linOff.red, max, encOff.red, linOff.red);
527 green = IntEncode(g,fGreen + linOff.green, max, encOff.green, linOff.green);
528 blue = IntEncode(g,fBlue + linOff.blue, max, encOff.blue, linOff.blue);
529 alpha = IntEncode(fAlpha + linOff.alpha, max, encOff.alpha, linOff.alpha);
530 dh.setError(x,y,linOff);
531 }
532
GetEncodedGrayValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g)533 float GetEncodedGrayValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g)
534 {
535 float fGray;
536 if (!img->IsPremultiplied() && img->HasTransparency())
537 {
538 // data has transparency and is stored non-premultiplied; precompose against a black background
539 float fAlpha;
540 img->GetGrayAValue(x, y, fGray, fAlpha);
541 AlphaPremultiply(fGray, fAlpha);
542 }
543 else
544 {
545 // no need to worry about premultiplication
546 fGray = img->GetGrayValue(x, y);
547 }
548 return GammaCurve::Encode(g,fGray);
549 }
GetEncodedGrayAValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float & fGray,float & fAlpha,bool premul)550 void GetEncodedGrayAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float& fGray, float& fAlpha, bool premul)
551 {
552 bool doPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't
553 bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
554 img->GetGrayAValue(x, y, fGray, fAlpha);
555 if (doPremultiply)
556 AlphaPremultiply(fGray, fAlpha);
557 else if (doUnPremultiply)
558 AlphaUnPremultiply(fGray, fAlpha);
559 // else no need to worry about premultiplication
560 fGray = GammaCurve::Encode(g,fGray);
561 }
GetEncodedRGBValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float & fRed,float & fGreen,float & fBlue)562 void GetEncodedRGBValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float& fRed, float& fGreen, float& fBlue)
563 {
564 if (!img->IsPremultiplied() && img->HasTransparency())
565 {
566 // data has transparency and is stored non-premultiplied; precompose against a black background
567 float fAlpha;
568 img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
569 AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
570 }
571 else
572 {
573 // no need to worry about premultiplication
574 img->GetRGBValue(x, y, fRed, fGreen, fBlue);
575 }
576 fRed = GammaCurve::Encode(g,fRed);
577 fGreen = GammaCurve::Encode(g,fGreen);
578 fBlue = GammaCurve::Encode(g,fBlue);
579 }
GetEncodedRGBAValue(const Image * img,unsigned int x,unsigned int y,const GammaCurvePtr & g,float & fRed,float & fGreen,float & fBlue,float & fAlpha,bool premul)580 void GetEncodedRGBAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float& fRed, float& fGreen, float& fBlue, float& fAlpha, bool premul)
581 {
582 bool doPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't
583 bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
584 img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha);
585 if (doPremultiply)
586 AlphaPremultiply(fRed, fGreen, fBlue, fAlpha);
587 else if (doUnPremultiply)
588 AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha);
589 // else no need to worry about premultiplication
590 fRed = GammaCurve::Encode(g,fRed);
591 fGreen = GammaCurve::Encode(g,fGreen);
592 fBlue = GammaCurve::Encode(g,fBlue);
593 }
594
595 }
596