Skip to content

Commit

Permalink
Add rotation support back in from the parent repo (#5)
Browse files Browse the repository at this point in the history
- SSIV has an imageRotation field that can be changed, no EXIF parsing is done
  • Loading branch information
dwagner-gh authored Aug 23, 2024
1 parent b8e1b0e commit faa0a72
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.davemorrissey.labs.subscaleview

enum class ImageRotation(val rotation: Int) {
ROTATION_0(0), ROTATION_90(90), ROTATION_180(180), ROTATION_270(270);

fun rotateBy90Degrees(): ImageRotation = when (this) {
ROTATION_0 -> ROTATION_90
ROTATION_90 -> ROTATION_180
ROTATION_180 -> ROTATION_270
ROTATION_270 -> ROTATION_0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ public class SubsamplingScaleImageView extends View {
// Source image dimensions and orientation - dimensions relate to the unrotated image
private int sWidth;
private int sHeight;
private ImageRotation imageRotation = ImageRotation.ROTATION_0;
// Min scale allowed (prevent infinite zoom)
private float minScale = minScale();
private Rect sRegion;
Expand Down Expand Up @@ -536,12 +537,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = parentHeight;
if (sWidth > 0 && sHeight > 0) {
if (resizeWidth && resizeHeight) {
width = sWidth;
height = sHeight;
width = getEffectiveSWidth();
height = getEffectiveSHeight();
} else if (resizeHeight) {
height = (int) ((((double) sHeight / (double) sWidth) * width));
height = (int) ((((double) getEffectiveSHeight() / (double) getEffectiveSWidth()) * width));
} else if (resizeWidth) {
width = (int) ((((double) sWidth / (double) sHeight) * height));
width = (int) ((((double) getEffectiveSWidth() / (double) getEffectiveSHeight()) * height));
}
}
width = Math.max(width, getSuggestedMinimumWidth());
Expand Down Expand Up @@ -606,6 +607,8 @@ public boolean onTouchEvent(@NonNull MotionEvent event) {
@SuppressWarnings("deprecation")
private boolean onTouchEventInternal(@NonNull MotionEvent event) {
int touchCount = event.getPointerCount();
int sHeight = getEffectiveSHeight();
int sWidth = getEffectiveSWidth();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
Expand Down Expand Up @@ -851,8 +854,8 @@ private void doubleTapZoom(PointF sCenter, PointF vFocus) {
sCenter.y = sRequestedCenter.y;
} else {
// With no requested center, scale around the image center.
sCenter.x = sWidth / 2;
sCenter.y = sHeight / 2;
sCenter.x = getEffectiveSWidth() / 2;
sCenter.y = getEffectiveSHeight() / 2;
}
}
float doubleTapZoomScale = Math.min(maxScale, SubsamplingScaleImageView.this.doubleTapZoomScale);
Expand Down Expand Up @@ -967,7 +970,17 @@ protected void onDraw(Canvas canvas) {
}
matrix.reset();
setMatrixArray(srcArray, 0, 0, tile.bitmap.getWidth(), 0, tile.bitmap.getWidth(), tile.bitmap.getHeight(), 0, tile.bitmap.getHeight());
setMatrixArray(dstArray, tile.vRect.left, tile.vRect.top, tile.vRect.right, tile.vRect.top, tile.vRect.right, tile.vRect.bottom, tile.vRect.left, tile.vRect.bottom);

switch (getImageRotation()) {
case ROTATION_0 ->
setMatrixArray(dstArray, tile.vRect.left, tile.vRect.top, tile.vRect.right, tile.vRect.top, tile.vRect.right, tile.vRect.bottom, tile.vRect.left, tile.vRect.bottom);
case ROTATION_90 ->
setMatrixArray(dstArray, tile.vRect.right, tile.vRect.top, tile.vRect.right, tile.vRect.bottom, tile.vRect.left, tile.vRect.bottom, tile.vRect.left, tile.vRect.top);
case ROTATION_180 ->
setMatrixArray(dstArray, tile.vRect.right, tile.vRect.bottom, tile.vRect.left, tile.vRect.bottom, tile.vRect.left, tile.vRect.top, tile.vRect.right, tile.vRect.top);
case ROTATION_270 ->
setMatrixArray(dstArray, tile.vRect.left, tile.vRect.bottom, tile.vRect.left, tile.vRect.top, tile.vRect.right, tile.vRect.top, tile.vRect.right, tile.vRect.bottom);
}
matrix.setPolyToPoly(srcArray, 0, dstArray, 0, 4);
canvas.drawBitmap(tile.bitmap, matrix, bitmapPaint);
if (debug) {
Expand All @@ -992,8 +1005,15 @@ protected void onDraw(Canvas canvas) {
}
matrix.reset();
matrix.postScale(xScale, yScale);
matrix.postRotate(getImageRotation().getRotation());
matrix.postTranslate(vTranslate.x, vTranslate.y);

switch (getImageRotation()) {
case ROTATION_90 -> matrix.postTranslate(scale * sHeight, 0);
case ROTATION_180 -> matrix.postTranslate(scale * sWidth, scale * sHeight);
case ROTATION_270 -> matrix.postTranslate(0, scale * sWidth);
}

if (tileBgPaint != null) {
if (sRect == null) {
sRect = new RectF();
Expand Down Expand Up @@ -1259,6 +1279,9 @@ private int calculateInSampleSize(float scale) {
scale = (minimumTileDpi / averageDpi) * scale;
}

int sWidth = getEffectiveSWidth();
int sHeight = getEffectiveSHeight();

int reqWidth = (int) (sWidth * scale);
int reqHeight = (int) (sHeight * scale);

Expand Down Expand Up @@ -1304,8 +1327,8 @@ private void fitToBounds(boolean center, ScaleAndTranslate sat) {

PointF vTranslate = sat.vTranslate;
float scale = limitedScale(sat.scale);
float scaleWidth = scale * sWidth;
float scaleHeight = scale * sHeight;
float scaleWidth = scale * getEffectiveSWidth();
float scaleHeight = scale * getEffectiveSHeight();

boolean extra = panLimit == PAN_LIMIT_INSIDE;
float extraLeft = extra ? vExtraSpaceLeft : 0;
Expand Down Expand Up @@ -1368,7 +1391,7 @@ private void fitToBounds(boolean center) {
scale = satTemp.scale;
vTranslate.set(satTemp.vTranslate);
if (init) {
vTranslate.set(vTranslateForSCenter(sWidth / 2, sHeight / 2, scale));
vTranslate.set(vTranslateForSCenter((float) getEffectiveSWidth() / 2, (float) getEffectiveSHeight() / 2, scale));
}
}

Expand All @@ -1381,6 +1404,8 @@ private void initialiseTileMap(Point maxTileDimensions) {
int sampleSize = fullImageSampleSize;
int xTiles = 1;
int yTiles = 1;
int sWidth = getEffectiveSWidth();
int sHeight = getEffectiveSHeight();
while (true) {
int sTileWidth = sWidth / xTiles;
int sTileHeight = sHeight / yTiles;
Expand Down Expand Up @@ -1545,13 +1570,63 @@ private Point getMaxBitmapDimensions(Canvas canvas) {
return new Point(Math.min(canvas.getMaximumBitmapWidth(), maxTileWidth), Math.min(canvas.getMaximumBitmapHeight(), maxTileHeight));
}

/**
* Get source width taking rotation into account.
*/
@SuppressWarnings("SuspiciousNameCombination")
private int getEffectiveSWidth() {
ImageRotation rotation = getImageRotation();
if (rotation == ImageRotation.ROTATION_90 || rotation == ImageRotation.ROTATION_270) {
return sHeight;
} else {
return sWidth;
}
}

/**
* Get source height taking rotation into account.
*/
@SuppressWarnings("SuspiciousNameCombination")
private int getEffectiveSHeight() {
ImageRotation rotation = getImageRotation();
if (rotation == ImageRotation.ROTATION_90 || rotation == ImageRotation.ROTATION_270) {
return sWidth;
} else {
return sHeight;
}
}

/**
* Converts source rectangle from tile, which treats the image file as if it were in the correct orientation already,
* to the rectangle of the image that needs to be loaded.
*/
@SuppressWarnings("SuspiciousNameCombination")
@AnyThread
private void fileSRect(Rect sRect, Rect target) {
target.set(sRect);
ImageRotation rotation = getImageRotation();

switch (rotation) {
case ROTATION_0 ->
target.set(sRect);
case ROTATION_90 ->
target.set(sRect.top, sHeight - sRect.right, sRect.bottom, sHeight - sRect.left);
case ROTATION_180 ->
target.set(sWidth - sRect.right, sHeight - sRect.bottom, sWidth - sRect.left, sHeight - sRect.top);
case ROTATION_270 ->
target.set(sWidth - sRect.bottom, sRect.left, sWidth - sRect.top, sRect.right);
}
}

public ImageRotation getImageRotation() {
return imageRotation;
}

public void setImageRotation(ImageRotation rotation) {
this.imageRotation = rotation;

reset(false);
invalidate();
requestLayout();
}

/**
Expand Down Expand Up @@ -1825,6 +1900,8 @@ private float minScale() {

int vPadding = getPaddingBottom() + getPaddingTop() + vExtra;
int hPadding = getPaddingLeft() + getPaddingRight() + hExtra;
int sWidth = getEffectiveSWidth();
int sHeight = getEffectiveSHeight();
switch (minimumScaleType) {
case SCALE_TYPE_CENTER_INSIDE:
default:
Expand Down Expand Up @@ -1939,8 +2016,8 @@ public final void getPanRemaining(RectF vTarget) {
return;
}

float scaleWidth = scale * sWidth;
float scaleHeight = scale * sHeight;
float scaleWidth = scale * getEffectiveSWidth();
float scaleHeight = scale * getEffectiveSHeight();

if (panLimit == PAN_LIMIT_CENTER) {
vTarget.top = Math.max(0, -(vTranslate.y - (getHeight() / 2)));
Expand Down Expand Up @@ -2215,7 +2292,7 @@ public final void resetScaleAndCenter() {
this.anim = null;
this.pendingScale = limitedScale(0);
if (isReady()) {
this.sPendingCenter = new PointF(sWidth / 2, sHeight / 2);
this.sPendingCenter = new PointF(getEffectiveSWidth() / 2, getEffectiveSHeight() / 2);
} else {
this.sPendingCenter = new PointF(0, 0);
}
Expand Down Expand Up @@ -2348,8 +2425,8 @@ public final boolean isPanEnabled() {
public final void setPanEnabled(boolean panEnabled) {
this.panEnabled = panEnabled;
if (!panEnabled && vTranslate != null) {
vTranslate.x = (getWidth() / 2) - (scale * (sWidth / 2));
vTranslate.y = (getHeight() / 2) - (scale * (sHeight / 2));
vTranslate.x = (getWidth() / 2) - (scale * (getEffectiveSWidth() / 2));
vTranslate.y = (getHeight() / 2) - (scale * (getEffectiveSHeight() / 2));
if (isReady()) {
refreshRequiredTiles(true);
invalidate();
Expand Down Expand Up @@ -2745,6 +2822,11 @@ protected Bitmap doInBackground(Void... params) {
view.decoderLock.readLock().lock();
try {
if (decoder.isReady()) {
// Update tile's file sRect according to rotation
view.fileSRect(tile.sRect, tile.fileSRect);
if (view.sRegion != null) {
tile.fileSRect.offset(view.sRegion.left, view.sRegion.top);
}
return decoder.decodeRegion(tile.fileSRect, tile.sampleSize);
} else {
tile.loading = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class ImageDisplayRotateFragment : Fragment() {
ImageSource.asset(requireContext(), "swissroad.jpg")
)

binding.rotate.setOnClickListener {
binding.imageView.imageRotation = binding.imageView.imageRotation.rotateBy90Degrees()
}

return binding.root
}
}
2 changes: 1 addition & 1 deletion sample/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
</string>
<string name="display.p2.subtitle">Rotation</string>
<string name="display.p2.text">
This image has been rotated 90 degrees. Tap the button to rotate it. EXIF rotation is supported for external files.
This image can be rotated by 90 degree increments. Tap the button to do so. EXIF data is not parsed, do it yourself!
</string>
<string name="display.p3.subtitle">Display region</string>
<string name="display.p3.text">Set the region to display instead of the whole image.</string>
Expand Down

0 comments on commit faa0a72

Please sign in to comment.