Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EffectLayer DrawTransformed #1814

Merged

Conversation

Wibble199
Copy link
Collaborator

This PR proposes adding a new method to the EffectLayer called DrawTransformed. It is a powerful method that is capable of providing a graphics context that transforms the drawn content to the target KeySequence bounds. This includes rotating if the user has rotated a freestyle KeySequence.

It has been implemented for the equalizer layer in this PR, which means the equalizer can now be rotated using the freestyle mode and will still draw correctly. This now allows for upside-down, left-to-right and right-to-left equalizer bars.

Example of the equalizer being rotated.

Usage (For developers)

To use this new method, in the layer's render method, call the new DrawTransformed method on the returned effect layer when you wish to do the drawing.

The first parameter of the method is the keysequence whose bounds will be used as the destination drawing rectangle. The method optionally next can take an action which can be used to define additional transformations on the matrix - will cover in more detail later. The next parameter is the actual render method. This is where you should be the code that will actually render the layer, using the provided graphics context. Note that you do not need to dispose of this graphics instance, that is done by DrawTransformed. The optional last parameter of this method is the 'source' rectangle. This defines the region that will be transformed onto the given key sequence region. By default, this is a rectangle from 0,0 to the entire canvas width and height.

Example

Example

Source Rectangle

This is the region which will be transformed to the key sequence region. If omitted, the whole canvas is used. This can be arbitrarily chosen by the developer when coding the layer. By allowing custom regions instead of just defaulting to the whole canvas, additional optimisations can be made. In the example above, since we know that we want the left column (red and green blocks) and the top row (red and yellow) to take up 3/8ths of the available space and we want the right column and bottom row to take up 5/8ths of the available space, we can program it to use a set size of 80 and widths/heights of 30 and 50 respectively. Compare that to if we were using the canvas width and height. We would have to get the width and multiply by 0.375 or 0.625 respectively. This means the computer is having to do additional calculations when these can be left to the transform instead.

Matrix Configuration (Advanced)

The matrix can be configured with additional transformations to allow for further or conditional customisation of the transformations. The configure action is invoked after the scaling, rotation and translation has been performed. Note that this means you can configure the matrix before or after these three transformations using either MatrixOrder.Prepend or MatrixOrder.Append respectively.


Default process
Default process order


As an example, lets setup the configure method so that the drawing is mirrored in in the Y direction before being transformed. We will also use the code from the above example as our drawing.

To do this correctly, we need to flip n the Y direction before the transform is scaled, rotated and translated. Otherwise, this will flip the rotated image, and it will be rotated the wrong direction!

So, let's think. We need to first scale our image by -1 in the Y direction. We can do that with matrix.Scale(1, -1). Since we can only scale around the point (0, 0), and our drawing is not centered around (0, 0), we will also need to translate it to put the image back in the correct place. If we flip about (0, 0) in the Y, the image's top left bound is now effectively (0, -80). We need to translate it back so that the top left is (0, 0), so we can do matrix.Translate(0, 80).

Since we need to do this before the existing matrix, we can use MatrixOrder.Prepend. So our code is this, right?

mtx.Scale(1, -1, MatrixOrder.Prepend);
mtx.Translate(0, 80, MatrixOrder.Prepend);

Not quite. Because we are prepending to the start of the transform, the first line of code adds the scale to the front of the transformation, the second line adds the translation to the front of the transformation (which means it ends up before the scale, and thus translates the wrong way).

So, when dealing with Prepend, we need to swap the order of these lines. Our final code that flips in the Y direction, is this:

DrawTransformed(
	Properties.Sequence,
	mtx => {
		mtx.Translate(0, 80, MatrixOrder.Prepend);
		mtx.Scale(1, -1, MatrixOrder.Prepend);
	},
	gfx => { ... },
	new RectangleF(0, 0, 80, 80)
);

This image hopefully shows how the resulting transformation behaves after each line in the configuration action. Notice how the translate is seemingly added in the wrong place (making the transformation wrong) until the scale is added in.

Our correct custom matrix configuration

Wibble199 and others added 6 commits November 18, 2019 11:13
…the area provided by a KeySequence, taking scaling, rotation and translation into account.

This can be used to draw complex content in the target region without requiring complex maths to render it correctly.
… matrix before the render is performed.

This allows actions such as mirroring to take place and still be able to easily utilise the method.
@diogotr7 diogotr7 merged commit 86a13ac into antonpup:dev Mar 24, 2020
@Wibble199 Wibble199 deleted the feature/effectlayer-matrix-transform-render branch March 27, 2020 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants