/**
* Copyright 2010 The ForPlay Authors
*
* 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 forplay.html;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Unit;
import forplay.core.Asserts;
import forplay.core.Image;
import forplay.core.ImageLayer;
import forplay.core.ResourceCallback;
class HtmlImageLayerDom extends HtmlLayerDom implements ImageLayer {
private float width, height;
private boolean widthSet, heightSet;
private float sx, sy, sw, sh;
private boolean sourceRectSet;
private boolean repeatX, repeatY;
private HtmlImage htmlImage;
public HtmlImageLayerDom() {
super(Document.get().createDivElement());
setRepeatX(false);
setRepeatY(false);
}
HtmlImageLayerDom(final Image img) {
this();
setImage(img);
}
@Override
public void clearHeight() {
heightSet = true;
applySize();
}
@Override
public void clearSourceRect() {
this.sourceRectSet = false;
applyBackgroundSize();
}
@Override
public void clearWidth() {
widthSet = true;
applySize();
}
@Override
public Image image() {
return htmlImage;
}
@Override
public void setHeight(float height) {
Asserts.checkArgument(height > 0, "Height must be > 0");
heightSet = true;
this.height = height;
applySize();
}
@Override
public void setImage(final Image img) {
Asserts.checkArgument(img instanceof HtmlImage);
// Make sure redundant setImage() calls don't cost much.
if (htmlImage == img) {
return;
}
htmlImage = (HtmlImage) img;
ImageElement imgElem = htmlImage.img.cast();
element().getStyle().setBackgroundImage("url(" + imgElem.getSrc() + ")");
element().getStyle().setOverflow(Overflow.HIDDEN);
img.addCallback(new ResourceCallback<Image>() {
@Override
public void done(Image resource) {
applySize();
applyBackgroundSize();
}
@Override
public void error(Throwable err) {
// Nothing to be done about errors.
}
});
}
@Override
public void setRepeatX(boolean repeat) {
Asserts.checkArgument(!repeat || !sourceRectSet, "Cannot repeat when source rect is used");
repeatX = repeat;
applyBackgroundSize();
}
@Override
public void setRepeatY(boolean repeat) {
Asserts.checkArgument(!repeat || !sourceRectSet, "Cannot repeat when source rect is used");
repeatY = repeat;
applyBackgroundSize();
}
@Override
public void setSourceRect(float sx, float sy, float sw, float sh) {
Asserts.checkState(!repeatX && !repeatY, "Cannot use source rect when repeating x or y");
Asserts.checkArgument(sw != 0 && sh != 0); // Will cause div-by-zero
// Early out if there's no change. applyBackgroundSize() isn't free.
if (sourceRectSet &&
(this.sx == sx) && (this.sy == sy) &&
(this.sw == sw) && (this.sh == sh)) {
return;
}
this.sourceRectSet = true;
this.sx = sx;
this.sy = sy;
this.sw = sw;
this.sh = sh;
applyBackgroundSize();
}
@Override
public void setWidth(float width) {
Asserts.checkArgument(width > 0, "Width must be > 0");
widthSet = true;
this.width = width;
applySize();
}
@Override
public void setSize(float width, float height) {
Asserts.checkArgument(width > 0 && height > 0,
"Width and height must be > 0 (got %dx%d)", width, height);
widthSet = true;
this.width = width;
heightSet = true;
this.height = height;
applySize();
}
private void applyBackgroundSize() {
Style style = element().getStyle();
// Set background-repeat to get the right repeating behavior.
String repeat = repeatX ? "repeat-x " : "";
repeat += repeatY ? "repeat-y" : "";
style.setProperty("backgroundRepeat", repeat);
// Set background-size to get the right pinning behavior.
if (sourceRectSet) {
float wratio = widthSet ? (width / sw) : (image().width() / sw);
float hratio = heightSet ? (height / sh) : (image().height() / sh);
if (wratio == 0) {
wratio = 1;
}
if (hratio == 0) {
hratio = 1;
}
float backWidth = image().width() * wratio;
float backHeight = image().height() * hratio;
style.setProperty("backgroundSize", backWidth + "px " + backHeight + "px");
style.setProperty("backgroundPosition", (-sx * wratio) + "px " + (-sy * hratio) + "px");
} else {
String size = repeatX ? image().width() + "px " : "100% ";
size += repeatY ? image().height() + "px" : "100%";
style.setProperty("backgroundSize", size);
style.clearProperty("backgroundPosition");
}
}
private void applySize() {
Style style = element().getStyle();
style.setWidth(widthSet ? width : htmlImage.img.getWidth(), Unit.PX);
style.setHeight(heightSet ? height : htmlImage.img.getHeight(), Unit.PX);
if (sourceRectSet) {
applyBackgroundSize();
}
}
@Override
public float width() {
Asserts.checkNotNull(htmlImage, "Image must not be null");
if (widthSet) {
return width;
} else {
return htmlImage.width();
}
}
@Override
public float height() {
Asserts.checkNotNull(htmlImage, "Image must not be null");
if (heightSet) {
return height;
} else {
return htmlImage.height();
}
}
@Override
public float scaledWidth() {
return transform().scaleX() * width();
}
@Override
public float scaledHeight() {
return transform().scaleY() * height();
}
}