TilingPaintFactory.java
/*
* Copyright 2017 The Apache Software Foundation.
*
* Licensed 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.pdfbox.rendering;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
import org.apache.pdfbox.pdmodel.graphics.pattern.PDTilingPattern;
import org.apache.pdfbox.util.Matrix;
/**
* Factory class to cache TilingPaint generation.
*
* @author Tilman Hausherr
*/
class TilingPaintFactory
{
private static final Log LOG = LogFactory.getLog(TilingPaintFactory.class);
private final PageDrawer drawer;
private final Map<TilingPaintParameter, WeakReference<Paint>> weakCache
= new WeakHashMap<>();
TilingPaintFactory(PageDrawer drawer)
{
this.drawer = drawer;
}
Paint create(PDTilingPattern pattern, PDColorSpace colorSpace,
PDColor color, AffineTransform xform) throws IOException
{
Paint paint = null;
TilingPaintParameter tilingPaintParameter
= new TilingPaintParameter(drawer.getInitialMatrix(), pattern.getCOSObject(), colorSpace, color, xform);
WeakReference<Paint> weakRef = weakCache.get(tilingPaintParameter);
if (weakRef != null)
{
// PDFBOX-4058: additional WeakReference makes gc work better
paint = weakRef.get();
}
if (paint == null)
{
paint = new TilingPaint(drawer, pattern, colorSpace, color, xform);
weakCache.put(tilingPaintParameter, new WeakReference<>(paint));
}
return paint;
}
// class to characterize a TilingPaint object. It is important that TilingPaint does not
// keep any objects from this class, so that the weak cache works.
private static class TilingPaintParameter
{
private final Matrix matrix;
private final COSDictionary patternDict;
private final PDColorSpace colorSpace;
private final PDColor color;
private final AffineTransform xform;
private TilingPaintParameter(Matrix matrix, COSDictionary patternDict, PDColorSpace colorSpace,
PDColor color, AffineTransform xform)
{
this.matrix = matrix.clone();
this.patternDict = patternDict;
this.colorSpace = colorSpace;
this.color = color;
this.xform = xform;
}
// this may not catch all equals, but at least those related to one resource dictionary.
// it isn't needed to investigate further because matrix or transform would be different anyway.
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof TilingPaintParameter))
{
return false;
}
final TilingPaintParameter other = (TilingPaintParameter) obj;
if (this.matrix != other.matrix && (this.matrix == null || !this.matrix.equals(other.matrix)))
{
return false;
}
if (this.patternDict != other.patternDict && (this.patternDict == null || !this.patternDict.equals(other.patternDict)))
{
return false;
}
if (this.colorSpace != other.colorSpace && (this.colorSpace == null || !this.colorSpace.equals(other.colorSpace)))
{
return false;
}
if (this.color == null && other.color != null)
{
return false;
}
if (this.color != null && other.color == null)
{
return false;
}
if (this.color != null && this.color.getColorSpace() != other.color.getColorSpace())
{
return false;
}
try
{
if (this.color != null && other.color != null &&
this.color != other.color && this.color.toRGB() != other.color.toRGB())
{
return false;
}
}
catch (IOException ex)
{
LOG.debug("Couldn't convert color to RGB - treating as not equal", ex);
return false;
}
return !(this.xform != other.xform && (this.xform == null || !this.xform.equals(other.xform)));
}
@Override
public int hashCode()
{
int hash = 7;
hash = 23 * hash + (this.matrix != null ? this.matrix.hashCode() : 0);
hash = 23 * hash + (this.patternDict != null ? this.patternDict.hashCode() : 0);
hash = 23 * hash + (this.colorSpace != null ? this.colorSpace.hashCode() : 0);
hash = 23 * hash + (this.color != null ? this.color.hashCode() : 0);
hash = 23 * hash + (this.xform != null ? this.xform.hashCode() : 0);
return hash;
}
@Override
public String toString()
{
return "TilingPaintParameter{" + "matrix=" + matrix + ", pattern=" + patternDict
+ ", colorSpace=" + colorSpace + ", color=" + color + ", xform=" + xform + '}';
}
}
}