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

macOS Ti.Blob/Image scale incorrect #12445

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,12 @@ timestamps {
// Run unit tests in parallel for android/iOS
stage('Test') {
parallel(
'android main unit tests': androidUnitTests('main', nodeVersion, npmVersion, testOnAndroidDevices, null),
'android 5.0 unit tests': androidUnitTests('5.0', nodeVersion, npmVersion, false, 'android-21-x86'),
'iPhone unit tests': iosUnitTests('iphone', nodeVersion, npmVersion, testOnIOSDevices),
'iPad unit tests': iosUnitTests('ipad', nodeVersion, npmVersion, testOnIOSDevices),
// 'android main unit tests': androidUnitTests('main', nodeVersion, npmVersion, testOnAndroidDevices, null),
// 'android 5.0 unit tests': androidUnitTests('5.0', nodeVersion, npmVersion, false, 'android-21-x86'),
// 'iPhone unit tests': iosUnitTests('iphone', nodeVersion, npmVersion, testOnIOSDevices),
// 'iPad unit tests': iosUnitTests('ipad', nodeVersion, npmVersion, testOnIOSDevices),
'macOS unit tests': macosUnitTests(nodeVersion, npmVersion),
'cli unit tests': cliUnitTests(nodeVersion, npmVersion),
// 'cli unit tests': cliUnitTests(nodeVersion, npmVersion),
failFast: false
)
}
Expand Down
1 change: 1 addition & 0 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiBlob.m
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ - (id)initWithImage:(UIImage *)image_
{
if (self = [super init]) {
image = [image_ retain];
NSLog(@"[WARN] Blob, wrapping image w/ scale %f", image.scale);
type = TiBlobTypeImage;
mimetype = [([UIImageAlpha hasAlpha:image_] ? MIMETYPE_PNG : MIMETYPE_JPEG) copy];
}
Expand Down
46 changes: 42 additions & 4 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiViewProxy.m
Original file line number Diff line number Diff line change
Expand Up @@ -696,11 +696,28 @@ - (TiBlob *)toImage:(id)args
^{
BOOL viewIsAttached = [self viewAttached];
if (!viewIsAttached) {
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(), the view was NOT attached!");
[self windowWillOpen];
}
TiUIView *myview = [self view];
CGSize size = myview.bounds.size;
CGRect bounds = myview.bounds;
CGRect frame = myview.frame;
CGSize size = bounds.size;
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(), the view's content scale factor is: %f", myview.contentScaleFactor);
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(),frame width: %f, frame height: %f", frame.size.width, frame.size.height);
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(), initial width: %f, initial height: %f", size.width, size.height);
#if TARGET_OS_MACCATALYST
// FIXME: macOS Catalina has a "known issue" around nativeScale, see https://developer.apple.com/documentation/macos-release-notes/macos-catalina-10_15-release-notes
// Try and detect when we have non-integer sizes *and* a scale of 1
// It's a tell-tale sign that macOS Catalyst did us dirty and gave us bad bounds
if ((ceilf(size.width) != size.width || ceilf(size.height) != size.height) && UITraitCollection.currentTraitCollection.displayScale == 1.0) {
// FIXME: Only issue is: this doesn't work for cases where it gave us bad bounds that happened to be even numbers originally (i.e. 6pts for 12px)
// TODO: OK, so how do we fix this?!
CGSize s = [[self view] sizeThatFits:CGSizeMake(1000, 1000)];
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(), using sizeThatFits to gather width: %f, height: %f", s.width, s.height);
bounds = CGRectMake(0, 0, s.width, s.height);
}
#endif
if (CGSizeEqualToSize(size, CGSizeZero) || size.width == 0 || size.height == 0) {
#ifndef TI_USE_AUTOLAYOUT
CGFloat width = [self autoWidthForSize:CGSizeMake(1000, 1000)];
Expand All @@ -713,9 +730,11 @@ - (TiBlob *)toImage:(id)args

if (width > 0 && height > 0) {
size = CGSizeMake(width, height);
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(), using sizeThatFits to gather width: %f, height: %f", size.width, size.height);
}
if (CGSizeEqualToSize(size, CGSizeZero) || width == 0 || height == 0) {
size = [UIScreen mainScreen].bounds.size;
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(), using UIScreen.mainScreen.bounds.size to gather width: %f, height: %f", size.width, size.height);
}
CGRect rect = CGRectMake(0, 0, size.width, size.height);
[TiUtils setView:myview positionRect:rect];
Expand All @@ -725,12 +744,31 @@ - (TiBlob *)toImage:(id)args
[self layoutChildren:NO];
}

UIGraphicsBeginImageContextWithOptions(size, [myview.layer isOpaque], (honorScale ? 0.0 : 1.0));
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(), width: %f, height: %f", size.width, size.height);
CGFloat scale = (honorScale ? 0.0 : 1.0);
NSLog(@"[WARN] Rendering Ti.UI.View.toImage(), w/ scale %f", scale);
NSLog(@"[WARN] mainScreen.nativeScale is %f", UIScreen.mainScreen.nativeScale);
NSLog(@"[WARN] Number of screens is %u", UIScreen.screens.count);
if (scale == 0.0) {
NSLog(@"[WARN] Set to honor device main screen scale, which is %f", UIScreen.mainScreen.scale);
NSLog(@"[WARN] Current Trait collection says scale is %f", UITraitCollection.currentTraitCollection.displayScale);
}
// New API
// UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
// UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
// [myview drawViewHierarchyInRect:bounds afterScreenUpdates:true];
// }];
// End New API

// Old API
UIGraphicsBeginImageContextWithOptions(size, [myview.layer isOpaque], scale);
[myview drawViewHierarchyInRect:bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
blob = [[[TiBlob alloc] initWithImage:image] autorelease];
[blob setMimeType:@"image/png" type:TiBlobTypeImage];
UIGraphicsEndImageContext();
// End Old API

blob = [[[TiBlob alloc] initWithImage:image] autorelease];
[blob setMimeType:@"image/png" type:TiBlobTypeImage]; // explicitly force to PNG regardless of alpha
if (callback != nil) {
[callback call:@[ blob ] thisObject:self];
}
Expand Down
Binary file modified tests/Resources/ios/snapshots/systemredcolor_light.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion tests/Resources/ti.blob.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,11 @@ describe('Titanium.Blob', function () {
win.removeEventListener('postlayout', postlayout); // only run once
try {
const blob = view.toImage();
should(blob.width).equal(11);
should(blob.width).equal(11); // FIXME: SOmetimes macOS will report 6 here!
should(blob.height).equal(13);
} catch (e) {
// when we fail, should we spit out details on scale?
console.log(Ti.Platform.displayCaps.logicalDensityFactor);
return finish(e);
}
finish();
Expand Down
12 changes: 10 additions & 2 deletions tests/Resources/utilities/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const PNG = require('pngjs').PNG;
const cgbiToPng = isIOSDevice ? require('cgbi-to-png') : { revert: buf => buf };
const pixelmatch = require('pixelmatch');

// Store the device scale and see if we ever report a different value.
// I suspect sometimes macOS will because of some OS modal popping up or something
let deviceScale = null;
// Copied from newer should.js
// Verifies the descriptor for an own property on a target
should.Assertion.add('ownPropertyWithDescriptor', function (name, desc) {
Expand Down Expand Up @@ -160,6 +163,11 @@ class ImageMatchDetails {
*/
possibleInputs() {
const density = Ti.Platform.displayCaps.logicalDensityFactor;
if (deviceScale === null) {
deviceScale = density;
} else if (deviceScale !== density) {
console.error(`The device scale changed (to ${density}) from what we first recorded (${deviceScale})!`);
}
const withoutSuffix = this.inputFilename.substr(0, this.inputFilename.length - 4);
const result = [];
if (density !== 1) {
Expand Down Expand Up @@ -229,13 +237,13 @@ should.Assertion.add('matchImage', function (image, options = { threshold: 0.1,
} else {
const details = new ImageMatchDetails(image);
outputFilePath = details.outputFile();
const possibleInputs = details.possibleInputs();
this.params = {
obj: this.obj.apiName,
operator: `to match image ('${outputFilePath}')`
operator: `to match one of the images ('${possibleInputs}')`
};

// Determine if any of the possible input images to match against exist
const possibleInputs = details.possibleInputs();
let snapshot;
let inputFilename;
for (const filepath of possibleInputs) {
Expand Down