Bagaimana cara mendapatkan teks di CATextLayer menjadi jelas

92

Saya telah membuat CALayerdengan menambahkan CATextLayerdan teks menjadi buram. Dalam dokumen, mereka berbicara tentang "sub-pixel antialiasing", tapi itu tidak berarti banyak bagi saya. Adakah yang punya potongan kode yang membuat CATextLayerdengan sedikit teks yang jelas?

Berikut teks dari dokumentasi Apple:

Catatan: CATextLayer menonaktifkan antialiasing sub-piksel saat merender teks. Teks hanya dapat digambar menggunakan antialiasing sub-piksel jika digabungkan menjadi latar belakang buram yang ada pada saat yang sama dengan rasterisasi. Tidak ada cara untuk menggambar teks subpiksel-antialiased dengan sendirinya, baik menjadi gambar atau lapisan, secara terpisah sebelum memiliki piksel latar belakang untuk menenun piksel teks. Mengatur properti opacity dari layer menjadi YES tidak mengubah mode rendering.

Kalimat kedua menyiratkan bahwa seseorang bisa mendapatkan teks yang terlihat bagus jika compositesmembuatnya menjadi existing opaque background at the same time that it's rasterized. Itu bagus, tapi bagaimana cara menggabungkannya dan bagaimana Anda memberinya latar belakang buram dan bagaimana Anda merasternya?

Kode yang mereka gunakan dalam contoh Menu Kios adalah sebagai berikut: (Ini OS X, bukan iOS, tapi saya anggap itu berfungsi!)

NSInteger i;
for (i=0;i<[names count];i++) {
    CATextLayer *menuItemLayer=[CATextLayer layer];
    menuItemLayer.string=[self.names objectAtIndex:i];
    menuItemLayer.font=@"Lucida-Grande";
    menuItemLayer.fontSize=fontSize;
    menuItemLayer.foregroundColor=whiteColor;
    [menuItemLayer addConstraint:[CAConstraint
         constraintWithAttribute:kCAConstraintMaxY
                      relativeTo:@"superlayer"
                       attribute:kCAConstraintMaxY
                          offset:-(i*height+spacing+initialOffset)]];
    [menuItemLayer addConstraint:[CAConstraint
         constraintWithAttribute:kCAConstraintMidX
                      relativeTo:@"superlayer"
                       attribute:kCAConstraintMidX]];
    [self.menuLayer addSublayer:menuItemLayer];
} // end of for loop 

Terima kasih!


EDIT: Menambahkan kode yang sebenarnya saya gunakan yang menghasilkan teks buram. Ini dari pertanyaan terkait yang saya posting tentang menambahkan UILabeldaripada CATextLayertetapi mendapatkan kotak hitam. http://stackoverflow.com/questions/3818676/adding-a-uilabels-layer-to-a-calayer-and-it-just-shows-black-box

CATextLayer* upperOperator = [[CATextLayer alloc] init];
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGFloat components1[4] = {1.0, 1.0, 1.0, 1.0};
CGColorRef almostWhite = CGColorCreate(space,components1);
CGFloat components2[4] = {0.0, 0.0, 0.0, 1.0};
CGColorRef almostBlack = CGColorCreate(space,components2);
CGColorSpaceRelease(space);
upperOperator.string = [NSString stringWithFormat:@"13"];
upperOperator.bounds = CGRectMake(0, 0, 100, 50);
upperOperator.foregroundColor = almostBlack;
upperOperator.backgroundColor = almostWhite;
upperOperator.position = CGPointMake(50.0, 25.0);
upperOperator.font = @"Helvetica-Bold";
upperOperator.fontSize = 48.0f;
upperOperator.borderColor = [UIColor redColor].CGColor;
upperOperator.borderWidth = 1;
upperOperator.alignmentMode = kCAAlignmentCenter;
[card addSublayer:upperOperator];
[upperOperator release];
CGColorRelease(almostWhite);
CGColorRelease(almostBlack);

EDIT 2: Lihat jawaban saya di bawah untuk bagaimana ini diselesaikan. sbg.

