Skip to content

Package: AnimatedScaleController

AnimatedScaleController

nameinstructionbranchcomplexitylinemethod
AnimatedScaleController(EditableImageRenderer)
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
bound(double)
M: 14 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
changeScale()
M: 105 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 21 C: 0
0%
M: 1 C: 0
0%
isRunning()
M: 7 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
lambda$changeScale$1(double)
M: 30 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
lambda$setScale$0(ActionEvent)
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setScale(double, Point)
M: 101 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 22 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * *********************************************************************************************************************
3: *
4: * Mistral: open source imaging engine
5: * http://tidalwave.it/projects/mistral
6: *
7: * Copyright (C) 2003 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *********************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
12: * the License. You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/mistral-src
23: * git clone https://github.com/tidalwave-it/mistral-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.image.render;
28:
29: import java.awt.Point;
30: import javax.swing.SwingUtilities;
31: import javax.swing.Timer;
32: import it.tidalwave.image.Quality;
33: import lombok.extern.slf4j.Slf4j;
34:
35: /***********************************************************************************************************************
36: *
37: * A special <code>ScaleController</code> which performs smooth scale transitions.
38: *
39: * @author Fabrizio Giudici
40: *
41: **********************************************************************************************************************/
42: @Slf4j
43: public class AnimatedScaleController extends ScaleController
44: {
45: private static final int DEFAULT_FRAMES_PER_SECOND = 20;
46: private static final int DEFAULT_DURATION = 150;
47: private int framesPerSecond = DEFAULT_FRAMES_PER_SECOND;
48: private int duration = DEFAULT_DURATION;
49:
50: /**
51: * When a zoom is performed, keep the following image point fixed.
52: */
53: private Point pivot;
54: private double startScale;
55: private double targetScale;
56: private long startTime;
57: private Timer timer;
58:
59: /**
60: * The scale quality saved at the start of the animation.
61: */
62: private Quality scaleQualitySave;
63:
64: /**
65: * The rotate quality saved at the start of the animation.
66: */
67: private Quality rotateQualitySave;
68:
69: /*******************************************************************************************************************
70: *
71: * Creates a new instance of this class, attached to the given renderer.
72: *
73: * @param imageRenderer the image renderer
74: *
75: ******************************************************************************************************************/
76: public AnimatedScaleController (final EditableImageRenderer imageRenderer)
77: {
78: super(imageRenderer);
79: }
80:
81: /*******************************************************************************************************************
82: *
83: * Returns true if the zoom animation is running.
84: *
85: * @return true if animation running
86: *
87: ******************************************************************************************************************/
88: public synchronized boolean isRunning()
89: {
90:• return timer != null;
91: }
92:
93: /*******************************************************************************************************************
94: *
95: * {@inheritDoc}
96: *
97: ******************************************************************************************************************/
98: @Override
99: public void setScale (final double scale, Point pivot)
100: {
101: log.debug("setScale(" + scale + ", " + pivot + ")");
102:
103:• if (pivot == null)
104: {
105: pivot = new Point(imageRenderer.getWidth() / 2, imageRenderer.getHeight() / 2);
106: }
107:
108: this.pivot = pivot;
109: startScale = imageRenderer.getScale();
110: targetScale = scale;
111: startTime = System.currentTimeMillis();
112:
113: synchronized (this)
114: {
115:• if (timer == null)
116: {
117: //
118: // During animation is useless to waste time for high quality.
119: // The original quality will be restored at the end of the animation.
120: //
121: scaleQualitySave = imageRenderer.getScaleQuality();
122: rotateQualitySave = imageRenderer.getRotateQuality();
123: log.debug(">>>> scale quality: " + imageRenderer.getScaleQuality());
124: log.debug(">>>> rotate quality: " + imageRenderer.getRotateQuality());
125: log.debug(">>>> temporarily setting quality to FASTEST");
126: imageRenderer.setScaleQuality(Quality.FASTEST);
127: imageRenderer.setRotateQuality(Quality.FASTEST);
128:
129: timer = new Timer(1000 / framesPerSecond,
130: e -> changeScale());
131:
132: timer.setRepeats(true);
133: timer.setInitialDelay(0);
134: timer.start();
135: }
136: }
137: }
138:
139: /*******************************************************************************************************************
140: *
141: *
142: ******************************************************************************************************************/
143: private void changeScale()
144: {
145: log.debug("changeScale()");
146:
147: final var deltaTime = System.currentTimeMillis() - startTime;
148: final var scale = bound(startScale + (((targetScale - startScale) * deltaTime) / duration));
149: imageRenderer.setScale(Math.max(Math.min(scale, EditableImageRenderer.MAX_SCALE),
150: EditableImageRenderer.MIN_SCALE), pivot);
151: log.debug(">>>> scale: " + scale + ", targetScale: " + targetScale);
152:
153:• if (Math.abs(scale - targetScale) < 1E-3)
154: {
155: //
156: // If this is the last operation in the sequence, restore original quality.
157: //
158: log.debug(">>>> scale quality: save: " + scaleQualitySave + " /// current: " +
159: imageRenderer.getScaleQuality());
160: log.debug(">>>> rotate quality: save: " + rotateQualitySave + " /// current: " +
161: imageRenderer.getRotateQuality());
162:
163:• if ((scaleQualitySave != imageRenderer.getScaleQuality()) ||
164:• (rotateQualitySave != imageRenderer.getRotateQuality()))
165: {
166: //
167: // A higher quality rendering can take too long and thus disrupt the smoothness of the animated zoom.
168: // For this reason, we have first rendered the image with the target scale in FASTEST mode, and now
169: // we schedule the final, high-quality rendering with invokeLater() - even though we already
170: // are in the Event Thread.
171: //
172: SwingUtilities.invokeLater(() ->
173: {
174: imageRenderer.setScaleQuality(scaleQualitySave);
175: imageRenderer.setRotateQuality(rotateQualitySave);
176: log.debug(">>>> quality restored to " + scaleQualitySave + ", " + rotateQualitySave);
177: imageRenderer.flushScaledImageCache();
178: imageRenderer.repaint();
179: log.debug(">>>> scale: " + scale + ", targetScale: " + targetScale);
180: });
181: }
182:
183: synchronized (this)
184: {
185:• if (timer != null)
186: {
187: log.debug(">>>> timer stopped");
188: timer.stop();
189: timer = null;
190: pivot = null;
191: }
192: }
193: }
194: }
195:
196: /*******************************************************************************************************************
197: *
198: *
199: ******************************************************************************************************************/
200: private double bound (final double scale)
201: {
202: return Math.max(Math.min(scale, Math.max(startScale, targetScale)), Math.min(startScale, targetScale));
203: }
204: }