Search code examples
javaimagegraphics2d

Drawing in greyscale produces color shift


I'm working on elevation map hierarchy, quad tree, and for that I need to process elevation dataset in form of 16bit grayscale images into different images in various levels. That is for the most part downsampling input data.

Consider input tile image of 14401x10801 in size, which is supposed to be drawn into part of an image 512x512.

Draw call looks something like this:

Graphics2D g2d = (Graphics2D) dstImage.getGraphics();
...
BufferedImage clip = sourceImage.getSubimage(srcX, srcY, srcWidth, srcHeight);
g2d.drawImage(clip , dstX, dstY, dstWidth, dstHeight, null);

where

srcX = srcY = 0
srcWidth = 14401
srcHeight = 10801
dstX = dstY = 0
dstWidth = 171
dstHeight = 128

The area being filled is correct in relative terms, meaning peaks are where they are supposed to be. The issue is, that values do not exactly correspond to what you'd expect. Specifically, pixel 0,0 in dst is definitely sea with zero elevation, yet value 64 is drawn in there.

Looking at contents of src image

sourceImage.getSubimage( 0, 0, 
                    (int)(srcWidth/(double)dstWidth), 
                    (int)(srcHeight/(double)dstHeight));

should yield more or less values from src image that go into pixel 0,0 in dst. These are all zero, so I suppose the issue is not in some peak swaying the value up. What is more perplexing, all pixels that should be zero are set to 64.

For purpose of this processing (as java cannot handle negative values in 16 bit grayscale) all pixels are elevated by 16384. Elevating these by mere 1000 results in error -229 instead of 64. Value change is simple "add to all inputs" -> "process" -> "subtract from all outputs".

I hoped to avoid all the resampling, clipping, and efficiency problems by simply doing all the work over images, but so far it seems java does some obscure color mapping.

Any ideas what I might be doing wrong?


Solution

  • So I poked the code around a bit, and out of sole desperation started turning off things that should not, but might, have an impact. The culprit turned out to be

    g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    

    This hint does not ... hint ... how on earth one could get from average over 84 times 16384 something else than 16384, but hey, who am I to judge.

    Documentation for this hint states

    The RENDERING hint is a general hint that provides a high level recommendation as to whether to bias algorithm choices more for speed or quality when evaluating tradeoffs. This hint could be consulted for any rendering or image manipulation operation, but decisions will usually honor other, more specific hints in preference to this hint.

    not very helpful either.

    I did already encounter some discouragement from use of rendering hints in java2d, and it seems there indeed is something about that.