Steve
sumber
Ini tidak akan menjawab pertanyaan Anda, tetapi mungkin relevan untuk seseorang seperti saya yang hanya ingin menggambar teks yang dikaitkan, dengan cara yang paling mirip dengan menggunakan UILabel: stackoverflow.com/questions/3786528/…
DenNukem

Jawaban:

229

Jawaban singkat - Anda perlu mengatur penskalaan konten:

textLayer.contentsScale = [[UIScreen mainScreen] scale];

Beberapa waktu yang lalu saya belajar bahwa ketika Anda memiliki kode gambar khusus, Anda harus memeriksa tampilan retina dan mengatur skala grafik Anda sesuai dengan itu. UIKitmenangani sebagian besar ini, termasuk penskalaan font.

Tidak demikian halnya dengan CATextLayer.

Keburaman saya berasal dari memiliki .zPositionyang bukan nol, yaitu, saya menerapkan transformasi ke lapisan induk saya. Dengan menyetel ini ke nol, keburaman hilang, dan digantikan oleh pikselasi serius.

Setelah mencari tinggi dan rendah, saya menemukan bahwa Anda dapat mengatur .contentsScaleuntuk CATextLayerdan Anda dapat mengaturnya untuk [[UIScreen mainScreen] scale]agar sesuai dengan resolusi layar. (Saya berasumsi ini berfungsi untuk non-retina, tetapi saya belum memeriksanya - terlalu lelah)

Setelah memasukkan ini untuk saya CATextLayer, teks menjadi jernih. Catatan - ini tidak perlu untuk lapisan induk.

Dan keburamannya? Itu muncul kembali saat Anda memutar dalam 3D, tetapi Anda tidak menyadarinya karena teks dimulai dengan jelas dan saat bergerak, Anda tidak dapat mengetahuinya.

Masalah terpecahkan!

Steve
sumber
37
Ini adalah jawaban singkatnya: textLayer.contentScale = [[UIScreen mainScreen] scale]; BTW, saya suka jawaban yang panjang juga :)
nacho4d
Ini berlaku untuk semua CALayer yang dibuat secara terprogram. Misalnya, jika Anda melihat gambar yang ditarik ke CALayer Anda tidak tajam pada layar retina, kemungkinan inilah masalahnya dan solusinya.
Jeff Argast
17
Koreksi _textLayer.contentsScale = [[UIScreen mainScreen] scale]; Anda kehilangan "s";)
Ralphleon
1
Ini tidak berhasil untuk saya. Saya menambahkan CATextLayer ke video (AVFoundation / AVMutableComposition) oleh AVVideoCompositionCoreAnimationTool. Teksnya masih memiliki aliasing.
vietstone
tidak ada opsi skala di mac osx
Sangram Shivankar
35

Cepat

Atur layer teks untuk menggunakan skala yang sama dengan layar.

textLayer.contentsScale = UIScreen.main.scale

Sebelum:

masukkan deskripsi gambar di sini

Setelah:

masukkan deskripsi gambar di sini

Suragch
sumber
macOS swift 4.2 versi: (NSScreen.main? .backingScaleFactor)!
roberto
17

Pertama, saya ingin menunjukkan bahwa Anda telah menandai pertanyaan Anda dengan iOS, tetapi manajer kendala hanya tersedia di OSX, jadi saya tidak yakin bagaimana Anda membuat ini berfungsi kecuali Anda dapat menautkannya di simulator entah bagaimana. Di perangkat, fungsi ini tidak tersedia.

Selanjutnya, saya hanya akan menunjukkan bahwa saya sering membuat CATextLayers dan tidak pernah memiliki masalah kabur yang Anda maksud, jadi saya tahu itu bisa dilakukan. Singkatnya, pemburaman ini terjadi karena Anda tidak memposisikan lapisan Anda pada keseluruhan piksel. Ingatlah bahwa ketika Anda mengatur posisi sebuah layer, Anda menggunakan nilai float untuk x dan y. Jika nilai-nilai tersebut memiliki angka setelah desimal, lapisan tidak akan ditempatkan pada keseluruhan piksel dan oleh karena itu akan memberikan efek kabur ini - tingkat yang bergantung pada nilai sebenarnya. Untuk mengujinya, cukup buat CATextLayer dan tambahkan secara eksplisit ke pohon lapisan untuk memastikan bahwa parameter posisi Anda pada seluruh piksel. Sebagai contoh:

