Untuk merasterkan NSImageView apl anda, gunakan kaedah pasangan cache bitmap AppKit, kemudian kekalkan dimensi piksel yang terhasil. Jika anda memerlukan bait yang boleh diramal untuk Metal, lukis semula bitmap itu ke dalam penimbal Core Graphics yang ditakrifkan secara jelas.

Panduan ini merangkumi imej sintetik yang dijana sepenuhnya dalam memori. Tugasnya adalah untuk mendapatkan raster yang boleh digunakan semula bagi paparannya dalam NSImageView, mendiagnosis piksel yang tersasar atau berulang, dan secara pilihan memindahkan raster itu ke tekstur Metal.

Pilih sama ada anda memerlukan paparan view atau imej bersaiz tepat

Imej dan view yang memaparkannya mempunyai tanggungjawab yang berbeza. View menggunakan mod penskalaannya untuk memuatkan imej ke dalam bingkainya. Nilai lalainya ialah scaleProportionallyDown, jadi mengekstrak imej asal tidak semestinya menghasilkan semula paparan view itu. Dokumentasi imageScaling Apple menerangkan tingkah laku ini.

Pilih output sebelum memilih API:

Output yang dikehendaki Titik permulaan yang sesuai Kontrak dimensi
Imej seperti yang dilukis oleh paparan imej Kaedah cache bitmap view AppKit Periksa dimensi piksel bitmap yang dipulangkan
Lukisan sintetik pada dimensi yang ditentukan dengan tepat Lukis terus ke CGContext bitmap Lebar dan tinggi piksel integer yang dinyatakan dengan jelas
Bait yang boleh diramal untuk tekstur Metal Lukis semula raster ke dalam konteks bitmap yang ditentukan Format piksel, stride dan pemilikan yang dinyatakan dengan jelas

Apple mendokumenkan bitmapImageRepForCachingDisplay(in:) sebagai kaedah untuk mencipta perwakilan yang sesuai bagi rantau view yang diminta. Hantar perwakilan itu kepada cacheDisplay(in:to:); Apple secara khusus mengesyorkan pasangan ini demi keserasian format piksel. Kaedah kedua melukis view berserta semua turunannya. Dokumentasi peruntukan bitmap, dokumentasi cache view.

Kaedah ini tidak mendedahkan argumen resolusi output sewenang-wenangnya. Jika keperluan anda ialah tepat 192 × 128 piksel, paparkan lukisan sintetik anda secara terus pada saiz itu. Membesarkan raster view yang lebih kecil selepas itu tidak dapat memulihkan perincian yang tidak pernah dirasterkan. Ini kesan daripada dua kontrak output yang berbeza, bukannya keupayaan tambahan bagi cache view.

Asingkan titik, piksel dan segi empat tepat

Gunakan view.bounds apabila meminta rantau lukisan setempat sepenuhnya. Bounds view menerangkan sistem koordinatnya sendiri; framenya menerangkan kedudukan dan saiz dalam sistem koordinat superview. Oleh itu, menghantar frame kepada kaedah yang menjangkakan rantau setempat boleh menyebabkan ofset yang tidak disengajakan. Dokumentasi bounds Apple.

Sebagai contoh kontrak saiz, bitmap sumber 192 × 128 mungkin dipaparkan sebagai imej logik 96 × 64. Kekalkan kedua-dua nilai: yang pertama menerangkan sampel, manakala yang kedua menerangkan saiz paparan. Pemula NSImage menerima saiz yang dinyatakan secara jelas; jika nilai sifar diberikan, ia akan menggunakan dimensi piksel imej sumber. Dokumentasi pemula imej Apple.

Apabila mendiagnosis geometri backing view sedia ada, gunakan convertToBacking(_:), yang menukar segi empat tepat setempatnya kepada koordinat storan backing. Bagi raster yang benar-benar dipulangkan, periksa pixelsWide dan pixelsHigh, atau dimensi CGImage-nya. Apple mendokumenkan bahawa cgImage bagi NSBitmapImageRep mempunyai dimensi piksel yang sama dengan perwakilan itu. Dokumentasi penukaran backing, dokumentasi CGImage bitmap.

