JAIPreviewGenerator implements ImagePreviewGenerator { private void style(InputStream is, OutputStream os, String format, ImageStyle style) throws IllegalArgumentException, IOException, OutOfMemoryError { if (is.available() == 0) throw new IllegalArgumentException("Empty input stream was passed to image styling"); if (style == null || ImageScalingMode.None.equals(style.getScalingMode())) { logger.trace("No scaling needed, performing a noop stream copy"); IOUtils.copy(is, os); return; } SeekableStream seekableInputStream = null; RenderedOp image = null; try { seekableInputStream = new FileCacheSeekableStream(is); image = JAI.create("stream", seekableInputStream); if (image == null) throw new IOException("Error reading image from input stream"); int imageWidth = image.getWidth(); int imageHeight = image.getHeight(); float scale = ImageStyleUtils.getScale(imageWidth, imageHeight, style); RenderingHints scaleHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); scaleHints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); scaleHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); scaleHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); int scaledWidth = Math.round(scale * image.getWidth()); int scaledHeight = Math.round(scale * image.getHeight()); int cropX = 0; int cropY = 0; if (scaledWidth < 1.0f) { scale = 1.0f / imageWidth; scaledWidth = 1; cropY = imageHeight - scaledHeight; scaledHeight = Math.round(imageHeight * scale); } else if (scaledHeight < 1.0f) { scale = 1.0f / imageHeight; scaledHeight = 1; cropX = imageWidth - scaledWidth; scaledWidth = Math.round(imageWidth * scale); } if (scale > 1.0) { ParameterBlock scaleParams = new ParameterBlock(); scaleParams.addSource(image); scaleParams.add(scale).add(scale).add(0.0f).add(0.0f); scaleParams.add(Interpolation.getInstance(Interpolation.INTERP_BICUBIC_2)); image = JAI.create("scale", scaleParams, scaleHints); } else if (scale < 1.0) { ParameterBlock subsampleAverageParams = new ParameterBlock(); subsampleAverageParams.addSource(image); subsampleAverageParams.add(Double.valueOf(scale)); subsampleAverageParams.add(Double.valueOf(scale)); image = JAI.create("subsampleaverage", subsampleAverageParams, scaleHints); } cropX = (int) Math.max(cropX, (float) Math.ceil(ImageStyleUtils.getCropX(scaledWidth, scaledHeight, style))); cropY = (int) Math.max(cropY, (float) Math.ceil(ImageStyleUtils.getCropY(scaledWidth, scaledHeight, style))); if ((cropX > 0 && Math.floor(cropX / 2.0f) > 0) || (cropY > 0 && Math.floor(cropY / 2.0f) > 0)) { ParameterBlock cropTopLeftParams = new ParameterBlock(); cropTopLeftParams.addSource(image); cropTopLeftParams.add(cropX > 0 ? ((float) Math.floor(cropX / 2.0f)) : 0.0f); cropTopLeftParams.add(cropY > 0 ? ((float) Math.floor(cropY / 2.0f)) : 0.0f); cropTopLeftParams.add(scaledWidth - Math.max(cropX, 0.0f)); cropTopLeftParams.add(scaledHeight - Math.max(cropY, 0.0f)); RenderingHints croppingHints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_COPY)); image = JAI.create("crop", cropTopLeftParams, croppingHints); } ParameterBlock encodeParams = new ParameterBlock(); encodeParams.addSource(image); encodeParams.add(os); encodeParams.add("jpeg"); JAI.create("encode", encodeParams); } catch (Throwable t) { if (t.getClass().getName().contains("ImageFormat")) { throw new IllegalArgumentException(t.getMessage()); } } finally { IOUtils.closeQuietly(seekableInputStream); if (image != null) image.dispose(); } }  @Override boolean supports(Resource<?> resource, Language language); @Override boolean supports(String format); @Override void createPreview(Resource<?> resource, Environment environment,
      Language language, ImageStyle style, String format, InputStream is,
      OutputStream os); @Override void createPreview(File imageFile, Environment environment,
      Language language, ImageStyle style, String format, InputStream is,
      OutputStream os); @Override String getContentType(Resource<?> resource, Language language,
      ImageStyle style); @Override String getSuffix(Resource<?> resource, Language language,
      ImageStyle style); @Override int getPriority();  }
@Test @Ignore public void testStyle() throws Exception { for (ImageStyle style : styles) { InputStream is = null; ByteArrayOutputStream bos = null; ByteArrayInputStream bis = null; try { is = imageURL.openStream(); bos = new ByteArrayOutputStream(); JAIPreviewGenerator previewGenerator = new JAIPreviewGenerator(); previewGenerator.createPreview(resource, Environment.Development, LanguageUtils.getLanguage("en"), style, null, is, bos); bis = new ByteArrayInputStream(bos.toByteArray()); SeekableStream seekableInputStream = new MemoryCacheSeekableStream(bis); RenderedOp image = JAI.create("stream", seekableInputStream); switch (style.getScalingMode()) { case Box: assertEquals(styleWidth, image.getWidth(), 0); assertEquals(originalHeight * (styleWidth / originalWidth), image.getHeight(), 0); break; case Cover: assertEquals(originalWidth * (styleHeight / originalHeight), image.getWidth(), 0); assertEquals(styleHeight, image.getHeight(), 0); break; case Fill: assertEquals(styleWidth, image.getWidth(), 0); assertEquals(styleHeight, image.getHeight(), 0); break; case Height: assertEquals(originalWidth * (styleHeight / originalHeight), image.getWidth(), 0); assertEquals(styleHeight, image.getHeight(), 0); break; case None: assertEquals(originalWidth, image.getWidth(), 0); assertEquals(originalHeight, image.getHeight(), 0); break; case Width: assertEquals(styleWidth, image.getWidth(), 0); assertEquals(originalHeight * (styleWidth / originalWidth), image.getHeight(), 0); break; default: fail("Unknown scaling mode " + style.getScalingMode()); } } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(bos); IOUtils.closeQuietly(bis); } } }