feat: rotate (Shift+sag tik), StartupWMClass taskbar gruplama, dist guncelle
Some checks failed
Build Windows / build-windows (push) Has been cancelled
Some checks failed
Build Windows / build-windows (push) Has been cancelled
This commit is contained in:
@@ -37,6 +37,7 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
|
||||
void _clamp() {
|
||||
if (_vpSize.width <= 0 || _vpSize.height <= 0) return;
|
||||
if (_angle != 0) return; // rotate varsa serbest pan
|
||||
if (_isFilled) {
|
||||
final double w = _vpSize.width * _sc;
|
||||
final double h = _vpSize.height * _sc;
|
||||
@@ -153,15 +154,23 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
|
||||
void _handleScroll(PointerScrollEvent e) {
|
||||
if (_vpSize.width <= 0 || _vpSize.height <= 0) return;
|
||||
final double vx = e.localPosition.dx;
|
||||
final double vy = e.localPosition.dy;
|
||||
final double vx = _angle == 0 ? e.localPosition.dx : _vpSize.width / 2;
|
||||
final double vy = _angle == 0 ? e.localPosition.dy : _vpSize.height / 2;
|
||||
final double cx = (vx - _tx) / _sc;
|
||||
final double cy = (vy - _ty) / _sc;
|
||||
const double factor = 1.1;
|
||||
final double rawSc = _sc * (e.scrollDelta.dy < 0 ? factor : 1.0 / factor);
|
||||
final double newSc = rawSc.clamp(1.0, 10.0);
|
||||
_tx = _tx + cx * _sc - cx * newSc;
|
||||
_ty = _ty + cy * _sc - cy * newSc;
|
||||
if (_angle == 0) {
|
||||
_tx = _tx + cx * _sc - cx * newSc;
|
||||
_ty = _ty + cy * _sc - cy * newSc;
|
||||
} else {
|
||||
// Rotate'li zoom: viewport merkezini koru
|
||||
final double dw = _vpSize.width * (newSc - _sc);
|
||||
final double dh = _vpSize.height * (newSc - _sc);
|
||||
_tx -= dw / 2;
|
||||
_ty -= dh / 2;
|
||||
}
|
||||
_sc = newSc;
|
||||
_clamp();
|
||||
setState(() {});
|
||||
@@ -235,32 +244,39 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
_interactionMode = 'none';
|
||||
}
|
||||
|
||||
// ── Color filters ──
|
||||
// ── Combined color filter (saturation × contrast) ──
|
||||
|
||||
static const double _rLum = 0.2126;
|
||||
static const double _gLum = 0.7152;
|
||||
static const double _bLum = 0.0722;
|
||||
|
||||
ColorFilter _buildSaturationFilter() {
|
||||
final s = _saturation;
|
||||
final invS = 1.0 - s;
|
||||
return ColorFilter.matrix(<double>[
|
||||
invS * _rLum + s, invS * _gLum, invS * _bLum, 0, 0,
|
||||
invS * _rLum, invS * _gLum + s, invS * _bLum, 0, 0,
|
||||
invS * _rLum, invS * _gLum, invS * _bLum + s, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
]);
|
||||
}
|
||||
ColorFilter? _cachedFilter;
|
||||
double _cachedSat = -1;
|
||||
double _cachedCon = -1;
|
||||
|
||||
ColorFilter _buildContrastFilter() {
|
||||
ColorFilter _buildCombinedFilter() {
|
||||
if (_cachedFilter != null && _cachedSat == _saturation && _cachedCon == _contrast) {
|
||||
return _cachedFilter!;
|
||||
}
|
||||
_cachedSat = _saturation;
|
||||
_cachedCon = _contrast;
|
||||
|
||||
final s = _saturation;
|
||||
final c = _contrast;
|
||||
final t = (1.0 - c) / 2.0;
|
||||
return ColorFilter.matrix(<double>[
|
||||
c, 0, 0, 0, t,
|
||||
0, c, 0, 0, t,
|
||||
0, 0, c, 0, t,
|
||||
0, 0, 0, 1, 0,
|
||||
final invS = 1.0 - s;
|
||||
|
||||
final sR_R = invS * _rLum + s, sR_G = invS * _gLum, sR_B = invS * _bLum;
|
||||
final sG_R = invS * _rLum, sG_G = invS * _gLum + s, sG_B = invS * _bLum;
|
||||
final sB_R = invS * _rLum, sB_G = invS * _gLum, sB_B = invS * _bLum + s;
|
||||
|
||||
_cachedFilter = ColorFilter.matrix(<double>[
|
||||
c * sR_R, c * sR_G, c * sR_B, 0, t,
|
||||
c * sG_R, c * sG_G, c * sG_B, 0, t,
|
||||
c * sB_R, c * sB_G, c * sB_B, 0, t,
|
||||
0, 0, 0, 1, 0,
|
||||
]);
|
||||
return _cachedFilter!;
|
||||
}
|
||||
|
||||
Widget _buildImage() {
|
||||
@@ -284,6 +300,29 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
final vpCenterY = _vpSize.height / 2;
|
||||
final angleRad = _angle * math.pi / 180;
|
||||
|
||||
Widget imageContent = Builder(
|
||||
builder: (context) {
|
||||
final hasFilter = _saturation != 1.0 || _contrast != 1.0;
|
||||
final image = _isFilled
|
||||
? SizedBox.expand(child: _buildImage())
|
||||
: Center(child: _buildImage());
|
||||
return hasFilter
|
||||
? ColorFiltered(colorFilter: _buildCombinedFilter(), child: image)
|
||||
: image;
|
||||
},
|
||||
);
|
||||
|
||||
if (_angle != 0) {
|
||||
imageContent = Transform(
|
||||
alignment: Alignment.topLeft,
|
||||
transform: Matrix4.identity()
|
||||
..translate(vpCenterX, vpCenterY)
|
||||
..rotateZ(angleRad)
|
||||
..translate(-vpCenterX, -vpCenterY),
|
||||
child: imageContent,
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox.expand(
|
||||
child: Stack(
|
||||
children: [
|
||||
@@ -301,20 +340,9 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
child: Transform(
|
||||
alignment: Alignment.topLeft,
|
||||
transform: Matrix4.identity()
|
||||
..translate(vpCenterX, vpCenterY)
|
||||
..rotateZ(angleRad)
|
||||
..translate(-vpCenterX, -vpCenterY)
|
||||
..translate(_tx, _ty)
|
||||
..scale(_sc),
|
||||
child: ColorFiltered(
|
||||
colorFilter: _buildSaturationFilter(),
|
||||
child: ColorFiltered(
|
||||
colorFilter: _buildContrastFilter(),
|
||||
child: _isFilled
|
||||
? SizedBox.expand(child: _buildImage())
|
||||
: Center(child: _buildImage()),
|
||||
),
|
||||
),
|
||||
child: imageContent,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user