org.cobraparser.html.renderer.ImgControl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Cobra Show documentation
Show all versions of Cobra Show documentation
Cobra is the rendering engine designed for LoboBrowser
/*
GNU LESSER GENERAL PUBLIC LICENSE
Copyright (C) 2006 The Lobo Project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Contact info: [email protected]
*/
/*
* Created on Nov 19, 2005
*/
package org.cobraparser.html.renderer;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import javax.swing.SwingUtilities;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.cobraparser.html.domimpl.HTMLElementImpl;
import org.cobraparser.html.domimpl.HTMLImageElementImpl;
import org.cobraparser.html.domimpl.ImageEvent;
import org.cobraparser.html.domimpl.ImageListener;
import org.cobraparser.html.style.HtmlValues;
import org.cobraparser.ua.ImageResponse;
import cz.vutbr.web.css.CSSProperty.VerticalAlign;
class ImgControl extends BaseControl implements ImageListener {
private static final long serialVersionUID = -1510794248068777990L;
private volatile ImageResponse imageResponse = new ImageResponse();
// private final UserAgentContext browserContext;
private String lastSrc;
public ImgControl(final HTMLImageElementImpl modelNode) {
super(modelNode);
// this.browserContext = pcontext;
modelNode.addImageListener(this);
}
@Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
final ImageResponse imageResponse = this.imageResponse;
if (imageResponse.isDecoded()) {
assert(imageResponse.img != null);
final Image image = imageResponse.img;
final Dimension size = this.getSize();
final Insets insets = this.getInsets();
final Graphics2D g2 = (Graphics2D) g;
final int width = size.width - insets.left - insets.right;
final int height = size.height - insets.top - insets.bottom;
final int imgWidth = image.getWidth(this);
final int imgHeight = image.getHeight(this);
if (width < imgWidth || height < imgHeight) {
// down-sampling needs better handling
final Image scaledImg = getScaledInstance(image, width, height, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.drawImage(scaledImg, insets.left, insets.top, width, height, this);
} else {
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.drawImage(image, insets.left, insets.top, width, height, this);
}
} else {
// TODO: show alt text
}
}
private Dimension preferredSize;
private int declaredWidth;
private int declaredHeight;
@Override
public void reset(final int availWidth, final int availHeight) {
// Expected in the GUI thread.
// final HTMLElementImpl element = this.controlElement;
// TODO: Remove the parameters dw, dh, and members declaredWith, declaredHeight completely.
// They seem to be used only for old style syntax.
// final int dw = HtmlValues.getOldSyntaxPixelSize(element.getAttribute("width"), availWidth, -1);
// final int dh = HtmlValues.getOldSyntaxPixelSize(element.getAttribute("height"), availHeight, -1);
final int dw = -1;
final int dh = -1;
this.declaredWidth = dw;
this.declaredHeight = dh;
this.preferredSize = this.createPreferredSize(dw, dh);
}
@Override
public VerticalAlign getVAlign() {
final HTMLElementImpl element = this.controlElement;
final @Nullable VerticalAlign verticalAlign = element.getRenderState().getVerticalAlign();
return verticalAlign == null ? VerticalAlign.BASELINE : verticalAlign;
}
@Override
public Dimension getPreferredSize() {
final Dimension ps = this.preferredSize;
return ps == null ? new Dimension(0, 0) : ps;
}
public Dimension createPreferredSize(int dw, int dh) {
final ImageResponse imageResponseLocal = this.imageResponse;
if (!imageResponseLocal.isDecoded()) {
return new Dimension(dw == -1 ? 0 : dw, dh == -1 ? 0 : dh);
}
assert(imageResponseLocal.img != null);
final @NonNull Image img = imageResponseLocal.img;
if (dw == -1) {
if (dh != -1) {
final int iw = HtmlValues.scaleToDevicePixels(img.getWidth(this));
final int ih = HtmlValues.scaleToDevicePixels(img.getHeight(this));
if (ih == 0) {
dw = iw;
} else {
dw = (dh * iw) / ih;
}
} else {
dw = HtmlValues.scaleToDevicePixels(img.getWidth(this));
}
}
if (dh == -1) {
if (dw != -1) {
final int iw = HtmlValues.scaleToDevicePixels(img.getWidth(this));
final int ih = HtmlValues.scaleToDevicePixels(img.getHeight(this));
if (iw == 0) {
dh = ih == -1 ? 0 : ih;
} else {
dh = (dw * ih) / iw;
}
} else {
dh = HtmlValues.scaleToDevicePixels(img.getHeight(this));
}
}
return new Dimension(dw, dh);
}
private final boolean checkPreferredSizeChange() {
final Dimension newPs = this.createPreferredSize(this.declaredWidth, this.declaredHeight);
final Dimension ps = this.preferredSize;
if (ps == null) {
return true;
}
if ((ps.width != newPs.width) || (ps.height != newPs.height)) {
this.preferredSize = newPs;
return true;
} else {
return false;
}
}
/*
* (non-Javadoc)
*
* @see java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int,
* int)
*/
@Override
public boolean imageUpdate(final Image img, final int infoflags, final int x, final int y, final int w, final int h) {
if (((infoflags & ImageObserver.ALLBITS) != 0) || ((infoflags & ImageObserver.FRAMEBITS) != 0)) {
SwingUtilities.invokeLater(() -> {
if (!checkPreferredSizeChange()) {
repaint();
} else {
ruicontrol.preferredSizeInvalidated();
}
});
}
return true;
}
/*
* (non-Javadoc)
*
* @see java.awt.Component#imageUpdate(java.awt.Image, int, int, int, int,
* int)
*/
public void imageUpdate(final Image img, final int w, final int h) {
SwingUtilities.invokeLater(() -> {
if (!checkPreferredSizeChange()) {
repaint();
} else {
ruicontrol.preferredSizeInvalidated();
}
});
}
public boolean paintSelection(final Graphics g, final boolean inSelection, final RenderableSpot startPoint, final RenderableSpot endPoint) {
return inSelection;
}
public void imageLoaded(final ImageEvent event) {
// Implementation of ImageListener. Invoked in a request thread most likely.
final ImageResponse imageResponseLocal = event.imageResponse;
this.imageResponse = imageResponseLocal;
if (imageResponseLocal.isDecoded()) {
assert(imageResponseLocal.img != null);
final Image image = imageResponseLocal.img;
final int width = image.getWidth(this);
final int height = image.getHeight(this);
this.imageUpdate(image, width, height);
}
}
public void imageAborted() {
// do nothing
}
@Override
public String toString() {
return "ImgControl[src=" + this.lastSrc + "]";
}
// https://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
// Adapted from: https://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
/**
* Convenience method that returns a scaled instance of the provided {@code BufferedImage}.
*
* @param img the original image to be scaled
* @param targetWidth the desired width of the scaled instance, in pixels
* @param targetHeight the desired height of the scaled instance, in pixels
* @param hint one of the rendering hints that corresponds to {@code RenderingHints.KEY_INTERPOLATION}
* @return a scaled version of the original {@code BufferedImage}
*/
private Image getScaledInstance(final Image img, final int targetWidth, final int targetHeight, final Object hint) {
final int type = BufferedImage.TYPE_INT_ARGB;
Image ret = img;
int w = img.getWidth(this);
int h = img.getHeight(this);
while (w != targetWidth || h != targetHeight) {
if (w > targetWidth) {
w /= 2;
}
if (w < targetWidth) {
w = targetWidth;
}
if (h > targetHeight) {
h /= 2;
}
if (h < targetHeight) {
h = targetHeight;
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
}
return ret;
}
@Override
public boolean isReadyToPaint() {
return imageResponse.isReadyToPaint();
}
}