-
Notifications
You must be signed in to change notification settings - Fork 184
[Win32] Reuse zoom handle for drawing image at size if appropriate #2634
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,33 +141,66 @@ public final class Image extends Resource implements Drawable { | |
|
||
private List<Consumer<Image>> onDisposeListeners; | ||
|
||
private record CachedHandle(ImageHandle handleContainer, int requestedWidth, int requestedHeight) { | ||
private class HandleAtSize { | ||
private ImageHandle handleContainer = null; | ||
private int requestedWidth = -1; | ||
private int requestedHeight = -1; | ||
|
||
public void destroy() { | ||
if (handleContainer != null) { | ||
if (handleContainer != null && !zoomLevelToImageHandle.containsValue(handleContainer)) { | ||
handleContainer.destroy(); | ||
} | ||
handleContainer = null; | ||
requestedWidth = -1; | ||
requestedHeight = -1; | ||
} | ||
|
||
public boolean isReusable(int height, int width) { | ||
if(handleContainer == null) { | ||
public ImageHandle refresh(int width, int height) { | ||
if (!isReusable(width, height)) { | ||
destroy(); | ||
requestedWidth = width; | ||
requestedHeight = height; | ||
handleContainer = createHandleAtExactSize(width, height) | ||
.orElseGet(() -> getOrCreateImageHandleAtClosestSize(width, height)); | ||
} | ||
return handleContainer; | ||
} | ||
|
||
private boolean isReusable(int width, int height) { | ||
if (handleContainer == null) { | ||
return false; | ||
} | ||
return (requestedHeight == height && requestedWidth == width) | ||
|| (handleContainer.height == height && handleContainer.width == width); | ||
} | ||
|
||
private Optional<ImageHandle> createHandleAtExactSize(int widthHint, int heightHint) { | ||
Optional<ImageData> imageData = imageProvider.loadImageDataAtExactSize(widthHint, heightHint); | ||
if (imageData.isPresent()) { | ||
ImageData adaptedData = adaptImageDataIfDisabledOrGray(imageData.get()); | ||
ImageHandle imageHandle = init(adaptedData, -1); | ||
return Optional.of(imageHandle); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
public long getHandle() { | ||
if (handleContainer != null) { | ||
return handleContainer.handle; | ||
private ImageHandle getOrCreateImageHandleAtClosestSize(int widthHint, int heightHint) { | ||
Rectangle bounds = getBounds(100); | ||
int imageZoomForWidth = 100 * widthHint / bounds.width; | ||
int imageZoomForHeight = 100 * heightHint / bounds.height; | ||
int imageZoom = DPIUtil.getZoomForAutoscaleProperty(Math.max(imageZoomForWidth, imageZoomForHeight)); | ||
ImageHandle bestFittingHandle = zoomLevelToImageHandle.get(imageZoom); | ||
if (bestFittingHandle == null) { | ||
ImageData bestFittingImageData = imageProvider.loadImageData(imageZoom).element(); | ||
ImageData adaptedData = adaptImageDataIfDisabledOrGray(bestFittingImageData); | ||
bestFittingHandle = init(adaptedData, -1); | ||
} | ||
return -1; | ||
return bestFittingHandle; | ||
} | ||
}; | ||
|
||
// Initialize lastRequestedHandle with -1 for size-related fields to indicate uninitialized values | ||
CachedHandle lastRequestedHandle = new CachedHandle(null, -1, -1); | ||
} | ||
|
||
private final HandleAtSize lastRequestedHandle = new HandleAtSize(); | ||
|
||
private Image (Device device, int type, long handle, int nativeZoom) { | ||
super(device); | ||
|
@@ -854,15 +887,8 @@ interface HandleAtSizeConsumer { | |
} | ||
|
||
void executeOnImageHandleAtSize(HandleAtSizeConsumer handleAtSizeConsumer, int widthHint, int heightHint) { | ||
if (!lastRequestedHandle.isReusable(heightHint, widthHint)) { | ||
ImageData imageData; | ||
imageData = this.imageProvider.loadImageDataAtSize(widthHint, heightHint); | ||
lastRequestedHandle.destroy(); | ||
ImageHandle handleContainer = init(imageData, -1); | ||
lastRequestedHandle = new CachedHandle(handleContainer, widthHint, heightHint); | ||
} | ||
handleAtSizeConsumer.accept(lastRequestedHandle.getHandle(), | ||
new Point(lastRequestedHandle.handleContainer().width, lastRequestedHandle.handleContainer().height)); | ||
ImageHandle imageHandle = lastRequestedHandle.refresh(widthHint, heightHint); | ||
handleAtSizeConsumer.accept(imageHandle.handle, new Point(imageHandle.width, imageHandle.height)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we are now passing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have created: We should merge that one first, so I can adapt this on top of that refactoring. |
||
} | ||
|
||
/** | ||
|
@@ -1096,8 +1122,8 @@ void destroy () { | |
} | ||
|
||
private void destroyHandles() { | ||
destroyHandles(__ -> true); | ||
lastRequestedHandle.destroy(); | ||
destroyHandles(__ -> true); | ||
} | ||
|
||
@Override | ||
|
@@ -2007,22 +2033,6 @@ ElementAtZoom<ImageData> getClosestAvailableImageData(int zoom) { | |
return new ElementAtZoom<>(getImageMetadata(new ZoomContext(closestZoom)).getImageData(), closestZoom); | ||
} | ||
|
||
public ImageData loadImageDataAtSize(int widthHint, int heightHint) { | ||
Optional<ImageData> exact = loadImageDataAtExactSize(widthHint, heightHint); | ||
if (exact.isPresent()) { | ||
return adaptImageDataIfDisabledOrGray(exact.get()); | ||
} | ||
return loadImageDataClosestTo(widthHint, heightHint); | ||
} | ||
|
||
private ImageData loadImageDataClosestTo(int targetWidth, int targetHeight) { | ||
Rectangle bounds = getBounds(100); | ||
int imageZoomForWidth = 100 * targetWidth / bounds.width; | ||
int imageZoomForHeight = 100 * targetHeight / bounds.height; | ||
int imageZoom = DPIUtil.getZoomForAutoscaleProperty(Math.max(imageZoomForWidth, imageZoomForHeight)); | ||
return loadImageData(imageZoom).element(); | ||
} | ||
|
||
protected Optional<ImageData> loadImageDataAtExactSize(int width, int height) { | ||
return Optional.empty(); // exact size not available | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesnt the
loadImageData(zoom)
already do theadaptImageDataIfDisabledOrGray
, so I think this is not needed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I am not mistaken,
adaptImageDataIfDisabledOrGray()
is only called at places where a handle is created, such asinitializeHandleFromSource()
. None of theloadIamgeData()
implementations seems to call it. Can you please check if I miss something, @arunjose696?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the following call stack
loadImageData(int) -> getImageMetadata(zoomContext) -> newImageHandle(zoomContext)-> initializeHandleFromSource(zoomContext) -> adaptImageDataIfDisabledOrGray()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. This seems to be an inconsistency that some
loadImageData()
implementations adapt the image data while others don't. Precisely, thePlainImageProviderWrapper
andExistingImageHandlerProviderWrapper
seem to be the only ones that adapt them (via callinggetClosedAvailableImageData()
). We should try to streamline that (independent of this PR), as we unnecessarily adapt images twice (and maybe there are even some that we do not adapt at all).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this could be streamlined in a seperate PR if running adaptImageDataIfDisabledOrGray is a no-op if applied for second time, I could see in addition to
PlainImageProviderWrapper
andExistingImageHandlerProviderWrapper
, the ImageGCDrawer wrapper also callsadaptImageDataIfDisabledOrGray