bool Brush::intersectsBrush(const Brush& brush) const { if (!bounds().intersects(brush.bounds())) return false; // separating axis theorem // http://www.geometrictools.com/Documentation/MethodOfSeparatingAxes.pdf FaceList::const_iterator faceIt, faceEnd; const VertexList& myVertices = vertices(); const FaceList& theirFaces = brush.faces(); for (faceIt = theirFaces.begin(), faceEnd = theirFaces.end(); faceIt != faceEnd; ++faceIt) { const Face& theirFace = **faceIt; const Vec3f& origin = theirFace.vertices().front()->position; const Vec3f& direction = theirFace.boundary().normal; if (vertexStatusFromRay(origin, direction, myVertices) == PointStatus::PSAbove) return false; } const VertexList& theirVertices = brush.vertices(); for (faceIt = m_faces.begin(), faceEnd = m_faces.end(); faceIt != faceEnd; ++faceIt) { const Face& myFace = **faceIt; const Vec3f& origin = myFace.vertices().front()->position; const Vec3f& direction = myFace.boundary().normal; if (vertexStatusFromRay(origin, direction, theirVertices) == PointStatus::PSAbove) return false; } const EdgeList& myEdges = edges(); const EdgeList& theirEdges = brush.edges(); EdgeList::const_iterator myEdgeIt, myEdgeEnd, theirEdgeIt, theirEdgeEnd; for (myEdgeIt = myEdges.begin(), myEdgeEnd = myEdges.end(); myEdgeIt != myEdgeEnd; ++myEdgeIt) { const Edge& myEdge = **myEdgeIt; for (theirEdgeIt = theirEdges.begin(), theirEdgeEnd = theirEdges.end(); theirEdgeIt != theirEdgeEnd; ++theirEdgeIt) { const Edge& theirEdge = **theirEdgeIt; const Vec3f myEdgeVec = myEdge.vector(); const Vec3f theirEdgeVec = theirEdge.vector(); const Vec3f& origin = myEdge.start->position; const Vec3f direction = crossed(myEdgeVec, theirEdgeVec); PointStatus::Type myStatus = vertexStatusFromRay(origin, direction, myVertices); if (myStatus != PointStatus::PSInside) { PointStatus::Type theirStatus = vertexStatusFromRay(origin, direction, theirVertices); if (theirStatus != PointStatus::PSInside) { if (myStatus != theirStatus) return false; } } } } return true; }
bool Brush::containsBrush(const Brush& brush) const { if (bounds().contains(brush.bounds())) return false; const VertexList& theirVertices = brush.vertices(); VertexList::const_iterator vertexIt, vertexEnd; for (vertexIt = theirVertices.begin(), vertexEnd = theirVertices.end(); vertexIt != vertexEnd; ++vertexIt) { const Vertex& vertex = **vertexIt; if (!containsPoint(vertex.position)) return false; } return true; }
void Editor::moveBrushPreview(const gfx::Point& pos, bool refresh) { ASSERT(m_sprite != NULL); gfx::Point oldScreenPos = m_cursorScreen; gfx::Point oldEditorPos = m_cursorEditor; clearBrushPreview(false); drawBrushPreview(pos, false); gfx::Point newEditorPos = m_cursorEditor; if (refresh) { // Restore pixels { ScreenGraphics g; SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); forEachBrushPixel(&g, oldScreenPos, oldEditorPos, gfx::ColorNone, clearpixel); } if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) { Brush* brush = get_current_brush(); gfx::Rect newBrushBounds = brush->bounds(); newBrushBounds.offset(newEditorPos); m_document->notifySpritePixelsModified (m_sprite, gfx::Region(lastBrushBounds.createUnion(newBrushBounds))); lastBrushBounds = newBrushBounds; } // Save area and draw the cursor ScreenGraphics g; forEachBrushPixel(&g, m_cursorScreen, newEditorPos, ui_cursor_color, savepixel); forEachBrushPixel(&g, m_cursorScreen, newEditorPos, ui_cursor_color, drawpixel); } }
// Draws the brush cursor in the specified absolute mouse position // given in 'pos' param. Warning: You should clean the cursor before // to use this routine with other editor. void BrushPreview::show(const gfx::Point& screenPos) { if (m_onScreen) hide(); app::Document* document = m_editor->document(); Sprite* sprite = m_editor->sprite(); Layer* layer = m_editor->layer(); ASSERT(sprite); // Get drawable region m_editor->getDrawableRegion(m_clippingRegion, ui::Widget::kCutTopWindows); // Get cursor color app::Color app_cursor_color = Preferences::instance().editor.cursorColor(); gfx::Color ui_cursor_color = color_utils::color_for_ui(app_cursor_color); m_blackAndWhiteNegative = (app_cursor_color.getType() == app::Color::MaskType); // Cursor in the screen (view) m_screenPosition = screenPos; // Get cursor position in the editor gfx::Point spritePos = m_editor->screenToEditor(screenPos); // Get the current tool tools::Ink* ink = m_editor->getCurrentEditorInk(); bool isFloodfill = m_editor->getCurrentEditorTool()->getPointShape(0)->isFloodFill(); // Setup the cursor type debrushding of several factors (current tool, // foreground color, and layer transparency). color_t brush_color = getBrushColor(sprite, layer); color_t mask_color = sprite->transparentColor(); if (ink->isSelection() || ink->isSlice()) { m_type = SELECTION_CROSS; } else if ( // Use cursor bounds for inks that are effects (eraser, blur, etc.) (ink->isEffect()) || // or when the brush color is transparent and we are not in the background layer (layer && !layer->isBackground() && brush_color == mask_color)) { m_type = BRUSH_BOUNDARIES; } else { m_type = CROSS; } // For cursor type 'bounds' we have to generate cursor boundaries if (m_type & BRUSH_BOUNDARIES) generateBoundaries(); // Draw pixel/brush preview if ((m_type & CROSS) && m_editor->getState()->requireBrushPreview()) { Brush* brush = getCurrentBrush(); gfx::Rect origBrushBounds = (isFloodfill ? gfx::Rect(0, 0, 1, 1): brush->bounds()); gfx::Rect brushBounds = origBrushBounds; brushBounds.offset(spritePos); // Create the extra cel to show the brush preview Site site = m_editor->getSite(); Cel* cel = site.cel(); int t, opacity = 255; if (cel) opacity = MUL_UN8(opacity, cel->opacity(), t); if (layer) opacity = MUL_UN8(opacity, static_cast<LayerImage*>(layer)->opacity(), t); document->prepareExtraCel(brushBounds, opacity); document->setExtraCelType(render::ExtraType::NONE); document->setExtraCelBlendMode( (layer ? static_cast<LayerImage*>(layer)->blendMode(): BlendMode::NORMAL)); Image* extraImage = document->getExtraCelImage(); extraImage->setMaskColor(mask_color); clear_image(extraImage, mask_color); if (layer) { render::Render().renderLayer( extraImage, layer, m_editor->frame(), gfx::Clip(0, 0, brushBounds), BlendMode::SRC); // This extra cel is a patch for the current layer/frame document->setExtraCelType(render::ExtraType::PATCH); } tools::ToolLoop* loop = create_tool_loop_preview( m_editor, UIContext::instance(), extraImage, -gfx::Point(brushBounds.x, brushBounds.y)); if (loop) { loop->getInk()->prepareInk(loop); loop->getIntertwine()->prepareIntertwine(); loop->getController()->prepareController(); loop->getPointShape()->preparePointShape(loop); loop->getPointShape()->transformPoint( loop, -origBrushBounds.x, -origBrushBounds.y); delete loop; } document->notifySpritePixelsModified( sprite, gfx::Region(m_lastBounds = brushBounds)); } // Save area and draw the cursor { ui::ScreenGraphics g; ui::SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); forEachBrushPixel(&g, m_screenPosition, spritePos, ui_cursor_color, &BrushPreview::savePixelDelegate); forEachBrushPixel(&g, m_screenPosition, spritePos, ui_cursor_color, &BrushPreview::drawPixelDelegate); } // Cursor in the editor (model) m_onScreen = true; m_editorPosition = spritePos; // Save the clipping-region to know where to clean the pixels m_oldClippingRegion = m_clippingRegion; }
// Draws the brush cursor in the specified absolute mouse position // given in 'pos' param. Warning: You should clean the cursor before // to use this routine with other editor. void BrushPreview::show(const gfx::Point& screenPos) { if (m_onScreen) hide(); app::Document* document = m_editor->document(); Sprite* sprite = m_editor->sprite(); Layer* layer = m_editor->layer(); ASSERT(sprite); // Get drawable region m_editor->getDrawableRegion(m_clippingRegion, ui::Widget::kCutTopWindows); // Remove the invalidated region in the editor. m_clippingRegion.createSubtraction(m_clippingRegion, m_editor->getUpdateRegion()); // Get cursor color const auto& pref = Preferences::instance(); app::Color app_cursor_color = pref.editor.cursorColor(); gfx::Color ui_cursor_color = color_utils::color_for_ui(app_cursor_color); m_blackAndWhiteNegative = (app_cursor_color.getType() == app::Color::MaskType); // Cursor in the screen (view) m_screenPosition = screenPos; // Get cursor position in the editor gfx::Point spritePos = m_editor->screenToEditor(screenPos); // Get the current tool tools::Ink* ink = m_editor->getCurrentEditorInk(); bool isFloodfill = m_editor->getCurrentEditorTool()->getPointShape(0)->isFloodFill(); // Setup the cursor type depending on several factors (current tool, // foreground color, layer transparency, brush size, etc.). Brush* brush = getCurrentBrush(); color_t brush_color = getBrushColor(sprite, layer); color_t mask_index = sprite->transparentColor(); if (ink->isSelection() || ink->isSlice()) { m_type = SELECTION_CROSS; } else if ( (brush->type() == kImageBrushType || brush->size() > 1.0 / m_editor->zoom().scale()) && (// Use cursor bounds for inks that are effects (eraser, blur, etc.) (ink->isEffect()) || // or when the brush color is transparent and we are not in the background layer (!ink->isShading() && (layer && !layer->isBackground()) && ((sprite->pixelFormat() == IMAGE_INDEXED && brush_color == mask_index) || (sprite->pixelFormat() == IMAGE_RGB && rgba_geta(brush_color) == 0) || (sprite->pixelFormat() == IMAGE_GRAYSCALE && graya_geta(brush_color) == 0))))) { m_type = BRUSH_BOUNDARIES; } else { m_type = CROSS; } bool usePreview = false; auto brushPreview = pref.editor.brushPreview(); if (!m_editor->docPref().show.brushPreview()) brushPreview = app::gen::BrushPreview::NONE; switch (brushPreview) { case app::gen::BrushPreview::NONE: m_type = CROSS; break; case app::gen::BrushPreview::EDGES: m_type = BRUSH_BOUNDARIES; break; case app::gen::BrushPreview::FULL: usePreview = m_editor->getState()->requireBrushPreview(); break; } // For cursor type 'bounds' we have to generate cursor boundaries if (m_type & BRUSH_BOUNDARIES) generateBoundaries(); // Draw pixel/brush preview if ((m_type & CROSS) && usePreview) { gfx::Rect origBrushBounds = (isFloodfill ? gfx::Rect(0, 0, 1, 1): brush->bounds()); gfx::Rect brushBounds = origBrushBounds; brushBounds.offset(spritePos); // Create the extra cel to show the brush preview Site site = m_editor->getSite(); Cel* cel = site.cel(); int t, opacity = 255; if (cel) opacity = MUL_UN8(opacity, cel->opacity(), t); if (layer) opacity = MUL_UN8(opacity, static_cast<LayerImage*>(layer)->opacity(), t); if (!m_extraCel) m_extraCel.reset(new ExtraCel); m_extraCel->create(document->sprite(), brushBounds, site.frame(), opacity); m_extraCel->setType(render::ExtraType::NONE); m_extraCel->setBlendMode( (layer ? static_cast<LayerImage*>(layer)->blendMode(): BlendMode::NORMAL)); document->setExtraCel(m_extraCel); Image* extraImage = m_extraCel->image(); extraImage->setMaskColor(mask_index); clear_image(extraImage, (extraImage->pixelFormat() == IMAGE_INDEXED ? mask_index: 0)); if (layer) { render::Render().renderLayer( extraImage, layer, site.frame(), gfx::Clip(0, 0, brushBounds), BlendMode::SRC); // This extra cel is a patch for the current layer/frame m_extraCel->setType(render::ExtraType::PATCH); } { base::UniquePtr<tools::ToolLoop> loop( create_tool_loop_preview( m_editor, extraImage, brushBounds.origin())); if (loop) { loop->getInk()->prepareInk(loop); loop->getIntertwine()->prepareIntertwine(); loop->getController()->prepareController(ui::kKeyNoneModifier); loop->getPointShape()->preparePointShape(loop); loop->getPointShape()->transformPoint( loop, -origBrushBounds.x, -origBrushBounds.y); } } document->notifySpritePixelsModified( sprite, gfx::Region(m_lastBounds = brushBounds), m_lastFrame = site.frame()); m_withRealPreview = true; } // Save area and draw the cursor { ui::ScreenGraphics g; ui::SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); forEachBrushPixel(&g, m_screenPosition, spritePos, ui_cursor_color, &BrushPreview::savePixelDelegate); forEachBrushPixel(&g, m_screenPosition, spritePos, ui_cursor_color, &BrushPreview::drawPixelDelegate); } // Cursor in the editor (model) m_onScreen = true; m_editorPosition = spritePos; // Save the clipping-region to know where to clean the pixels m_oldClippingRegion = m_clippingRegion; }
// Draws the brush cursor inside the specified editor. // Warning: You should clean the cursor before to use // this routine with other editor. // // Note: x and y params are absolute positions of the mouse. void Editor::drawBrushPreview(const gfx::Point& pos, bool refresh) { ASSERT(m_cursorThick == 0); ASSERT(m_sprite != NULL); // Get drawable region getDrawableRegion(clipping_region, kCutTopWindows); // Get cursor color cursor_negative = is_cursor_mask; // Cursor in the screen (view) m_cursorScreen = pos; // Get cursor position in the editor gfx::Point spritePos = screenToEditor(pos); // Get the current tool tools::Tool* tool = getCurrentEditorTool(); tools::Ink* ink = getCurrentEditorInk(); // Setup the cursor type debrushding of several factors (current tool, // foreground color, and layer transparency). color_t brush_color = get_brush_color(m_sprite, m_layer); color_t mask_color = m_sprite->transparentColor(); if (ink->isSelection() || ink->isSlice()) { cursor_type = CURSOR_THICKCROSS; } else if ( // Use cursor bounds for inks that are effects (eraser, blur, etc.) (ink->isEffect()) || // or when the brush color is transparent and we are not in the background layer (m_layer && !m_layer->isBackground() && brush_color == mask_color)) { cursor_type = CURSOR_BRUSHBOUNDS; } else { cursor_type = CURSOR_THINCROSS; } // For cursor type 'bounds' we have to generate cursor boundaries if (cursor_type & CURSOR_BRUSHBOUNDS) generate_cursor_boundaries(this); // Draw pixel/brush preview if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) { IToolSettings* tool_settings = UIContext::instance() ->settings() ->getToolSettings(tool); Brush* brush = get_current_brush(); gfx::Rect brushBounds = brush->bounds(); brushBounds.offset(spritePos); // Create the extra cel to show the brush preview Site site = getSite(); Cel* cel = site.cel(); m_document->prepareExtraCel( brushBounds, cel ? cel->opacity(): 255); m_document->setExtraCelType(render::ExtraType::NONE); m_document->setExtraCelBlendMode( m_layer ? static_cast<LayerImage*>(m_layer)->getBlendMode(): BLEND_MODE_NORMAL); // In 'indexed' images, if the current color is 0, we have to use // a different mask color (different from 0) to draw the extra layer if (brush_color == mask_color) mask_color = (mask_color == 0 ? 1: 0); Image* extraImage = m_document->getExtraCelImage(); extraImage->setMaskColor(mask_color); clear_image(extraImage, mask_color); if (m_layer) { render::Render().renderLayer( extraImage, m_layer, m_frame, gfx::Clip(0, 0, brushBounds), BLEND_MODE_COPY); // This extra cel is a patch for the current layer/frame m_document->setExtraCelType(render::ExtraType::PATCH); } tools::ToolLoop* loop = create_tool_loop_preview( this, UIContext::instance(), extraImage, -gfx::Point(brushBounds.x, brushBounds.y)); if (loop) { loop->getInk()->prepareInk(loop); loop->getIntertwine()->prepareIntertwine(); loop->getController()->prepareController(); loop->getPointShape()->preparePointShape(loop); loop->getPointShape()->transformPoint( loop, -brush->bounds().x, -brush->bounds().y); delete loop; } if (refresh) { m_document->notifySpritePixelsModified (m_sprite, gfx::Region(lastBrushBounds = brushBounds)); } } // Save area and draw the cursor if (refresh) { ScreenGraphics g; SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); forEachBrushPixel(&g, m_cursorScreen, spritePos, ui_cursor_color, savepixel); forEachBrushPixel(&g, m_cursorScreen, spritePos, ui_cursor_color, drawpixel); } // Cursor thickness m_cursorThick = 1; // Cursor in the editor (model) m_cursorEditor = spritePos; // Save the clipping-region to know where to clean the pixels old_clipping_region = clipping_region; }