【问题标题】:Get size of image without loading in to memory在不加载到内存的情况下获取图像的大小
【发布时间】:2010-12-05 19:07:18
【问题描述】:

我将在UITableViewCells 中显示几张.png 图像(ETA:,但格式也可以是 JPEG 或其他格式)。现在,为了获取行高,我加载图像,获取它们的size 属性,并使用它来计算行的高度(计算沿途的任何必要更改,因为大多数图像在显示之前调整大小)。为了加快速度并减少内存使用,我希望能够在不加载图像的情况下获得size。有没有办法做到这一点?

注意:我知道我可以实施许多快捷方式来消除此问题,但由于多种原因,我无法提前调整图像大小或提前收集图像大小,迫使我在运行时获取此信息。

【问题讨论】:

  • CGImageSource 是完美的 API,但令人讨厌的是它在 iPhone 上不可用。您可能必须自己实现它。也就是说,请记住,如果您使用 +imageWithContentsOfFile: UIImage 将清除其数据,这应该消除您的内存使用问题。

标签: iphone cocoa cocoa-touch uikit core-graphics


【解决方案1】:

imageUrl 是一个 NSURL,同样导入 ImageIO/ImageIO.h 并带有 。

CGImageSourceRef imageSourceRef = CGImageSourceCreateWithURL((CFURLRef)imageUrl, NULL);

if (!imageSourceRef)
    return;

CFDictionaryRef props = CGImageSourceCopyPropertiesAtIndex(imageSourceRef, 0, NULL);

NSDictionary *properties = (NSDictionary*)CFBridgingRelease(props);

if (!properties) {
    return;
}

NSNumber *height = [properties objectForKey:@"PixelHeight"];
NSNumber *width = [properties objectForKey:@"PixelWidth"];
int height = 0;
int width = 0;

if (height) {
    height = [height intValue];
}
if (width) {
    width = [width intValue];
}

