#include "DocumentView.h" #include "background/MainBackgroundPainter.h" #include "control/tools/EditSelection.h" #include "control/tools/Selection.h" #include "model/Layer.h" #include "model/eraser/EraseableStroke.h" #include "StrokeView.h" #include "TextView.h" DocumentView::DocumentView() { this->backgroundPainter = new MainBackgroundPainter(); } DocumentView::~DocumentView() { delete this->backgroundPainter; this->backgroundPainter = nullptr; } /** * Mark stroke with Audio */ void DocumentView::setMarkAudioStroke(bool markAudioStroke) { this->markAudioStroke = markAudioStroke; } void DocumentView::applyColor(cairo_t* cr, Stroke* s) { if (s->getToolType() == STROKE_TOOL_HIGHLIGHTER) { if (s->getFill() != -1) { applyColor(cr, s, s->getFill()); } else { applyColor(cr, s, 120); } } else { applyColor(cr, static_cast(s)); } } void DocumentView::applyColor(cairo_t* cr, Element* e, uint8_t alpha) { applyColor(cr, e->getColor(), alpha); } void DocumentView::applyColor(cairo_t* cr, Color c, uint8_t alpha) { Util::cairo_set_source_rgbi(cr, c, alpha / 255.0); } void DocumentView::drawStroke(cairo_t* cr, Stroke* s, int startPoint, double scaleFactor, bool changeSource, bool noAlpha) const { if (s->getPointCount() < 2) { // Should not happen g_warning("DocumentView::drawStroke empty stroke..."); return; } StrokeView sv(cr, s, startPoint, scaleFactor, noAlpha); if (changeSource) { sv.changeCairoSource(this->markAudioStroke); } sv.paint(this->dontRenderEditingStroke); } void DocumentView::drawText(cairo_t* cr, Text* t) { cairo_matrix_t defaultMatrix = {0}; cairo_get_matrix(cr, &defaultMatrix); if (t->isInEditing()) { return; } cairo_set_operator(cr, CAIRO_OPERATOR_OVER); applyColor(cr, t); TextView::drawText(cr, t); cairo_set_matrix(cr, &defaultMatrix); } void DocumentView::drawImage(cairo_t* cr, Image* i) { cairo_matrix_t defaultMatrix = {0}; cairo_get_matrix(cr, &defaultMatrix); cairo_surface_t* img = i->getImage(); int width = cairo_image_surface_get_width(img); int height = cairo_image_surface_get_height(img); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); double xFactor = i->getElementWidth() / width; double yFactor = i->getElementHeight() / height; cairo_scale(cr, xFactor, yFactor); cairo_set_source_surface(cr, img, i->getX() / xFactor, i->getY() / yFactor); cairo_paint(cr); cairo_set_matrix(cr, &defaultMatrix); } void DocumentView::drawTexImage(cairo_t* cr, TexImage* texImage) { cairo_matrix_t defaultMatrix = {0}; cairo_get_matrix(cr, &defaultMatrix); PopplerDocument* pdf = texImage->getPdf(); cairo_surface_t* img = texImage->getImage(); if (pdf != nullptr) { if (poppler_document_get_n_pages(pdf) < 1) { g_warning("Got latex PDf without pages!: %s", texImage->getText().c_str()); return; } PopplerPage* page = poppler_document_get_page(pdf, 0); double pageWidth = 0; double pageHeight = 0; poppler_page_get_size(page, &pageWidth, &pageHeight); double xFactor = texImage->getElementWidth() / pageWidth; double yFactor = texImage->getElementHeight() / pageHeight; cairo_translate(cr, texImage->getX(), texImage->getY()); cairo_scale(cr, xFactor, yFactor); poppler_page_render(page, cr); g_clear_object(&page); } else if (img != nullptr) { int width = cairo_image_surface_get_width(img); int height = cairo_image_surface_get_height(img); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); double xFactor = texImage->getElementWidth() / width; double yFactor = texImage->getElementHeight() / height; cairo_scale(cr, xFactor, yFactor); cairo_set_source_surface(cr, img, texImage->getX() / xFactor, texImage->getY() / yFactor); cairo_paint(cr); } cairo_set_matrix(cr, &defaultMatrix); } void DocumentView::drawElement(cairo_t* cr, Element* e) const { if (e->getType() == ELEMENT_STROKE) { drawStroke(cr, dynamic_cast(e)); } else if (e->getType() == ELEMENT_TEXT) { drawText(cr, dynamic_cast(e)); } else if (e->getType() == ELEMENT_IMAGE) { drawImage(cr, dynamic_cast(e)); } else if (e->getType() == ELEMENT_TEXIMAGE) { drawTexImage(cr, dynamic_cast(e)); } } /** * Draw a single layer * @param cr Draw to thgis context * @param l The layer to draw */ void DocumentView::drawLayer(cairo_t* cr, Layer* l) { cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); #ifdef DEBUG_SHOW_REPAINT_BOUNDS int drawn = 0; int notDrawn = 0; #endif // DEBUG_SHOW_REPAINT_BOUNDS for (Element* e: *l->getElements()) { #ifdef DEBUG_SHOW_ELEMENT_BOUNDS cairo_set_source_rgb(cr, 0, 1, 0); cairo_set_line_width(cr, 1); cairo_rectangle(cr, e->getX(), e->getY(), e->getElementWidth(), e->getElementHeight()); cairo_stroke(cr); #endif // DEBUG_SHOW_REPAINT_BOUNDS // cairo_new_path(cr); if (this->lX != -1) { if (e->intersectsArea(this->lX, this->lY, this->width, this->height)) { drawElement(cr, e); #ifdef DEBUG_SHOW_REPAINT_BOUNDS drawn++; #endif // DEBUG_SHOW_REPAINT_BOUNDS } #ifdef DEBUG_SHOW_REPAINT_BOUNDS else { notDrawn++; } #endif // DEBUG_SHOW_REPAINT_BOUNDS } else { #ifdef DEBUG_SHOW_REPAINT_BOUNDS drawn++; #endif // DEBUG_SHOW_REPAINT_BOUNDS drawElement(cr, e); } } #ifdef DEBUG_SHOW_REPAINT_BOUNDS g_message("DBG:DocumentView: draw %i / not draw %i", drawn, notDrawn); #endif // DEBUG_SHOW_REPAINT_BOUNDS } void DocumentView::paintBackgroundImage() { GdkPixbuf* pixbuff = page->getBackgroundImage().getPixbuf(); if (pixbuff) { cairo_matrix_t matrix = {0}; cairo_get_matrix(cr, &matrix); int width = gdk_pixbuf_get_width(pixbuff); int height = gdk_pixbuf_get_height(pixbuff); double sx = page->getWidth() / width; double sy = page->getHeight() / height; cairo_scale(cr, sx, sy); gdk_cairo_set_source_pixbuf(cr, pixbuff, 0, 0); cairo_paint(cr); cairo_set_matrix(cr, &matrix); } } void DocumentView::drawSelection(cairo_t* cr, ElementContainer* container) { for (Element* e: *container->getElements()) { drawElement(cr, e); } } void DocumentView::limitArea(double x, double y, double width, double height) { this->lX = x; this->lY = y; this->lWidth = width; this->lHeight = height; } /** * Drawing first step * @param page The page to draw * @param cr Draw to thgis context * @param dontRenderEditingStroke false to draw currently drawing stroke */ void DocumentView::initDrawing(PageRef page, cairo_t* cr, bool dontRenderEditingStroke) { this->cr = cr; this->page = page; this->width = page->getWidth(); this->height = page->getHeight(); this->dontRenderEditingStroke = dontRenderEditingStroke; } /** * Last step in drawing */ void DocumentView::finializeDrawing() { #ifdef DEBUG_SHOW_REPAINT_BOUNDS if (this->lX != -1) { g_message("DBG:repaint area"); cairo_set_source_rgb(cr, 1, 0, 0); cairo_set_line_width(cr, 1); cairo_rectangle(cr, this->lX + 3, this->lY + 3, this->lWidth - 6, this->lHeight - 6); cairo_stroke(cr); } else { g_message("DBG:repaint complete"); } #endif // DEBUG_SHOW_REPAINT_BOUNDS this->lX = -1; this->lY = -1; this->lWidth = -1; this->lHeight = -1; this->page = nullptr; this->cr = nullptr; } /** * Draw the background */ void DocumentView::drawBackground(bool hidePdfBackground, bool hideImageBackground, bool hideRulingBackground) { PageType pt = page->getBackgroundType(); if (pt.isPdfPage()) { // Handled in PdfView } else if (pt.isImagePage() && !hideImageBackground) { paintBackgroundImage(); } else if (!hideRulingBackground) { backgroundPainter->paint(pt, cr, page); } } /** * Draw background if there is no background shown, like in GIMP etc. */ void DocumentView::drawTransparentBackgroundPattern() { Util::cairo_set_source_rgbi(cr, 0x666666U); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); Util::cairo_set_source_rgbi(cr, 0x999999U); bool second = false; for (int y = 0; y < height; y += 8) { second = !second; for (int x = second ? 8 : 0; x < width; x += 16) { cairo_rectangle(cr, x, y, 8, 8); cairo_fill(cr); } } } /** * Draw the full page, usually you would like to call this method * @param page The page to draw * @param cr Draw to thgis context * @param dontRenderEditingStroke false to draw currently drawing stroke * @param hideBackground true to hide the background */ void DocumentView::drawPage(PageRef page, cairo_t* cr, bool dontRenderEditingStroke, bool hidePdfBackground, bool hideImageBackground, bool hideRulingBackground) { initDrawing(page, cr, dontRenderEditingStroke); bool backgroundVisible = page->isLayerVisible(0); if (backgroundVisible) { drawBackground(hidePdfBackground, hideImageBackground, hideRulingBackground); } if (!backgroundVisible) { drawTransparentBackgroundPattern(); } int layer = 0; for (Layer* l: *page->getLayers()) { if (!page->isLayerVisible(l)) { continue; } drawLayer(cr, l); layer++; } finializeDrawing(); }