The problem in the code you pointed to can be improved by rounding the result (add 0x80 before shifting by 8).
result = S + D * (((256 - (A + (A>>7))) + 0x80) >> 8) What you're proposing is interesting, essentially: result = S + (D * (256 - A))>>8 Basically, this has a bigger error, but it is biased on the "opaque" side, so it never causes a pixel to become more transparent. It looks like it works very well for the premultiplied alpha case above. I'll put this on the list of stuff to investigate further :-) Thanks for the feedback. Mathias On Feb 27, 1:56 am, Eric <[email protected]> wrote: > Correct, that is why I mentioned it cannot be used in all cases. The > fact is that for some PorterDuff modes (see the SrcOver example > above ) the current implementation with the shift optimization > produces incorrect results because fully opaque pixels (with alpha > 255) that should remain fully opaque turn into partially transparent > pixels (with alpha 254). It is only a very slight difference but it > can be avoided. > > On Feb 27, 8:52 am, pixelflinger <[email protected]> wrote: > > > Hello, > > > If you compute the errors, that is: > > > a*float(256/255) - (a + (a>>7)) > > > and > > > a*float(256/255) - (a + 1) > > > you'll see that the former is a better choice; the maximum error is > > smaller (0.5 instead of 1.0) > > > The later is also incorrect per the OpenGL blending specification > > because it doesn't map "0" to "0" (you're lucky in that specific > > example, but if you were to dither the result, it wouldn't work). > > > Additionally, doing the actual computation in integer: > > > (a * 256)/255 > > > will give you the same result than with the equation used in skia. > > > Mathias > > > On Feb 26, 11:04 am, Eric <[email protected]> wrote: > > > > When using some of the PorterDuff modes in the Android SDK incorrect > > > alpha values are produced. For example the following: > > > > Bitmap bitmap = Bitmap.createBitmap(100, 100, > > > Bitmap.Config.ARGB_8888); > > > bitmap.eraseColor(0xff000000); // Black with alpha 255 > > > Canvas canvas = new Canvas(bitmap); > > > canvas.drawColor(0x80000000, PorterDuff.Mode.SRC_OVER); // Black with > > > alpha 128 > > > > results in 0xfe000000 so an alpha value of 254 instead of 255 > > > > After some digging in the source code I traced it back to incorrect > > > use of SkAlpha255To256 defined in > > > > platform/external/skia.git/include/core/SkColorPriv.h > > > > 34 static inline unsigned SkAlpha255To256(U8CPU alpha) { > > > 35 SkASSERT(SkToU8(alpha) == alpha); > > > 36 return alpha + (alpha >> 7); > > > 37 } > > > > It is frequently used in the PorterDuff blending functions, below is > > > SRC_OVER from: > > > > platform/external/skia.git/src/core/SkXfermode.cpp > > > > 347 // kSrcOver_Mode, //!< [Sa + (1 - Sa)*Da, Sc + (1 - Sa)*Dc] > > > 348 static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) { > > > 349 return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - > > > SkGetPackedA32(src))); > > > 350 } > > > > Running the above numbers for alpha it produces 128 + (255 * 127) >> 8 > > > = 254. Doing it without the shift optimization returns 128 + (255 * > > > 127) / 255 = 255. > > > > Fro some of these functions (not all) it would be better to use a > > > SkAlpha255To256 defined as > > > > static inline unsigned SkAlpha255To256(U8CPU alpha) { > > > SkASSERT(SkToU8(alpha) == alpha); > > > return alpha + 1; > > > > } > > > > which is not 100% accurate either but you can still use the shift > > > trick and it does produce correct result when alpha is 0 and when > > > alpha is 255. Any thoughts? --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "android-framework" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/android-framework?hl=en -~----------~----~----~----~------~----~------~--~---
