123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- /* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ==================================================================== */
-
- package org.apache.poi.sl.draw;
-
- import java.awt.AlphaComposite;
- import java.awt.Graphics2D;
- import java.awt.PaintContext;
- import java.awt.Rectangle;
- import java.awt.RenderingHints;
- import java.awt.Shape;
- import java.awt.TexturePaint;
- import java.awt.geom.AffineTransform;
- import java.awt.geom.Dimension2D;
- import java.awt.geom.Point2D;
- import java.awt.geom.Rectangle2D;
- import java.awt.image.BufferedImage;
- import java.awt.image.ColorModel;
-
- import org.apache.poi.sl.usermodel.Insets2D;
- import org.apache.poi.sl.usermodel.PaintStyle;
- import org.apache.poi.util.Dimension2DDouble;
-
- /* package */ class DrawTexturePaint extends java.awt.TexturePaint {
- private final PaintStyle.TexturePaint fill;
- private final Shape shape;
- private final double flipX, flipY;
- private final boolean isBitmapSrc;
-
- private static final Insets2D INSETS_EMPTY = new Insets2D(0,0,0,0);
-
-
- DrawTexturePaint(BufferedImage txtr, Shape shape, PaintStyle.TexturePaint fill, double flipX, double flipY, boolean isBitmapSrc) {
- // deactivate scaling/translation in super class, by specifying the dimension of the texture
- super(txtr, new Rectangle2D.Double(0,0,txtr.getWidth(),txtr.getHeight()));
- this.fill = fill;
- this.shape = shape;
- this.flipX = flipX;
- this.flipY = flipY;
- this.isBitmapSrc = isBitmapSrc;
- }
-
- @Override
- public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
-
- final Dimension2D userDim = new Dimension2DDouble();
- final Rectangle2D usedBounds;
- if (fill.isRotatedWithShape() || shape == null) {
- usedBounds = userBounds;
- } else {
- AffineTransform transform = new AffineTransform(xform);
-
- // Eliminate any post-translation
- transform.preConcatenate(AffineTransform.getTranslateInstance(
- -transform.getTranslateX(), -transform.getTranslateY()));
- Point2D p1 = new Point2D.Double(1, 0);
- p1 = transform.transform(p1,p1);
-
- final double rad = Math.atan2(p1.getY(),p1.getX());
-
- if (rad != 0) {
- xform.rotate(-rad, userBounds.getCenterX(), userBounds.getCenterY());
- }
-
- // TODO: check if approximation via rotating only the bounds (instead of the shape) is sufficient
- transform = AffineTransform.getRotateInstance(rad, userBounds.getCenterX(), userBounds.getCenterY());
- usedBounds = transform.createTransformedShape(shape).getBounds2D();
- }
- userDim.setSize(usedBounds.getWidth(), usedBounds.getHeight());
- xform.translate(usedBounds.getX(), usedBounds.getY());
-
- BufferedImage bi = getImage(usedBounds);
-
- if (fill.getStretch() != null) {
- TexturePaint tp = new TexturePaint(bi, new Rectangle2D.Double(0, 0, bi.getWidth(), bi.getHeight()));
- return tp.createContext(cm, deviceBounds, usedBounds, xform, hints);
- } else if (fill.getScale() != null) {
- AffineTransform newXform = getTiledInstance(usedBounds, (AffineTransform) xform.clone());
- TexturePaint tp = new TexturePaint(bi, new Rectangle2D.Double(0, 0, bi.getWidth(), bi.getHeight()));
- return tp.createContext(cm, deviceBounds, userBounds, newXform, hints);
- } else {
- return super.createContext(cm, deviceBounds, userBounds, xform, hints);
- }
- }
-
- public BufferedImage getImage(Rectangle2D userBounds) {
- BufferedImage bi = super.getImage();
- final Insets2D insets = fill.getInsets();
- final Insets2D stretch = fill.getStretch();
-
- if ((insets == null || INSETS_EMPTY.equals(insets)) && (stretch == null) || userBounds == null || userBounds.isEmpty()) {
- return bi;
- }
-
- if (insets != null && !INSETS_EMPTY.equals(insets)) {
- final int width = bi.getWidth();
- final int height = bi.getHeight();
-
- bi = bi.getSubimage(
- (int)(Math.max(insets.left,0)/100_000 * width),
- (int)(Math.max(insets.top,0)/100_000 * height),
- (int)((100_000-Math.max(insets.left,0)-Math.max(insets.right,0))/100_000 * width),
- (int)((100_000-Math.max(insets.top,0)-Math.max(insets.bottom,0))/100_000 * height)
- );
-
- int addTop = (int)(Math.max(-insets.top, 0)/100_000 * height);
- int addLeft = (int)(Math.max(-insets.left, 0)/100_000 * width);
- int addBottom = (int)(Math.max(-insets.bottom, 0)/100_000 * height);
- int addRight = (int)(Math.max(-insets.right, 0)/100_000 * width);
-
- // handle outsets
- if (addTop > 0 || addLeft > 0 || addBottom > 0 || addRight > 0) {
- int[] buf = new int[bi.getWidth()*bi.getHeight()];
- bi.getRGB(0, 0, bi.getWidth(), bi.getHeight(), buf, 0, bi.getWidth());
- BufferedImage borderBi = new BufferedImage(bi.getWidth()+addLeft+addRight, bi.getHeight()+addTop+addBottom, bi.getType());
- borderBi.setRGB(addLeft, addTop, bi.getWidth(), bi.getHeight(), buf, 0, bi.getWidth());
- bi = borderBi;
- }
- }
-
- if (stretch != null) {
- Rectangle2D srcBounds = new Rectangle2D.Double(
- 0, 0, bi.getWidth(), bi.getHeight()
- );
-
- Rectangle2D dstBounds = new Rectangle2D.Double(
- stretch.left/100_000 * userBounds.getWidth(),
- stretch.top/100_000 * userBounds.getHeight(),
- (100_000-stretch.left-stretch.right)/100_000 * userBounds.getWidth(),
- (100_000-stretch.top-stretch.bottom)/100_000 * userBounds.getHeight()
- );
-
- BufferedImage stretchBi = new BufferedImage((int)userBounds.getWidth(), (int)userBounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
- Graphics2D g = stretchBi.createGraphics();
-
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
- g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
- g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
- g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
-
- g.setComposite(AlphaComposite.Clear);
- g.fillRect(0, 0, stretchBi.getWidth(), stretchBi.getHeight());
- g.setComposite(AlphaComposite.SrcOver);
-
- AffineTransform at = new AffineTransform();
- at.translate(dstBounds.getCenterX(), dstBounds.getCenterY());
- at.scale(dstBounds.getWidth()/srcBounds.getWidth(), dstBounds.getHeight()/srcBounds.getHeight());
- at.translate(-srcBounds.getCenterX(), -srcBounds.getCenterY());
-
- g.drawRenderedImage(bi, at);
-
- g.dispose();
-
- bi = stretchBi;
- }
-
- return bi;
- }
-
- private AffineTransform getTiledInstance(final Rectangle2D usedBounds, final AffineTransform xform) {
- final BufferedImage bi = getImage();
- final Dimension2D scale = fill.getScale();
- assert(scale != null);
- final double img_w = bi.getWidth() * (scale.getWidth() == 0 ? 1 : scale.getWidth())/flipX;
- final double img_h = bi.getHeight() * (scale.getHeight() == 0 ? 1 : scale.getHeight())/flipY;
-
- // Alignment happens after the scaling but before any offset.
- PaintStyle.TextureAlignment ta = fill.getAlignment();
- final double alg_x, alg_y;
- final double usr_w = usedBounds.getWidth(), usr_h = usedBounds.getHeight();
- switch (ta == null ? PaintStyle.TextureAlignment.TOP_LEFT : ta) {
- case BOTTOM:
- alg_x = (usr_w-img_w)/2;
- alg_y = usr_h-img_h;
- break;
- case BOTTOM_LEFT:
- alg_x = 0;
- alg_y = usr_h-img_h;
- break;
- case BOTTOM_RIGHT:
- alg_x = usr_w-img_w;
- alg_y = usr_h-img_h;
- break;
- case CENTER:
- alg_x = (usr_w-img_w)/2;
- alg_y = (usr_h-img_h)/2;
- break;
- case LEFT:
- alg_x = 0;
- alg_y = (usr_h-img_h)/2;
- break;
- case RIGHT:
- alg_x = usr_w-img_w;
- alg_y = (usr_h-img_h)/2;
- break;
- case TOP:
- alg_x = (usr_w-img_w)/2;
- alg_y = 0;
- break;
- default:
- case TOP_LEFT:
- alg_x = 0;
- alg_y = 0;
- break;
- case TOP_RIGHT:
- alg_x = usr_w-img_w;
- alg_y = 0;
- break;
- }
- xform.translate(alg_x, alg_y);
-
- // Apply additional horizontal/vertical offset after alignment.
- // Values are as percentages.
-
- // TODO: apply scaling of drawing context to offset
- final Point2D offset = fill.getOffset();
-
- if (offset != null) {
- xform.translate(offset.getX(),offset.getY());
- }
-
- xform.scale(scale.getWidth()/(isBitmapSrc ? flipX : 1.),scale.getHeight()/(isBitmapSrc ? flipY : 1.));
-
- return xform;
- }
- }
|