@@ -89,8 +89,8 @@ class PNGOutput final : public ImageOutput {
89
89
}
90
90
91
91
template <class T >
92
- void deassociateAlpha (T* data, int size , int channels, int alpha_channel ,
93
- float gamma);
92
+ void deassociateAlpha (T* data, size_t npixels , int channels,
93
+ int alpha_channel, float gamma);
94
94
};
95
95
96
96
@@ -269,30 +269,33 @@ PNGOutput::close()
269
269
270
270
template <class T >
271
271
void
272
- PNGOutput::deassociateAlpha (T* data, int size , int channels, int alpha_channel ,
273
- float gamma)
272
+ PNGOutput::deassociateAlpha (T* data, size_t npixels , int channels,
273
+ int alpha_channel, float gamma)
274
274
{
275
- unsigned int max = std::numeric_limits<T>::max ();
276
275
if (gamma == 1 ) {
277
- for (int x = 0 ; x < size; ++x, data += channels)
278
- if (data[alpha_channel])
279
- for (int c = 0 ; c < channels; c++)
280
- if (c != alpha_channel) {
281
- unsigned int f = data[c];
282
- f = (f * max) / data[alpha_channel];
283
- data[c] = (T)std::min (max, f);
284
- }
276
+ for (size_t x = 0 ; x < npixels; ++x, data += channels) {
277
+ DataArrayProxy<T, float > val (data);
278
+ float alpha = val[alpha_channel];
279
+ if (alpha != 0 .0f && alpha != 1 .0f ) {
280
+ for (int c = 0 ; c < channels; c++) {
281
+ if (c != alpha_channel)
282
+ val[c] = data[c] / alpha;
283
+ }
284
+ }
285
+ }
285
286
} else {
286
- for (int x = 0 ; x < size; ++x, data += channels)
287
- if (data[alpha_channel]) {
287
+ for (size_t x = 0 ; x < npixels; ++x, data += channels) {
288
+ DataArrayProxy<T, float > val (data);
289
+ float alpha = val[alpha_channel];
290
+ if (alpha != 0 .0f && alpha != 1 .0f ) {
288
291
// See associateAlpha() for an explanation.
289
- float alpha_deassociate = pow ((float )max / data[alpha_channel],
290
- gamma);
291
- for (int c = 0 ; c < channels; c++)
292
+ float alpha_deassociate = pow (1 .0f / val[alpha_channel], gamma);
293
+ for (int c = 0 ; c < channels; c++) {
292
294
if (c != alpha_channel)
293
- data [c] = static_cast <T>( std::min (
294
- max, ( unsigned int )(data[c] * alpha_deassociate)));
295
+ val [c] = val[c] * alpha_deassociate;
296
+ }
295
297
}
298
+ }
296
299
}
297
300
}
298
301
@@ -302,26 +305,42 @@ bool
302
305
PNGOutput::write_scanline (int y, int z, TypeDesc format, const void * data,
303
306
stride_t xstride)
304
307
{
305
- y -= m_spec.y ;
306
308
m_spec.auto_stride (xstride, format, spec ().nchannels );
307
309
const void * origdata = data;
310
+ if (format == TypeUnknown)
311
+ format = m_spec.format ;
312
+
313
+ // PNG specifically dictates unassociated (un-"premultiplied") alpha.
314
+ // If we need to unassociate alpha, do it in float.
315
+ std::unique_ptr<float []> unassoc_scratch;
316
+ if (m_convert_alpha) {
317
+ size_t nvals = size_t (m_spec.width ) * size_t (m_spec.nchannels );
318
+ float * floatvals = nullptr ;
319
+ if (nvals * sizeof (float ) <= (1 << 16 )) {
320
+ floatvals = OIIO_ALLOCA (float , nvals); // small enough for stack
321
+ } else {
322
+ unassoc_scratch.reset (new float [nvals]);
323
+ floatvals = unassoc_scratch.get ();
324
+ }
325
+ // Contiguize and convert to float
326
+ OIIO::convert_image (m_spec.nchannels , m_spec.width , 1 , 1 , data, format,
327
+ xstride, AutoStride, AutoStride, floatvals,
328
+ TypeFloat, AutoStride, AutoStride, AutoStride);
329
+ // Deassociate alpha
330
+ deassociateAlpha (floatvals, size_t (m_spec.width ), m_spec.nchannels ,
331
+ m_spec.alpha_channel , m_gamma);
332
+ data = floatvals;
333
+ format = TypeFloat;
334
+ xstride = size_t (m_spec.nchannels ) * sizeof (float );
335
+ }
336
+
308
337
data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z);
309
338
if (data == origdata && (m_convert_alpha || m_need_swap)) {
310
339
m_scratch.assign ((unsigned char *)data,
311
340
(unsigned char *)data + m_spec.scanline_bytes ());
312
341
data = &m_scratch[0 ];
313
342
}
314
343
315
- // PNG specifically dictates unassociated (un-"premultiplied") alpha
316
- if (m_convert_alpha) {
317
- if (m_spec.format == TypeDesc::UINT16)
318
- deassociateAlpha ((unsigned short *)data, m_spec.width ,
319
- m_spec.nchannels , m_spec.alpha_channel , m_gamma);
320
- else
321
- deassociateAlpha ((unsigned char *)data, m_spec.width ,
322
- m_spec.nchannels , m_spec.alpha_channel , m_gamma);
323
- }
324
-
325
344
// PNG is always big endian
326
345
if (m_need_swap)
327
346
swap_endian ((unsigned short *)data, m_spec.width * m_spec.nchannels );
@@ -354,27 +373,41 @@ PNGOutput::write_scanlines(int ybegin, int yend, int z, TypeDesc format,
354
373
m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels ,
355
374
m_spec.width , m_spec.height );
356
375
const void * origdata = data;
376
+ if (format == TypeUnknown)
377
+ format = m_spec.format ;
378
+
379
+ // PNG specifically dictates unassociated (un-"premultiplied") alpha.
380
+ // If we need to unassociate alpha, do it in float.
381
+ std::unique_ptr<float []> unassoc_scratch;
382
+ size_t npixels = size_t (m_spec.width ) * size_t (yend - ybegin);
383
+ size_t nvals = npixels * size_t (m_spec.nchannels );
384
+ if (m_convert_alpha) {
385
+ unassoc_scratch.reset (new float [nvals]);
386
+ float * floatvals = unassoc_scratch.get ();
387
+ // Contiguize and convert to float
388
+ OIIO::convert_image (m_spec.nchannels , m_spec.width , m_spec.height , 1 ,
389
+ data, format, xstride, ystride, AutoStride,
390
+ floatvals, TypeFloat, AutoStride, AutoStride,
391
+ AutoStride);
392
+ // Deassociate alpha
393
+ deassociateAlpha (floatvals, npixels, m_spec.nchannels ,
394
+ m_spec.alpha_channel , m_gamma);
395
+ data = floatvals;
396
+ format = TypeFloat;
397
+ xstride = size_t (m_spec.nchannels ) * sizeof (float );
398
+ ystride = xstride * size_t (m_spec.width );
399
+ zstride = ystride * size_t (m_spec.height );
400
+ }
401
+
357
402
data = to_native_rectangle (m_spec.x , m_spec.x + m_spec.width , ybegin, yend,
358
403
z, z + 1 , format, data, xstride, ystride,
359
- zstride, m_scratch);
360
- size_t npixels = m_spec.width * (yend - ybegin);
361
- size_t nvals = npixels * m_spec.nchannels ;
404
+ zstride, m_scratch, m_dither, 0 , ybegin, z);
362
405
if (data == origdata && (m_convert_alpha || m_need_swap)) {
363
406
m_scratch.assign ((unsigned char *)data,
364
407
(unsigned char *)data + nvals * m_spec.format .size ());
365
408
data = m_scratch.data ();
366
409
}
367
410
368
- // PNG specifically dictates unassociated (un-"premultiplied") alpha
369
- if (m_convert_alpha) {
370
- if (m_spec.format == TypeDesc::UINT16)
371
- deassociateAlpha ((unsigned short *)data, npixels, m_spec.nchannels ,
372
- m_spec.alpha_channel , m_gamma);
373
- else
374
- deassociateAlpha ((unsigned char *)data, npixels, m_spec.nchannels ,
375
- m_spec.alpha_channel , m_gamma);
376
- }
377
-
378
411
// PNG is always big endian
379
412
if (m_need_swap)
380
413
swap_endian ((unsigned short *)data, nvals);
0 commit comments