Jangan gantikan dimensi yang dipulangkan itu dengan pengganda 2× yang diandaikan. Catat rantau setempat yang diminta, lebar dan tinggi piksel yang dipulangkan, serta saiz logik yang dikehendaki bersama-sama. Dengan itu, anda mempunyai perbandingan konkrit apabila hasil kelihatan dibesarkan, dipotong atau tersasar.

Bina contoh rasterisasi sintetik yang lengkap

Program Objective-C berikut menjana imej ujian tidak simetri, menetapkannya kepada view imej dalam memori, merasterkan view itu dan menormalkan hasilnya ke dalam bitmap Core Graphics. Program ini tidak mencipta sebarang fail. Simpannya sebagai RasterDemo.m pada Mac yang mempunyai alat pembangun baris arahan dipasang.

clang -fobjc-arc RasterDemo.m -framework Cocoa -o RasterDemo
./RasterDemo

Konteks normalisasi menggunakan komponen lapan bit, alpha pradarab pada kedudukan paling signifikan, dan storan 32-bit little-endian. Pilihan ini bersama-sama menghasilkan susunan bait BGRA: komponen biru paling tidak signifikan berada dahulu dalam memori. Ini berdasarkan takrif Apple bagi premultipliedFirst dan byteOrder32Little.

#import <Cocoa/Cocoa.h>

