i'm attempting to maximize the scale of an image so that it uses as much of the allowable pixel limit as possible.
the language i'm using is ActionScript 3.0, but this problem is purely math related, and unfortunately i'm not the sharpest calculator in the drawer.
from the ActionScript 3.0 documentation on BitmapData:
the maximum size for a BitmapData object is 8,191 pixels in width or height, and the total number of pixels cannot exceed 16,777,215 pixels. (So, if a BitmapData object is 8,191 pixels wide, it can only be 2,048 pixels high.)
so first i load my image asset into the program and determine it's max scalability like this:
const MAX_BITMAP_MEASUREMENT:Number = 8191;
const MAX_BITMAP_PIXELS:Number = 16777215;
var maximumScaleProperty:Number = Math.sqrt(MAX_BITMAP_PIXELS / (image.width * image.height));
this works well for most images, but it becomes problematic with images that are much wider than they are higher (such as panoramic images).
for example, i have an image that is 1928 width x 451 height
. with the above equation to determine it's maximum scalability, it will return 4.392564519715899
:
var maximumScaleProperty:Number = Math.sqrt(16777215 / (1928 * 451));
//returns 4.392564519715899
however, if i now actually scale this image by 4.392564519715899, while the total number of pixels is within the MAX_BITMAP_PIXELS
(16,777,215) the image width becomes too large to fit within the maximum allowed for a side (8191):
Pixels: (1928 * 4.392564519715899) * (451 * 4.392564519715899) = 1677215; //good
height: (451 * 4.392564519715899) = 1981; //good
width: (1928 * 4.392564519715899) = 8469; //bad - greater than 8191
so to try and remedy (read train wreck) this problem, i first evaluate to see if the longest side of the image multiplied by the initial maximumScale is greater than 8191 (for simplicity i'm only concentrating on the width being longer for now). then i assign the longest side of the image to 8191 and reduce the shortest side (height) by the percentage of which the image's width has overflowed in allowable pixels
// Evaluate to see if it's too big and reajust if it is:
if (Math.max(imageWidth * maximumScaleProperty, imageHeight * maximumScaleProperty) > MAX_BITMAP_MEASUREMENT)
{
var percentReduction:Number = 1.0 - MAX_BITMAP_MEASUREMENT / (Math.max(imageWidth, imageHeight) * maximumScaleProperty);
maximumScaleProperty = Math.sqrt(MAX_BITMAP_PIXELS / (MAX_BITMAP_MEASUREMENT * (image.height - image.height * percentReduction)));
}
while this works, it reduces the scalability too much so that the maximum amount of available pixels isn't being used.
in this specific case, the initial scalability was calculated at 4.392564519715899
but then because the side was too long, the scalability was reduced all the way down to 2.1669422014011723
, but this is too much. based on manually scaling it in my program, something close to 3.9 or 4.0
would use the maximum amount without being cut off.
where is the problem in my equations?
So you want to maximize the scale (s
) under the following constraints (where h
= height of image, w
= width of image)
maximize s
such that
s*h*s*w <= 16777215
s*h <= 8191
s*w <= 8191
s >= 0
Let's rearrange your constraints and let n = h*w
:
s <= sqrt(16777215/n)
s <= 8191 / h
s <= 8191 / w
Note that we choose the positive root of the first constraint since s
has to be positive.
Therefore, s = min( sqrt( 16777215 / n),8191 / h , 8191 / w)
.
Using your example of h = 1928, w = 451
, we have:
s = min( sqrt( 16777215 / n),8191 / h , 8191 / w)
= min(4.3926,4.2484,18.1619)
= 4.2484
which maximizes the scaled height.