获取图像大小而不加载到内存中

时间:2009-10-11 18:02:36

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

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

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

8 个答案:

答案 0 :(得分:6)

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

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

答案 1 :(得分:4)

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

答案 2 :(得分:1)

这在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只有几行。

答案 3 :(得分:1)

注意:此功能不适用于iPhone压缩的PNG,此压缩由XCode自动执行并更改图像标题,请在此处查看更多详细信息以及如何禁用此功能:{{3 }}

PSFramework的未来版本也将解释此标题,请继续关注。


看到这个功能,她就是这么做的。只读取30个字节的PNG文件并返回大小(CGSize)。此函数是处理称为PSFramework(http://discussions.apple.com/thread.jspa?threadID=1751896)的图像的框架的一部分。尚未实现其他图像格式,欢迎开发人员。该项目是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 );

}

答案 4 :(得分:1)

//这是一个快速的&amp;脏端口到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 :(得分:1)

imageUrl是NSURL,也导入ImageIO / ImageIO.h,其中&lt;&gt;在它周围。

class ServerQuery {

constructor(request, level) {
    console.log("Server query started, " + request + " " + level );
    this.lev = level;
    this.file = "http://" + location.hostname + "/" + this.lev + "/" + request;
    this.values = new Object();
    this.data   = new Array();
    this.result      = null;
    this.setRequiredValues();
}

setRequiredValues() {
    console.log("Setting required Values");
        let ses = document.getElementById("key").value;
        let orgid = document.getElementById("OrgId").value;
        let userid = document.getElementById("userid").value;            
        this.values['key'] = ses;
        this.values['orgid'] = orgid;
        this.values['userid'] = userid;
        console.log("Required Values loaded: " + JSON.stringify(this.values));           
}

addValue(key, insert) {
    console.log("adding Values " + key + " " + JSON.stringify(insert));
    this.r = new Object();
    this.r[key] = insert;
    this.data.push(this.r);
    console.log("Values Added " + JSON.stringify(this.data));              
}

// select this method to trigger a return callback
sendRequest() {
    console.log("Server Query sending Request");
    connect_ajax();

    // this.values is an object
    this.values['linked'] = this.data;
    let req = JSON.stringify(this.values);
    let uandp = "requesting=" + req;
        console.log("Data adding " + uandp);
        $.post(this.file, uandp)
            .done(function done2(result) {
                console.log("server query finsihed with this result " + result);
                this.r = JSON.parse(result);
                if (this.r.status == 1) {
                    console.log("ServerQuery after parse " + this.r);
                    console.log("output " + this.r.callbackFunct);
                    if (typeof this.r.callbackFunct != 'undefined') {
                        setTimeout(function() {
                            this.r.callbackFunct(this.r.callbackVars);
                        }, 500);
                    } else {
                        alert("Callback Not set");
                    }
                }
                else if (this.r.status == 3) {
                    alert(this.r.msg);
                }
            })
            .fail(function processFailed() {
                console.log("an Error has occured");
            })
            .always(function processAlways() {
                console.log("Finished");
            });
        console.log("Requesting url " + this.file);

}   

// select this method to get a static response from server
callRequest() {
    console.log("starting serverquery process() ");
    let answer = this.process();
    console.log("process returned " + answer);
    return answer;

}

process() {
    this.values['linked'] = this.data;
    let req = JSON.stringify(this.values);
    let uandp = "requesting=" + req;
    let file = this.file;
        console.log("Data adding " + uandp);
    let return_first = function () {
    let tmp = null;
    $.ajax({
        'async': false,
        'type': "POST",
        'global': false,
        'dataType': 'html',
        'url': file,
        'data': uandp,
        'success': function (data) {
            tmp = data;
        }
    });
    return tmp;
    }();
    return return_first;
}

cleanUp() {
    delete this.file;
    delete this.values          
    console.log("removed values from global " + this.values);
    console.log("removed file from global " + this.file);
}  
}

答案 6 :(得分:0)

尝试使用CGImageCreateWithPNGDataProviderCGImageCreateWithJPEGDataProvider功能。我不知道他们是否懒得,或者这对JPEG是否可行,但是值得尝试。

答案 7 :(得分:0)

低技术解决方案:

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

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