bug 815512 pt 1 - on retina-display Macs, moz-icon gets a bitmap that is 2x the 'expected' size. r=joe
authorJonathan Kew <jkew@mozilla.com>
Wed, 28 Nov 2012 09:04:14 +0000
changeset 114346 72e123c2602da3920a17c3983f572f4c5524dd2d
parent 114345 9d18ded603301309aaebc9261db21dd941e16762
child 114347 8ab6b738b68647d41c6409ac963a691c50b120c5
push id18709
push userjkew@mozilla.com
push dateWed, 28 Nov 2012 09:04:28 +0000
treeherdermozilla-inbound@f1df34df5f3b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs815512
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 815512 pt 1 - on retina-display Macs, moz-icon gets a bitmap that is 2x the 'expected' size. r=joe
image/decoders/icon/mac/nsIconChannelCocoa.mm
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -213,22 +213,22 @@ nsresult nsIconChannel::MakeInputStream(
   bool fileExists = false;
   if (fileloc) {
     // ensure that we DO NOT resolve aliases, very important for file views
     fileloc->SetFollowLinks(false);
     fileloc->Exists(&fileExists);
   }
 
   NSImage* iconImage = nil;
-  
+
   // first try to get the icon from the file if it exists
   if (fileExists) {
     nsCOMPtr<nsILocalFileMac> localFileMac(do_QueryInterface(fileloc, &rv));
     NS_ENSURE_SUCCESS(rv, rv);
-    
+
     CFURLRef macURL;
     if (NS_SUCCEEDED(localFileMac->GetCFURL(&macURL))) {
       iconImage = [[NSWorkspace sharedWorkspace] iconForFile:[(NSURL*)macURL path]];
       ::CFRelease(macURL);
     }
   }
 
   // if we don't have an icon yet try to get one by extension
@@ -238,49 +238,58 @@ nsresult nsIconChannel::MakeInputStream(
   }
 
   // If we still don't have an icon, get the generic document icon.
   if (!iconImage)
     iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeUnknown];
 
   if (!iconImage)
     return NS_ERROR_FAILURE;
-  
+
   // we have an icon now, size it
   NSRect desiredSizeRect = NSMakeRect(0, 0, desiredImageSize, desiredImageSize);
   [iconImage setSize:desiredSizeRect.size];
-  
+
   [iconImage lockFocus];
   NSBitmapImageRep* bitmapRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:desiredSizeRect] autorelease];
   [iconImage unlockFocus];
-  
+
   // we expect the following things to be true about our bitmapRep
   NS_ENSURE_TRUE(![bitmapRep isPlanar] &&
-                 (unsigned int)[bitmapRep bytesPerPlane] == desiredImageSize * desiredImageSize * 4 &&
+                 // Not necessarily: on a HiDPI-capable system, we'll get a 2x bitmap
+                 // (unsigned int)[bitmapRep bytesPerPlane] == desiredImageSize * desiredImageSize * 4 &&
                  [bitmapRep bitsPerPixel] == 32 &&
                  [bitmapRep samplesPerPixel] == 4 &&
                  [bitmapRep hasAlpha] == YES,
                  NS_ERROR_UNEXPECTED);
-  
+
+  // check what size we actually got, and ensure it isn't too big to return
+  uint32_t actualImageSize = [bitmapRep bytesPerRow] / 4;
+  NS_ENSURE_TRUE(actualImageSize < 256, NS_ERROR_UNEXPECTED);
+
+  // now we can validate the amount of data
+  NS_ENSURE_TRUE((unsigned int)[bitmapRep bytesPerPlane] == actualImageSize * actualImageSize * 4,
+                 NS_ERROR_UNEXPECTED);
+
   // rgba, pre-multiplied data
   uint8_t* bitmapRepData = (uint8_t*)[bitmapRep bitmapData];
-  
+
   // create our buffer
-  int32_t bufferCapacity = 2 + desiredImageSize * desiredImageSize * 4;
+  int32_t bufferCapacity = 2 + [bitmapRep bytesPerPlane];
   nsAutoTArray<uint8_t, 3 + 16 * 16 * 5> iconBuffer; // initial size is for 16x16
   if (!iconBuffer.SetLength(bufferCapacity))
     return NS_ERROR_OUT_OF_MEMORY;
-  
+
   uint8_t* iconBufferPtr = iconBuffer.Elements();
-  
+
   // write header data into buffer
-  *iconBufferPtr++ = desiredImageSize;
-  *iconBufferPtr++ = desiredImageSize;
+  *iconBufferPtr++ = actualImageSize;
+  *iconBufferPtr++ = actualImageSize;
 
-  uint32_t dataCount = (desiredImageSize * desiredImageSize) * 4;
+  uint32_t dataCount = [bitmapRep bytesPerPlane];
   uint32_t index = 0;
   while (index < dataCount) {
     // get data from the bitmap
     uint8_t r = bitmapRepData[index++];
     uint8_t g = bitmapRepData[index++];
     uint8_t b = bitmapRepData[index++];
     uint8_t a = bitmapRepData[index++];
 
@@ -297,21 +306,22 @@ nsresult nsIconChannel::MakeInputStream(
     *iconBufferPtr++ = r;
     *iconBufferPtr++ = g;
     *iconBufferPtr++ = b;
 #endif
   }
 
   NS_ASSERTION(iconBufferPtr == iconBuffer.Elements() + bufferCapacity,
                "buffer size miscalculation");
-  
+
   // Now, create a pipe and stuff our data into it
   nsCOMPtr<nsIInputStream> inStream;
   nsCOMPtr<nsIOutputStream> outStream;
-  rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream), bufferCapacity, bufferCapacity, nonBlocking);  
+  rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream),
+                  bufferCapacity, bufferCapacity, nonBlocking);
 
   if (NS_SUCCEEDED(rv)) {
     uint32_t written;
     rv = outStream->Write((char*)iconBuffer.Elements(), bufferCapacity, &written);
     if (NS_SUCCEEDED(rv))
       NS_IF_ADDREF(*_retval = inStream);
   }