/*
* Copyright 2014 PRImA Research Lab, University of Salford, United Kingdom
*
* 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.primaresearch.web.gwt.client.ui.page.renderer;
import java.util.ArrayList;
import java.util.List;
import org.primaresearch.web.gwt.client.page.PageLayoutC;
import org.primaresearch.web.gwt.client.ui.DocumentImageListener;
import org.primaresearch.web.gwt.client.ui.DocumentImageSource;
import org.primaresearch.web.gwt.client.ui.RenderStyles;
import org.primaresearch.web.gwt.client.ui.page.SelectionManager;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.CssColor;
import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.user.client.ui.Image;
/**
* Basic renderer for a document page that renders the document image.<br>
* Add renderer plugins for additional layers.
*
* @author Christian Clausner
*
*/
public class PageRenderer implements DocumentImageListener {
private Canvas canvas;
private Canvas imageBuffer;
private boolean imageBuffered = false;
private PageLayoutC pageLayout;
private String pageContentToRender = null;
private String contentObjectToHighlightFaintly = null;
private SelectionManager selectionManager;
private DocumentImageSource imageSource;
private int width = 0;
private int height = 0;
private Context2d context;
private double zoomFactor = 1.0;
private RenderStyles renderStyles = DefaultRenderStyles.getInstance();
private List<RendererPlugin> plugins = new ArrayList<RendererPlugin>();
/**
* Constructor
* @param canvas Drawing canvas
* @param pageLayout Page layout to render
* @param selectionManager Page object selection manager (to highlight selected objects for instance)
* @param imageSource Document image source
*/
public PageRenderer(Canvas canvas, PageLayoutC pageLayout, SelectionManager selectionManager, DocumentImageSource imageSource) {
this.canvas = canvas;
imageBuffer = Canvas.createIfSupported();
this.pageLayout = pageLayout;
this.selectionManager = selectionManager;
this.imageSource = imageSource;
imageSource.addListener(this);
}
/**
* Add renderer plug-in (layer)
*/
public void addPlugin(RendererPlugin plugin) {
this.plugins.add(plugin);
}
/**
* Remove renderer plug-in (layer)
*/
public void removePlugin(RendererPlugin plugin) {
plugins.remove(plugin);
}
/**
* Redraws everything using the current zoom factor
*/
public void refresh() {
refresh(this.zoomFactor);
}
/**
* Redraws everything
* @param zoom Zoom factor to use
*/
public synchronized void refresh(double zoom) {
if (canvas == null)
return;
this.context = canvas.getContext2d();
onZoom(zoom);
//Draw image
try {
Image image = imageSource.getImage(zoom);
if (image == null)
drawBackground();
else
drawImage(/*image*/);
} catch (Exception exc) {
exc.printStackTrace();
}
//Process plugins
for (int i=0; i<plugins.size(); i++)
plugins.get(i).render(this);
}
/**
* Apply new zoom
* @param zoomFactor Zoom factor to use
*/
private void onZoom(double zoomFactor) {
this.zoomFactor = zoomFactor;
//Update size
int newWidth = (int)(pageLayout.getWidth() * zoomFactor);
int newHeight = (int)(pageLayout.getHeight() * zoomFactor);
if ((newWidth != width || newHeight != height) && newHeight > 0 && newWidth > 0) {
canvas.setSize(newWidth+"px", newHeight+"px");
canvas.setCoordinateSpaceWidth(newWidth);
canvas.setCoordinateSpaceHeight(newHeight);
width = newWidth;
height = newHeight;
}
context.setTransform(zoomFactor, 0, 0, zoomFactor, 0, 0);
}
/**
* Draws grey background
*/
private void drawBackground() {
CssColor liteGrey = CssColor.make(200, 200, 200);
CssColor darkGrey = CssColor.make(100, 100, 100);
boolean dark;
boolean startLineDark = false;
final int rectSize = 200;
for (int x=0; x<canvas.getCoordinateSpaceWidth(); x+=rectSize) {
dark = startLineDark;
for (int y=0; y<canvas.getCoordinateSpaceWidth(); y+=rectSize) {
context.setFillStyle(dark ? darkGrey : liteGrey);
context.fillRect(x, y, x+rectSize, y+rectSize);
context.fill();
dark = !dark;
}
startLineDark = !startLineDark;
}
}
/**
* Draws the document image into a buffer
*/
private void bufferImage() {
//
//CC: Comment on using a separate canvas as buffer for the page image:
//
//The straightforward approach of directly drawing the image on the main canvas
//proved to slow in Chrome (though extremely fast in IE).
//As a workaround we now draw the image on an internal canvas (buffer).
//We then draw the buffer canvas on the main canvas. This improves
//performance in Chrome considerably, but slows down IE a little bit.
if (imageBuffered)
return;
Image image = imageSource.getImage(zoomFactor);
if (image == null)
return;
imageBuffer.setSize(pageLayout.getWidth()+"px", pageLayout.getHeight()+"px");
imageBuffer.getCanvasElement().setWidth(pageLayout.getWidth());
imageBuffer.getCanvasElement().setHeight(pageLayout.getHeight());
imageBuffer.setCoordinateSpaceWidth(pageLayout.getWidth());
imageBuffer.setCoordinateSpaceHeight(pageLayout.getHeight());
imageBuffer.getContext2d().drawImage((ImageElement)(image).getElement().cast(), 0, 0);
imageBuffered = true;
}
/**
* Draws the document image onto the canvas
*/
private void drawImage(/*Image image*/) {
//context.putImageData(((ImageElement)image.getElement().cast()), 0, 0);
//context.drawImage((ImageElement)image.getElement().cast(), 0, 0);
CanvasElement ce = imageBuffer.getCanvasElement();
//int w = imageBuffer.getElement().getClientWidth();
context.drawImage(ce, 0, 0);
}
/**
* Specify which page content objects to render
* @param pageContentToRender See PageLayoutC.TYPE_ constants)
*/
public void setPageContentToRender(String pageContentToRender) {
this.pageContentToRender = pageContentToRender;
}
/**
* Returns the current setting for which page content objects to render
* @return See PageLayoutC.TYPE_ constants)
*/
public String getPageContentToRender() {
return pageContentToRender;
}
/**
* Sets a style provider for rendering page content objects (e.g. fill colour of text region).
*/
public void setRenderStyles(RenderStyles renderStyles) {
this.renderStyles = renderStyles;
}
/**
* Highlights the content object with the given ID
* @param id Content object ID. Use <code>null</code> to highlight nothing
*/
public void highlightContentObject(String id) {
if (id == null && contentObjectToHighlightFaintly == null) //No change
return;
if (id == null) { //No highlight
contentObjectToHighlightFaintly = null;
refresh(zoomFactor);
return;
}
if (!id.equals(contentObjectToHighlightFaintly)) { //Changed
contentObjectToHighlightFaintly = id;
refresh(zoomFactor);
}
}
public PageLayoutC getPageLayout() {
return pageLayout;
}
public String getContentObjectToHighlightFaintly() {
return contentObjectToHighlightFaintly;
}
public SelectionManager getSelectionManager() {
return selectionManager;
}
public Context2d getContext() {
return context;
}
public RenderStyles getRenderStyles() {
return renderStyles;
}
public double getZoomFactor() {
return zoomFactor;
}
public int measureTextWidth(String text, String font) {
Context2d context = canvas.getContext2d();
context.setFont(font);
return (int)context.measureText(text).getWidth();
}
@Override
public void imageLoaded() {
bufferImage();
refresh();
}
}