When copying or resizing between buffered images, doing multiple calls to drawImage() can be preferable to a single call on the destination image's graphics.<p>The most obvious reason is speed-up with parallelization, using multiple destination graphics with exclusive clips, and one drawImage() call per graphics. This can cause slight inaccuracies due to how clipping is handled, and might actually slow things down, possibly a lot, depending on image types, for example due to locks in images internals or due to slower clipped images processing paths. With the most efficiently supported image types though, such as INT_XXX and BYTE_GRAY, it's often well worth it.<p>Another reason is speed-up and/or accuracy improvement by using intermediary image types, because for some combinations of source and destination image types drawImage() can be very slow and/or inaccurate.<p>For example, when copying from INT_ARGB_PRE to BYTE_BINARY, the resulting intensity is not appropriate due to a same coefficient being used for each R/G/B component. This can be mostly fixed by first copying to BYTE_GRAY, which also speeds things up. Adding intermediary INT_ARGB images on each side of the BYTE_GRAY image further increases the accuracy and the speed of the overall process.<p>Another example is upscaling an image by just a factor 2 while converting from BYTE_ABGR_PRE to a CUSTOM type (like INT_ABGR_PRE): using INT_ARGB_PRE intermediary images as source and destination for the resize operation can speed up the overall process by a factor of 7 sequentially, or 20 in parallel on a 8-core when using NEAREST, or by a factor of 2 to 7 when using BICUBIC.<p>A third reason is cache misses: when copying between multiple intermediary image types (which, as we saw, can be useful when converting to BYTE_BINARY), rather than copying the whole image to each new type, it's better to go through all the types slice per slice, for the pixels written at one step to still be in cache when being written into the next image.<p>Maintaining slices through resizing should also be possible, but it would be more tricky and require enlarged source slices, as resizing algorithms typically use surrounding pixels. At this point it would probably be better instead to optimize drawImage() internals, although that would only apply to newer JDK versions.<p>I made this library to take care of these kinds of optimizations:
<a href="https://github.com/jeffhain/jimsizr" rel="nofollow">https://github.com/jeffhain/jimsizr</a>
It also implements BOXSAMPLED algorithm.<p>Its API is similar to that of Thumbnailator's Resizer interface, in that it takes source and destination buffered images as arguments, so it is straightforward to implement Resizer on top of it.<p>I also added support for parallelism in a Thumbnailator fork, by adding ThumbnailMaker.parallel(Executor), ParallelResizer interface and implementations of it for usual algorithms:
<a href="https://github.com/jeffhain/thumbnailator" rel="nofollow">https://github.com/jeffhain/thumbnailator</a>
That makes it on par with Jimsizr for speed and accuracy if you stick to standard INT_XXX images.