Existe a melhor maneira de dimensionar um nsimage para um máximo de tamanho de file?
-
21-09-2019 - |
Pergunta
Aqui está o que eu tenho até agora:
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:
[file.image TIFFRepresentation]];
// Resize image to 200x200
CGFloat maxSize = 200.0;
NSSize imageSize = imageRep.size;
if (imageSize.height>maxSize || imageSize.width>maxSize) {
// Find the aspect ratio
CGFloat aspectRatio = imageSize.height/imageSize.width;
CGSize newImageSize;
if (aspectRatio > 1.0) {
newImageSize = CGSizeMake(maxSize / aspectRatio, maxSize);
} else if (aspectRatio < 1.0) {
newImageSize = CGSizeMake(maxSize, maxSize * aspectRatio);
} else {
newImageSize = CGSizeMake(maxSize, maxSize);
}
[imageRep setSize:NSSizeFromCGSize(newImageSize)];
}
NSData *imageData = [imageRep representationUsingType:NSPNGFileType properties:nil];
NSString *outputFilePath = [@"~/Desktop/output.png" stringByExpandingTildeInPath];
[imageData writeToFile:outputFilePath atomically:NO];
O código pressupõe que um PNG de 200x200 será menor que 128k, que é o meu limite de tamanho. 200x200 é grande o suficiente, mas eu prefiro maximizar o tamanho, se possível.
Aqui estão meus dois problemas:
- O código não funciona. Eu verifico o tamanho do arquivo exportado e é do mesmo tamanho que o original.
- Existe uma maneira de prever o tamanho do arquivo de saída antes de exportar, para que eu possa maximizar as dimensões, mas ainda assim obter uma imagem menor que 128k?
Aqui está o código de trabalho. É muito desleixado e provavelmente poderia usar algumas otimizações, mas neste momento ele funciona rápido o suficiente para que eu não me importe. Ele itera mais de 100x para a maioria das fotos e acaba em milissegundos. Além disso, esse método é declarado em uma categoria em NSImage
.
- (NSData *)resizeImageWithBitSize:(NSInteger)size
andImageType:(NSBitmapImageFileType)fileType {
CGFloat maxSize = 500.0;
NSSize originalImageSize = self.size;
NSSize newImageSize;
NSData *returnImageData;
NSInteger imageIsTooBig = 1000;
while (imageIsTooBig > 0) {
if (originalImageSize.height>maxSize || originalImageSize.width>maxSize) {
// Find the aspect ratio
CGFloat aspectRatio = originalImageSize.height/originalImageSize.width;
if (aspectRatio > 1.0) {
newImageSize = NSMakeSize(maxSize / aspectRatio, maxSize);
} else if (aspectRatio < 1.0) {
newImageSize = NSMakeSize(maxSize, maxSize * aspectRatio);
} else {
newImageSize = NSMakeSize(maxSize, maxSize);
}
} else {
newImageSize = originalImageSize;
}
NSImage *resizedImage = [[NSImage alloc] initWithSize:newImageSize];
[resizedImage lockFocus];
[self drawInRect:NSMakeRect(0, 0, newImageSize.width, newImageSize.height)
fromRect: NSMakeRect(0, 0, originalImageSize.width, originalImageSize.height)
operation: NSCompositeSourceOver
fraction: 1.0];
[resizedImage unlockFocus];
NSData *tiffData = [resizedImage TIFFRepresentation];
[resizedImage release];
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithData:tiffData];
NSDictionary *imagePropDict = [NSDictionary
dictionaryWithObject:[NSNumber numberWithFloat:0.85]
forKey:NSImageCompressionFactor];
returnImageData = [imageRep representationUsingType:fileType properties:imagePropDict];
[imageRep release];
if ([returnImageData length] > size) {
maxSize = maxSize * 0.99;
imageIsTooBig--;
} else {
imageIsTooBig = 0;
}
}
return returnImageData;
}
Solução
Por 1.
Como outro pôster mencionado, o SetSize apenas altera os tamanhos de exibição da imagem, e não os dados pixels reais do arquivo de imagem subjacente.
Para redimensionar, convém redesenhar a imagem de origem em outro NSIMAGEREP e, em seguida, escrevê -lo para arquivar.
Esta postagem do blog contém algum código de amostra sobre como fazer o redimensionamento dessa maneira.
Para 2.
Eu não acho que você pode sem pelo menos criar o objeto na memória e verificar o comprimento da imagem. Os bytes reais utilizados dependerão do tipo de imagem. O Bitmaps com o ARGB será fácil de prever seu tamanho, mas o PNG e o JPEG seriam muito mais difíceis.
Outras dicas
[imageData length]
Deve fornecer o comprimento do conteúdo do NSDATA, que eu entendo ser o tamanho final do arquivo quando gravado no disco. Isso deve dar a você a chance de maximizar o tamanho do arquivo antes de realmente escrevê -lo.
Sobre por que a imagem não está diminuindo ou crescendo, de acordo com os documentos para o Setsize:
O tamanho de uma representação de imagem combinada com as dimensões físicas dos dados da imagem determina a resolução da imagem.
Portanto, pode ser que, ao definir o tamanho e não alterar a resolução, você não está modificando nenhum pixels, exatamente a maneira pela qual os pixels devem ser interpretados.