static CGContextRef NewBitmap(size_t w, size_t h) {
    // Illustrative allocation budget, not an API dimension limit.
    if (w == 0 || h == 0 || w > 4096 || h > 4096) return NULL;
    size_t rowBytes = (w * 4 + 63) & ~(size_t)63;
    CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
    if (!space) return NULL;
    CGContextRef c = CGBitmapContextCreate(
        NULL, w, h, 8, rowBytes, space,
        kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(space);
    return c;
}

static CGImageRef NewTestImage(void) {
    CGContextRef c = NewBitmap(192, 128);
    if (!c) return NULL;
    CGContextSetRGBFillColor(c, 1, 1, 1, 1);
    CGContextFillRect(c, CGRectMake(0, 0, 192, 128));
    CGContextSetRGBFillColor(c, 0, 0, 0, 1);
    for (size_t x = 0; x < 192; x += 16) {
        CGContextFillRect(c, CGRectMake(x, 0, 1, 128));
    }
    CGContextSetRGBFillColor(c, 0, 0.7, 0.8, 1);
    CGContextFillRect(c, CGRectMake(8, 92, 24, 20));
    CGImageRef image = CGBitmapContextCreateImage(c);
    CGContextRelease(c);
    return image;
}

static CGImageRef NewNormalizedImage(CGImageRef input) {
    size_t w = CGImageGetWidth(input);
    size_t h = CGImageGetHeight(input);
    CGContextRef c = NewBitmap(w, h);
    if (!c) return NULL;
    CGContextClearRect(c, CGRectMake(0, 0, w, h));
    CGContextSetBlendMode(c, kCGBlendModeCopy);
    CGContextSetInterpolationQuality(c, kCGInterpolationNone);
    CGContextDrawImage(c, CGRectMake(0, 0, w, h), input);
    NSLog(@"Normalized: %zu x %zu pixels; stride %zu bytes",
          w, h, CGBitmapContextGetBytesPerRow(c));
    CGImageRef result = CGBitmapContextCreateImage(c);
    CGContextRelease(c);
    return result;
}

int main(void) {
    @autoreleasepool {
        [NSApplication sharedApplication];
        CGImageRef source = NewTestImage();
        if (!source) { NSLog(@"Source allocation failed"); return 1; }
        NSImage *image = [[NSImage alloc] initWithCGImage:source
                                                  size:NSMakeSize(96, 64)];
        CGImageRelease(source);
        NSImageView *view = [[NSImageView alloc]
                            initWithFrame:NSMakeRect(0, 0, 96, 64)];
        view.image = image;
        view.imageScaling = NSImageScaleNone;
        view.imageAlignment = NSImageAlignCenter;
        view.imageFrameStyle = NSImageFrameNone;
        [view layoutSubtreeIfNeeded];
        NSRect region = view.bounds;
        NSBitmapImageRep *rep =
            [view bitmapImageRepForCachingDisplayInRect:region];
        if (!rep) { NSLog(@"View bitmap allocation failed"); return 1; }
        [view cacheDisplayInRect:region toBitmapImageRep:rep];
        CGImageRef cached = rep.CGImage;
        if (!cached) { NSLog(@"View bitmap has no CGImage"); return 1; }
        CGImageRef raster = NewNormalizedImage(cached);
        if (!raster) { NSLog(@"Normalization failed"); return 1; }
        NSImage *result = [[NSImage alloc] initWithCGImage:raster
                                                   size:region.size];
        NSLog(@"Result logical size: %@", NSStringFromSize(result.size));
        CGImageRelease(raster);
    }
    return 0;
}

Program ini sengaja melaporkan dimensi output sebenar dan bukannya mengandaikan bahawa cache view sepadan dengan 192 × 128 piksel sumber. Penanda sian membezakan perubahan orientasi daripada garisan ujian menegak yang berulang. Jika keperluan anda ialah resolusi sumber yang tepat, gunakan CGImage yang dipulangkan oleh NewTestImage() secara terus dan jangan lalukannya melalui view.

Pembersihan eksplisit menetapkan piksel destinasi telus sebelum normalisasi. Lukisan imej tunggal memenuhi segi empat tepat destinasi; ia tidak membuat jubin. Apple mendokumenkan bahawa operasi lukisan Core Graphics yang sepadan menskalakan imej ke segi empat tepat destinasi dan melukis satu salinan melainkan penjubinan diaktifkan. Dokumentasi lukisan imej Apple.

Program ini telah diperiksa tetapi tidak dapat dikompil dalam persekitaran penyelidikan Linux, yang tidak mempunyai AppKit dan pengkompil C. Pengiraan stride yang digunakan di bawah telah disemak dengan pengiraan Python kecil yang deterministik.

Diagnosis piksel tersasar atau berulang mengikut urutan tetap

Urutan berikut ialah bantuan diagnostik yang diperoleh daripada kontrak geometri dan memori di atas. Gejala menunjukkan perkara yang wajar diperiksa dahulu; gejala itu tidak menentukan satu punca yang unik.

  1. Periksa segi empat tepat yang diminta. Untuk keseluruhan paparan imej, bandingkan permintaan cache dengan bounds. Jika mengeksport subrantau, kekalkan asalan setempatnya sebagai sebahagian daripada kontrak input.
  2. Periksa dimensi sebelum bait mentah. Bandingkan lebar dan tinggi imej yang dicache dengan dimensi yang dibekalkan kepada setiap pengguna seterusnya. Menggantikan lebar piksel dengan lebar titik mengubah tafsiran penimbal.
  3. Periksa stride baris. Jika setiap baris bermula semakin jauh daripada kedudukan yang dijangkakan, periksa jarak antara permulaan baris sebelum mengubah transformasi.
  4. Periksa orientasi menggunakan penanda tidak simetri. Pembalikan menegak bermakna konvensyen koordinat perlu diperiksa. Anjakan malar bermakna asalan dan translasi perlu diperiksa.
  5. Periksa sama ada pengulangan itu disengajakan. Periksa sebarang panggilan lukisan yang mengaktifkan penjubinan. Mengubah stride baris tidak akan membetulkan lukisan yang ditetapkan untuk dijubin secara jelas.
  6. Periksa penskalaan paparan sebagai langkah terakhir. Jika raster tersimpan betul tetapi hasil yang dipaparkan berbeza, bandingkan saiz logik imej output dan tetapan penskalaan paparan imej yang menerimanya.

Bagi piksel empat bait, penimbal ilustrasi 192 × 128 mempunyai 768 bait setiap baris dan memerlukan 98,304 bait. Namun, penimbal 191 × 128 dengan penjajaran baris 64 bait dalam contoh ini juga mempunyai stride 768 bait: setiap baris mengandungi empat bait padding. Melangkah sebanyak 191 × 4, atau 764, akan memulakan setiap baris berikutnya empat bait terlalu awal. Apple menyediakan stride sebenar melalui sifat bytesPerRow konteks.

Gunakan pengiraan alamat base + y * bytesPerRow + x * 4 hanya untuk format empat bait yang dinormalkan dan ditetapkan di sini. Bitmap AppKit sewenang-wenangnya mempunyai kontrak format tersendiri; jangan membuat kesimpulan tentang susun atur komponennya hanya berdasarkan rupa.

Begitu juga, gunakan perubahan koordinat dengan sengaja. Core Graphics menyediakan operasi berasingan untuk mengubah asalan dan skala ruang pengguna. Jika anda memperkenalkan pembalikan menegak, dokumentasikan peringkat yang bertanggungjawab terhadap penukaran itu dan sahkannya dengan penanda sebelum menambah satu lagi. Dokumentasi translasi, dokumentasi skala.

Kekalkan pemilikan apabila memulangkan imej atau memuat naik ke Metal

Contoh ini memastikan perwakilan AppKit kekal hidup semasa melukis CGImage-nya. Kemudian, contoh ini memulangkan petikan yang dicipta daripada konteks normalisasi. Apple mendokumenkan bahawa makeImage() mencipta salinan, yang mungkin menggunakan salin-semasa-tulis secara dalaman, dan lukisan konteks seterusnya tidak mengubah imej yang dipulangkan. Ini membolehkan konteks dilepaskan selepas petikan dicipta. Dokumentasi petikan bitmap Apple.

Penunjuk bitmap mentah mempunyai jangka hayat yang berbeza. Apple menyediakan akses kepada data bitmap milik konteks walaupun konteks dicipta dengan argumen data null. Anggap penunjuk itu milik konteks yang masih hidup; untuk pengguna seterusnya, salin bait ke storan milik anda atau kekalkan konteks sehingga penggunaan selesai. Dokumentasi data bitmap Apple.

Untuk muat naik Metal, lakukan langkah ini semasa konteks yang dinormalkan masih hidup:

  1. Cipta tekstur dua dimensi dengan lebar dan tinggi piksel konteks. Gunakan bgra8Unorm untuk susun atur komponen BGRA yang ditunjukkan; Apple mentakrifkannya sebagai empat komponen ternormal tanpa tanda lapan bit dalam susunan BGRA.
  2. Untuk muat naik CPU, kekalkan mod storan lalai sistem dan jangan paksa mod kongsi khusus perkakasan. Apple mendokumenkan lalai kongsi pada Apple silicon dan lalai terurus bagi tekstur pada Mac berasaskan Intel.
  3. Panggil replace(region:mipmapLevel:withBytes:bytesPerRow:) untuk rantau tekstur penuh, aras mip 0, menggunakan penunjuk data konteks dan stride baris sebenar.
  4. Selesaikan muat naik sebelum melepaskan konteks. Untuk penggunaan semula tekstur, pastikan juga kerja GPU terdahulu yang menggunakan tekstur itu telah selesai sebelum CPU menulis ganti kandungannya.

Pilihan format dan storan ini berdasarkan dokumentasi bgra8Unorm Apple dan panduan storan sumber. Apple menyatakan bahawa kaedah penggantian itu menyalin piksel yang dibekalkan serta-merta tetapi tidak menyegerakkan operasi GPU. Untuk tekstur peribadi, laluan yang didokumenkan ialah tekstur sementara bukan peribadi diikuti blit. Dokumentasi penggantian tekstur Apple.

Muat naik tekstur mengekalkan bait yang dibekalkan; padanan susun atur komponen sahaja tidak mentakrifkan bahagian lain dalam saluran paip pemaparan anda. Bawa keputusan alpha pradarab dan sRGB dalam contoh ini ke dalam reka bentuk pengguna. Normalisasi ialah lukisan semula ke dalam format yang dipilih, jadi ia tidak seharusnya digambarkan sebagai jaminan penukaran bait demi bait yang serupa daripada setiap perwakilan AppKit yang mungkin.

Bagi tugas ini, serahan yang boleh dipercayai terdiri daripada raster berserta dimensi piksel, saiz paparan logik, stride baris, format komponen, konvensyen orientasi dan pemiliknya. Kekalkan nilai-nilai itu bersama-sama, uji dengan lukisan sintetik tidak simetri dan betulkan peringkat pertama tempat nilainya menyimpang. Dengan itu, hasil yang tersasar atau berulang menjadi masalah kontrak yang boleh dijejaki.

Sumber