Hi Jay,

It looks like it will work, but there are some inconsistencies in the code that we should address and a couple of optimizations.

First, there is a mixture of using "int" and "jboolean" for the type of the new boolean parameter. It would be great if we could just declare it as jboolean in the structures, but it looks like those are limited to basic C types. It seems a little clunky to mix types like this, but I'd be interested in hearing what Phil says. I think it would be fine to just use int throughout.

I'd suggest a name change:
    representsPrimary => representsPrimaries
(since there are multiple primary colors)

The place to load the variable for testing in the ByteIndexed macros would be in the "InitByteIndexedStoreVarsY" macro where it sets up the "PREFIX ## InvLut" variable. It should also use a PREFIX. Look through those macros for wherever the InvLut value is declared and initialized, and follow a similar pattern for "PREFIX ## <somename>". You could use "representsPrimaries" for the name here too, but it could also be shortened to just "repPrims" or "optPrims" since the macros are all centralized here.

Once you do the preceding step, you can delete a lot of lines that pre-load the "representsPrimaries" in the calling macros, which should also eliminate a lot of files from the webrev.

In the code in ByteIndexed.h:StoreByteIndexedFrom3ByteRgb() that tests the variable, I'd just test "representsPrimary" with no "==" rather than comparing it to 1 since it is a boolean, not (really) an integer.

With respect to the new function that tests the primary matches (in ByteIndexed.c), some of the code is unnecessarily complicated:

- When you find a color match failure (lines 301, et al), you could just return false directly. As it stands, you set a variable and break, but the break only breaks out of 1 of 3 nested if statements, so it doesn't save any calculations. Just return false directly as a single failure means the answer is "no".

- your r, g, and b values are extracted from the color in the wrong order near line 285. r involves a shift of 16, and b involves no shift. I suppose this is paired with computing the dr and db values with the wrong indices or this technique wouldn't work, but it is clunky to name the variables inconsistently with the data they actually hold.

- you use parentheses on the left-hand side of an assignment when you extract the r, g, and b values (also near line 285). You don't need parentheses on that side of an assignment operator.

There are several things that are more complicated than they need to be with your testing lines. They use a lot more computations than you need. For instance:

- You test the i,j,k using a modulus operation (lines 298, 312, 326), but really you just need to know if it is 0 or not-0, so just test it for ==0 or !=0. - rather than using multiplication and modulus to assign the dr,dg,db values near line 291, why not just use "dr = (i == 0) ? 0 : 255;"? - the tests for range use a separate subtraction and a compare, when they could be inlined.

First, I'd get rid of the "represents_primary" variable entirely and then rewrite the whole tail end of the loop body from 291 to the end of the loop as:

if (i == 0) {
    if (r > delta) return false;
} else {
    if (r < 255-delta) return false;
}
// 2 more similar tests for j/g and k/b

Then "return true" at the end of the function if it reaches there. Keep in mind that the mapping of ijk to rgb might be affected if you fix the shifts used to extract the rgb components from color...

                        ...jim

On 4/20/2016 2:46 AM, Jayathirth D V wrote:
Hi Jim,

As discussed we will not add dithering error values to primary colors with 
color map which represents Primary colors approximately(+/-5 delta).
I have made changes based on this suggestion and added new function to 
calculate whether color map represents primary colors approximately or not.
If it represents only then we will avoid adding dithering error values in case 
ByteIndexed destination.

I have observed that in case of white color we are picking (252,252,252) or 
(246,246,246) and not (255,255,255) from colormap. What I have observed that 
out of 256 places we are storing RGB colors at start and then gray values. So 
we are picking final gray value and not in between white value. That's why I am 
not verifying white color validity in test case.

Please find updated webrev for review :
http://cr.openjdk.java.net/~jdv/7116979/webrev.01/

Thanks,
Jay

-----Original Message-----
From: Jim Graham
Sent: Saturday, February 20, 2016 3:02 AM
To: Jayathirth D V
Cc: 2d-dev@openjdk.java.net; Philip Race; Prasanta Sadhukhan
Subject: Re: [OpenJDK 2D-Dev] Review Request for JDK-7116979 : Unexpected pixel 
colour when converting images to TYPE_BYTE_INDEXED

Hi Jayathirth,

Thank you for attaching the detailed logs, but I don't have a program that can 
deal with the 7z files.  Can you summarize what was learned from them?