【讨论】:

    【解决方案2】:

    从 iOS SDK 4.0 开始,此任务可以通过 ImageIO 框架 (CGImageSource...) 完成。我已回复a similar question here

    【讨论】:

      【解决方案3】:

      这在 Perl 的Image::Size module 中很好地实现了大约十几种格式——包括 PNG 和 JPEG。为了在Objective C中重新实现它,只需获取perl代码并将其作为伪代码读取;-)

      例如,pngsize() 被定义为

      # pngsize : gets the width & height (in pixels) of a png file
      # cor this program is on the cutting edge of technology! (pity it's blunt!)
      #
      # Re-written and tested by tmetro@vl.com
      sub pngsize
      {
          my $stream = shift;
      
          my ($x, $y, $id) = (undef, undef, "could not determine PNG size");
          my ($offset, $length);
      
          # Offset to first Chunk Type code = 8-byte ident + 4-byte chunk length + 1
          $offset = 12; $length = 4;
          if (&$read_in($stream, $length, $offset) eq 'IHDR')
          {
              # IHDR = Image Header
              $length = 8;
              ($x, $y) = unpack("NN", &$read_in($stream, $length));
              $id = 'PNG';
          }
      
          ($x, $y, $id);
      }
      

      jpegsize 只长了几行。

      【讨论】:

        【解决方案4】:

        //这是一个快速而肮脏的 C# 端口

        
        public static Size PNGSize(string fileName)
        {
            // PNG Signature. 
            byte[] png_signature = {137, 80, 78, 71, 13, 10, 26, 10}; 
        
        try
        {
            using (FileStream stream = File.OpenRead(fileName))
            {
                byte[] buf = new byte[30];
                if (stream.Read(buf, 0, 30) == 30)
                {
                    int i = 0;
                    int imax = png_signature.Length;
        
                    for (i = 0; i < imax; i++)
                    {
                        if (buf[i] != png_signature[i])
                            break;
                    }
        
                    // passes sig test
                    if (i == imax)
                    {
                            // Calc Sizes. Isolate only four bytes of each size (width, height). 
                            // Convert bytes to integer
                            int resultWidth = buf[16] << 24 | buf[17] << 16 | buf[18] << 8 | buf[19];
                            int resultHeight = buf[20] << 24 | buf[21] << 16 | buf[22] << 8 | buf[23];  
        
                            // Return Size. 
                            return new Size( resultWidth, resultHeight ); 
                    }
                }
            }
        }
        catch
        {
        
        }
        
        return new Size(0, 0);
        

        }

        【讨论】:

          【解决方案5】:

          注意:此功能不适用于 iPhone 压缩的 PNG,此压缩由 XCode 自动执行并更改图像标题,请在此处查看更多详细信息以及如何禁用此功能:http://discussions.apple.com/thread.jspa?threadID=1751896

          PSFramework 的未来版本也将解释此标头,敬请期待。


          看到这个功能,她就是这样做的。仅读取 30 字节的 PNG 文件并返回大小 (CGSize)。此函数是用于处理图像的框架的一部分,称为 PSFramework (http://sourceforge.net/projects/photoshopframew/)。其他图像格式尚未实现,欢迎开发者。该项目在 GNU 许可证下是开源的。

          CGSize PSPNGSizeFromMetaData( NSString* anFileName ) {
          
              // File Name from Bundle Path.
              NSString *fullFileName = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] bundlePath], anFileName ];
          
              // File Name to C String.
              const char* fileName = [fullFileName UTF8String];
          
              /* source file */ 
              FILE * infile;    
          
              // Check if can open the file.
              if ((infile = fopen(fileName, "rb")) == NULL) 
              {
                  NSLog(@"PSFramework Warning >> (PSPNGSizeFromMetaData) can't open the file: %@", anFileName );
                  return CGSizeZero;
          
              }
          
              //////  //////  //////  //////  //////  //////  //////  //////  //////  //////  ////// 
          
              // Lenght of Buffer.
              #define bytesLenght 30
          
              // Bytes Buffer.
              unsigned char buffer[bytesLenght];
          
              // Grab Only First Bytes.
              fread(buffer, 1, bytesLenght, infile);
          
              // Close File.
              fclose(infile);
          
              //////  //////  //////  //////  ////// 
          
              // PNG Signature.
              unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
          
              // Compare File signature.
              if ((int)(memcmp(&buffer[0], &png_signature[0], 8))) {
          
                  NSLog(@"PSFramework Warning >> (PSPNGSizeFromMetaData) : The file (%@) don't is one PNG file.", anFileName);    
                  return CGSizeZero;
          
              }
          
              //////  //////  //////  //////  ////// //////   //////  //////  //////  ////// 
          
              // Calc Sizes. Isolate only four bytes of each size (width, height).
              int width[4];
              int height[4];
              for ( int d = 16; d < ( 16 + 4 ); d++ ) {
                  width[ d-16] = buffer[ d ];
                  height[d-16] = buffer[ d + 4];
              }
          
              // Convert bytes to Long (Integer)
              long resultWidth = (width[0] << (int)24) | (width[1] << (int)16) | (width[2] << (int)8) | width[3]; 
              long resultHeight = (height[0] << (int)24) | (height[1] << (int)16) | (height[2] << (int)8) | height[3]; 
          
              // Return Size.
              return CGSizeMake( resultWidth, resultHeight );
          
          }
          

          【讨论】:

            【解决方案6】:

            低技术解决方案:

            如果您事先知道图像是什么,请将图像大小及其文件名存储在 XML 文件或 plist(或任何您喜欢的方式)中,然后读取这些属性。

            如果您不知道图像是什么(即它们将在运行时定义),那么您一定是在某个时间加载了图像。第一次加载它们时,将它们的高度和宽度保存在一个文件中,以便以后访问。

            【讨论】:

              【解决方案7】:

              尝试使用CGImageCreateWithPNGDataProviderCGImageCreateWithJPEGDataProvider 函数。我不知道他们是否足够懒惰,或者 JPEG 是否有可能,但值得一试。

              【讨论】:

                【解决方案8】:

                应该很简单。 PNG spec 解释了 PNG datastream(实际上是一个文件)。 IHDR 部分包含有关图像尺寸的信息。

                所以你要做的就是读入PNG“magic value”,然后读入两个四字节整数,分别是宽和高。您可能还需要对这些值中的位进行重新排序(不确定它们是如何存储的),但是一旦您弄清楚了,这将非常简单。

                【讨论】:

                • 好主意,除了我忘了提到它可能不是 PNG,这正是我现在碰巧拥有的(编辑后的帖子以反映这一点)。至少我也应该支持 JPEG,尽管我想我可以用与你描述的类似的方式来做到这一点。
                猜你喜欢
                • 1970-01-01
                • 2011-08-26
                • 1970-01-01
                • 2015-09-17
                • 2013-03-25
                • 1970-01-01
                • 2018-05-26
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多