CATextLayer *textLayer = [CATextLayer layer];
[textLayer setBounds:CGRectMake(0.0f, 0.0f, 200.0f, 30.0f)];
[textLayer setPosition:CGPointMake(200.0f, 100.0f)];
[textLayer setString:@"Hello World!"];

[[self menuLayer] addSublayer:textLayer];

Jika teks Anda masih buram, berarti ada hal lain yang salah. Teks buram pada lapisan teks Anda adalah artefak dari kode yang salah ditulis dan bukan kemampuan lapisan yang diinginkan. Saat menambahkan lapisan Anda ke lapisan induk, Anda cukup memaksakan nilai x dan y ke seluruh piksel terdekat dan itu akan menyelesaikan masalah kabur Anda.

Matt Long
sumber
15

Sebelum menyetel shouldRasterize, Anda harus:

  1. atur rasterizationScale dari lapisan dasar yang akan Anda rasterisasi
  2. mengatur properti contentScale dari CATextLayers dan mungkin jenis layer lainnya (tidak ada salahnya untuk melakukannya)

Jika Anda tidak melakukan # 1, maka sub lapisan versi retina akan terlihat buram, bahkan untuk CALayers normal.

- (void)viewDidLoad {
  [super viewDidLoad];

  CALayer *mainLayer = [[self view] layer];
  [mainLayer setRasterizationScale:[[UIScreen mainScreen] scale]];

  CATextLayer *messageLayer = [CATextLayer layer];
  [messageLayer setForegroundColor:[[UIColor blackColor] CGColor]];
  [messageLayer setContentsScale:[[UIScreen mainScreen] scale]];
  [messageLayer setFrame:CGRectMake(50, 170, 250, 50)];
  [messageLayer setString:(id)@"asdfasd"];

  [mainLayer addSublayer:messageLayer];

  [mainLayer setShouldRasterize:YES];
}
Gabe
sumber
4

Anda harus melakukan 2 hal, yang pertama disebutkan di atas:

Perluas CATextLayer dan setel properti opaque dan contentScale untuk mendukung tampilan retina dengan benar, lalu render dengan anti aliasing diaktifkan untuk teks.

+ (TextActionLayer*) layer
{
  TextActionLayer *layer = [[TextActionLayer alloc] init];
  layer.opaque = TRUE;
  CGFloat scale = [[UIScreen mainScreen] scale];
  layer.contentsScale = scale;
  return [layer autorelease];
}

// Render after enabling with anti aliasing for text

- (void)drawInContext:(CGContextRef)ctx
{
    CGRect bounds = self.bounds;
    CGContextSetFillColorWithColor(ctx, self.backgroundColor);
    CGContextFillRect(ctx, bounds);
    CGContextSetShouldSmoothFonts(ctx, TRUE);
    [super drawInContext:ctx];
}
MoDJ
sumber
3

Jika Anda datang mencari masalah serupa di sini untuk CATextLayer di OSX, setelah banyak melakukan benturan di dinding, saya mendapatkan teks yang jelas dan tajam dengan melakukan:

text_layer.contentsScale = self.window!.backingScaleFactor

(Saya juga mengatur tampilan background layer contentScale menjadi sama).

james_alvarez
sumber
0

Ini lebih cepat dan lebih mudah: Anda hanya perlu mengatur contentScale

    CATextLayer *label = [[CATextLayer alloc] init];
    [label setFontSize:15];
    [label setContentsScale:[[UIScreen mainScreen] scale]];
    [label setFrame:CGRectMake(0, 0, 50, 50)];
    [label setString:@"test"];
    [label setAlignmentMode:kCAAlignmentCenter];
    [label setBackgroundColor:[[UIColor clearColor] CGColor]];
    [label setForegroundColor:[[UIColor blackColor] CGColor]];
    [self addSublayer:label];
Alejandro Luengo
sumber