Also, the colormap you created, while subset, was a perfectly scaled cube.  I 
was referring more to a random colormap where you might have 2 colors close to 
a primary, but neither is on a line from the primary to the 0.5 gray value so 
each has a different hue.  That's something that can only be evaluated 
visually.  One example of randomized colormaps is when we display via X11 onto 
an 8-bit display.  That probably never happens any more, but we may not be able 
to allocate colors in the colormap in such a case and if another program has 
already allocated most of the dynamic colormap then we would not be able to add 
our own colors.

This could be dealt with by simply marking our standard colormap as having 
primaries, or by scanning any given colormap and marking it if it has 
primaries, and then only perform this code on colormaps with the primaries.  
Solving it for our own colormap (or solving it for any colormap that has all 
primaries) would likely solve it for 99% of the vanishingly small number of 
developers who might run into this.

But, in the end, what would we accomplish by adding this one very targeted fix?

I'm also curious where the push for this is coming from.  While we aren't 
perfect here, so much of the world today is focused on lossless image formats 
that I don't imagine there is much need for really good 8-bit indexed image 
support any more...?

If anything, spatial dithering is considered a very low quality algorithm and error 
propagation dithering would handle all of the colormaps much better.  We used to do error 
propagation dithering back in the JDK 1.1 days, but we dropped it when we introduced 2D 
because we added a bunch of ways to render small pieces of an image and only the current 
spatial dithering algorithm can be started in the middle of an operation.  That doesn't 
mean that you can't still do error propagation since many operations already operate on 
the full image any way and you can still handle subset image operations by either 
converting the full image first and then copying or by tracing the errors from the edge 
of the image to the point of the subimage operation.  We never bothered to upgrade our 
algorithms it just never seemed to be work that made sense to prioritize given that 
screens were overwhelmingly moving to full-color at the time of "Java 2" (and 
now to HDR in 2016+).  8-bit indexed isn't used much any more.

If we want to make 8-bit destination images work better we'd be better off 
adopting error propagation dithering again rather than trying to tune this 
algorithm for black.

It looks like the bug was closed briefly in 2014 and then reopened soon 
thereafter with no justification.  It is true that we still have this anomaly, 
but it isn't clear why this is high enough priority to work on in 2016 when 
other bit depths would work better for destination imagery and other dithering 
algorithms would work better for most customers and even this targeted fix 
isn't perfect...

                        ...jim

On 2/16/2016 8:32 AM, Jayathirth D V wrote:
Hi Jim,

Thanks for letting me know about the importance of custom color maps. I was not 
able to understand what you mentioned previously since I am new to this 
terminology.

Regarding performance I checked with image having complete 255,255,128 pixels 
and ran it for 100 times. I am running on Windows 7  Professional SP1 with 
Intel i5-5300U CPU. Overall I don't see much difference in time taken for 
drawImage() API before and after change. Details for INT_RGB image as input:

Before change :
Minimum time   536827ns
Maximum time  1560947ns
Average time      871167ns

After change :
Minimum time  536380ns
Maximum time 1468130ns
Average time     830778ns

There is +/- 10% delta both for before and after change time but there is no 
major gap. Even for image with other input type it holds the same.


Regarding custom color maps, previously I ran on default color map generated in 
TYPE_BYTE_INDEXED constructor. Now I tested with modified color map so that it 
doesn't contain any of the primary color values. Instead of using 0-255 values 
with value 51 increments in R,G,B components I used color map with  20-245 
values with value 45 as increment. For this color map before and after change 
all the pixels are referring to same index in color map irrespective of removal 
of error delta addition in case of primary colors.

I have attached log for Red and Green primary colors before and after change(Was not able 
to attach for more since it exceeded 120KB mailing list limit). Please search for 
"Final index value =" in the logs. Please let me know your inputs.

Thanks,
Jay

-----Original Message-----
From: Jim Graham
Sent: Tuesday, February 16, 2016 1:03 AM
To: Jayathirth D V
Cc: 2d-dev@openjdk.java.net; Philip Race; Prasanta Sadhukhan
Subject: Re: [OpenJDK 2D-Dev] Review Request for JDK-7116979 :
Unexpected pixel colour when converting images to TYPE_BYTE_INDEXED

Hi Jayathirth,

The issue with testing performance with white images is that the tests allow 
the code to eliminate 3 indexed fetches which take time.  Primary colors end up 
being the one obscure case where the new code might be faster.  But, 
non-primary colors would be slower by a fair amount, so performance testing 
with converting a randomized or non-primary-color image are the more telling 
case.  An image filled with 255,255,128 would be the worst case because it 
would involve all of the tests and still have to do all of the lookups.

