9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
From: Russ Cox <rsc@swtch.com>
To: Fans of the OS Plan 9 from Bell Labs <9fans@9fans.net>
Subject: Re: [9fans] Porter-Duff alpha blending
Date: Fri,  6 Mar 2009 12:48:31 -0800	[thread overview]
Message-ID: <dd6fe68a0903061248q70a02f56q7b350645fabed8e4@mail.gmail.com> (raw)
In-Reply-To: <14ec7b180903051921u10524f16naad654b297a67de3@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2786 bytes --]

Thanks for tracking this down and pointing out where
the error is.  There are actually a handful of errors
in that code.

Assume a source value sv, source alpha sa,
mask alpha ma, destination value dv, and
destination alpha da.  The values sv and dv
are stored premultiplied by their corresponding
alphas (the one true way).

Given those values, the correct new values
for the destination pixel in an S over D op are:

    dv = (sv*ma + dv*(255-sa*ma)) / 255
    da = (sa*ma + da*(255-sa*ma)) / 255

Bug #1: The current draw.c does the division
separately on the two halves:

    dv = (sv*ma)/255 + (dv*(255-sa*ma))/255

This can be off by one if the remainders from the
two divisions sum to >= 255.

Bug #2: The MUL0123 macro assumes four
values are packed into a 32-bit int and runs
them in simultaneous pairs as 32-bit operations
(MUL02 and MUL13) operating on 16-bit halves
of the word.  Those two don't use the right rounding
for the bitwise op implementation of /255.  On
a single value, the implementation is

    x / 255 == (t = x+1, (t+(t>>8))>>8)
    (x+127) / 255 == (t = x+128, (t+(t>>8))>>8)

These calculations only need 16 bits so you can
run two of them in the two halves of a 32-bit word.
The second implements round-to-nearest and
is what the draw code tries to do in this case.
But it only adds 128 (0x80), so it only rounds
the bottom half correctly.  It needs to add 0x00800080,
which would round both of them.  This explains:

src  rFF gFF bFF αFF
mask  kFF αFF
dst  r00 g00 b00 αFF
dst after calc  rFE gFE bFF αFF

Bug #3: MUL0123 is enabled whenever the src
and dst both have 32-bit pixel width, but there is
no check that the sub-channels are in the same
order.  You don't say what the image chans were
in your test, but this:

src  rFF g00 b00 αFF
mask  kFF αFF
dst  r00 g00 b00 αFF
dst after calc  rFE gFE b00 α00

would be explained by, say, src==ARGB and
dst==RGBA.  The A and R values in src became
the R and G chans in dst.  (In fact, since the dst
R and G are FE not FF, that's almost certainly
the scenario, modulo the little-endian draw names.)

This one is similarly explained:

src  rCC g00 b00 αCC
mask  kFF αFF
dst  r00 g00 b00 αFF
dst after calc  rCB gCB b00 α33

The destination got scaled by 0x33/0xFF,
leaving 00 00 00 33, and then the source,
cc 00 00 cc, was added in the wrong place,
using the incorrect rounding, to produce
cb cb 00 33.

I have corrected these bugs in the plan9port
copy of src/libmemdraw/draw.c and finally
get the right answer for Andrey's test (attached).
I leave it as an exercise to the interested reader to
port the changes to the other dozen copies
of libmemdraw that are floating around,
or to unify them all.

Russ

[-- Attachment #2: andrey.png --]
[-- Type: image/png, Size: 11450 bytes --]

  reply	other threads:[~2009-03-06 20:48 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-03-04 21:32 erik quanstrom
2009-03-04 21:37 ` andrey mirtchovski
2009-03-04 21:42   ` andrey mirtchovski
2009-03-04 21:44   ` erik quanstrom
2009-03-04 21:53     ` andrey mirtchovski
2009-03-04 22:03       ` erik quanstrom
2009-03-04 22:21         ` andrey mirtchovski
2009-03-04 22:22         ` Devon H. O'Dell
2009-03-04 22:45           ` andrey mirtchovski
2009-03-04 22:59             ` erik quanstrom
2009-03-04 23:05               ` andrey mirtchovski
2009-03-04 23:58             ` Charles Forsyth
2009-03-05  1:04               ` erik quanstrom
2009-03-05  1:29               ` andrey mirtchovski
2009-03-05  2:07                 ` andrey mirtchovski
2009-03-05  2:22                   ` erik quanstrom
2009-03-06  3:21                     ` andrey mirtchovski
2009-03-06 20:48                       ` Russ Cox [this message]
2009-03-06 21:42                         ` andrey mirtchovski
2009-03-08 22:33                           ` Jeff Sickel
2009-03-09 15:28                             ` Michaelian Ennis
2009-03-09 16:40                               ` Jeff Sickel
2009-03-09 14:00                         ` erik quanstrom
2009-03-09 15:32                           ` yy
2009-03-04 21:53 erik quanstrom
2009-03-04 22:01 ` andrey mirtchovski
2009-03-05 18:31 maht
2009-03-04 21:18 ` andrey mirtchovski
2009-03-04 23:24 ` Russ Cox

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=dd6fe68a0903061248q70a02f56q7b350645fabed8e4@mail.gmail.com \
    --to=rsc@swtch.com \
    --cc=9fans@9fans.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).