My question about how this works with custom colormaps was not really addressed 
by your response.  A simple test to make sure the colormap has accurate (or 
very close) conversions for the primary colors may be enough to validate the 
colormap for the indicated tests.  If the colormap contains no pure conversions 
for some of the primary colors then they may need to be dithered anyway...

                        ...jim

On 2/15/16 3:39 AM, Jayathirth D V wrote:
Hi Jim,

I performed performance analysis with white image so that all
conditions are checked in the new branch added. There is no major
difference in time taken before and after change. For each input I
have tested time taken for drawImage() API to convert from every
format to Byte indexed type. For every unique conversion test is run
for 100 times. Please find the details:

Input

type

        

Min

before change

(ns)

        

Min

after change

(ns)

        

Max

before change

(ns)

        

Max

after change

(ns)

        

Average

before change

(ns)

        

Average

after

change

(ns)

INT_RGB

        

523437

        

481491

        

1230724

        

1270440

        

789452

        

666144

INT_ARGB

        

500232

        

493986

        

12406307

        

1308816

        

793384

        

654015

INT_ARGB_PRE

        

500233

        

492201

        

1037057

        

981277

        

710250

        

699214

INT_BGR

        

537716

        

562706

        

1446703

        

2046001

        

862377

        

863256

3BYTE_BGR

        

483275

        

481045

        

1181638

        

1384676

        

651427

        

580961

4BYTE_ABGR

        

544410

        

499786

        

1292305

        

968783

        

690106

        

683881

4BYTE_ABGR_PRE

        

504249

        

505588

        

1680086

        

1216445

        

756101

        

687750

USHORT_565_RGB

        

507818

        

505588

        

978153

        

1346746

        

652908

        

655782

USHORT_555_RGB

        

510496

        

509604

        

952272

        

1162004

        

650418

        

670811

BYTE_GRAY

        

481491

        

478367

        

1140585

        

1799231

        

691160

        

583250

USHORT_GRAY

        

506927

        

507373

        

1375751

        

1255267

        

728202

        

646902

BYTE_BINARY

        

541733

        

496217

        

1083466

        

959411

        

730527

        

728461

The changes are tested with plain images having primary colors like
RED, GREEN, BLUE, BLACK and WHITE.

Thanks,

Jay

-----Original Message-----
From: Jim Graham
Sent: Friday, February 12, 2016 4:05 AM
To: Jayathirth D V; 2d-dev@openjdk.java.net; Philip Race; Prasanta
Sadhukhan
Subject: Re: [OpenJDK 2D-Dev] Review Request for JDK-7116979 :
Unexpected pixel colour when converting images to TYPE_BYTE_INDEXED

Hi Jayathirth,

Did you do any performance analysis of this change?  You are adding 6
tests and multiple branches to per-pixel code.

The effectiveness of this technique depends on the colormap that we
have set up.  For the BufferedImage.TYPE_INDEXED constructor we
produce a fairly nice colormap, but if someone creates a custom
colormap then the colors could be anything.  We create a decent
inversion for just about any colormap, but that doesn't mean that
using only "the best match for solid red" will produce a better
result for a dithered approximation for red.  It is true that if
there is a really good match for red then we should just use that,
but if there are no direct matches for red then we may choose to
paint a red region with solid orange even though there is another
color in the colormap that when mixed with orange approximates a red
tone better.  For example, if a colormap contains no pure red, but

contains:

240, 20,  0

240,  0, 20

(I'm not sure if 20 is within our current error deltas that we use
for dithering, but this is an example not a test case.)

Then using one of these alone might skew the color towards orange or
purple.  Using both together in a dither pattern might keep the
overall hue impression as red, but with a small amount of noise in its 
saturation.

What types of colormaps was this tested with?

                                                  ...jim

On 2/11/16 6:37 AM, Jayathirth D V wrote:

Hi,



_Please review the following fix in JDK9:_



__



Bug :https://bugs.openjdk.java.net/browse/JDK-7116979



Webrev :http://cr.openjdk.java.net/~jdv/7116979/webrev.00/



Issue : When Image containing black pixels are converted from any

format to Byte Indexed format some of the pixels are not black. They

are following pattern similar to dithering.



Root cause : When we convert any format type to ByteIndexed we are

adding Error delta values to R,G,B components using dithering indices.

This is causing some pixels values to not point to proper index in

color table.



Solution : There is no need to add error delta for primary colors

containing basic values in R,G,B components. Exclude such pixels
from

delta addition.



Thanks,



Jay



Reply via email to