2012-06-24 10 views
21

Oto prosty rysunekUIBezierPath stroke 1px line and fill 1px width rectangle - różne wyniki.

 

- (void)drawRect:(CGRect)rect 
{ 
    //vertical line with 1 px stroking 
    UIBezierPath *vertLine = [[UIBezierPath alloc] init]; 
    [vertLine moveToPoint:CGPointMake(20.0, 10.0)]; 
    [vertLine addLineToPoint:CGPointMake(20.0, 400.0)]; 
    vertLine.lineWidth = 1.0; 
    [[UIColor blackColor] setStroke]; 
    [vertLine stroke]; 

    //vertical rectangle 1px width 
    UIBezierPath *vertRect= [UIBezierPath bezierPathWithRect:CGRectMake(40.0, 10.0, 1.0, 390.0)]; 
    [[UIColor blackColor] setFill]; 
    [vertRect fill]; 

} 

Na non siatkówki 3GS i symulatora pierwsza linia jest rozmyty i wygląda na szerszą niż 1 px, ale druga linia jest ostry.

Niestety nie mam ani iPhone4, ani nowego iPada do testowania, ale na symulatorze Retina obie linie wyglądają tak samo.

Pytanie: Czy prostokącie zamiast obrysu to jedyny sposób uzyskania tego samego wyniku dla urządzeń innych niż siatkówka i siatkówka?

Odpowiedz

51

Wypełniasz wnętrze prostokąta, ale głaszczesz linię od środka. Ponieważ współrzędne w obu przypadkach (rogi prostokąta oraz współrzędne początku i końca w wierszu) są zdefiniowane jako liczby całkowite (bez ułamków), współrzędne leżą na dokładnych granicach punktów.

Powiedziałem "współrzędne" powyżej, mówiąc o punktach linii, aby nie mylić ich z punktami na ekranie. Powiedziałem też "granice punktowe" zamiast "granice pikseli" z tego samego powodu. iOS definiuje swoje współrzędne i wszystkie punkty w tak zwanych punktach zamiast pikseli. Punkt jest pomiarem niezależnym od rozdzielczości. Zarówno urządzenia siatkówki, jak i inne niż siatkówka mają taką samą liczbę punktów na ekranie, tylko tyle, że odpowiadają różnej liczbie rzeczywistych pikseli. wygląd

Miejmy na głaskanie linię, która leży na granicy punktowych (jak w pytaniu) w porównaniu do napełniania prostokąt gdzie rogi leżą na granicach punkcie:

Na poniższych ilustracjach ja głaskanie linię z czernią i wypełnianiem prostokąta pomarańczowym zarówno na ekranie nie-siatkówkowym, jak i na siatkówce. Podkreślam również linię i prostokąt za pomocą niebieskiego. W obu przypadkach można zobaczyć rozmiar punktu dla tej rozdzielczości i porównać go z rzeczywistą siatką pikseli.

W przypadku nieretinowym można zauważyć, że próba obrysowania linii od środka za pomocą linii 1-punktowej (w tym przypadku odpowiadającej szerokości linii o szerokości 1 piksela) spowoduje wypełnienie połowy pikseli na górze i połowa na pikselach poniżej. Ponieważ piksele są tylko w połowie wypełnione, krycie tych pikseli wynosi 50%. Powoduje to jaśniejszy kolor (na białym tle). Ponieważ zarówno piksele u góry, jak i poniżej są wypełnione stroną, gładząc wypełnienie, oba piksele są u góry i poniżej. Dzięki temu linia wygląda tak, jakby miała szerokość 2 pikseli zamiast jednej.

Możesz szybko porównać to z prostokątem wypełnionym od wewnątrz.

non retina

Ten sam przypadek na ekranie siatkówki wygląda inaczej. W takim przypadku rozmiar punktu jest taki sam, ale składa się z 4 pikseli zamiast 1. Tym razem, podczas gładzenia linii, pół punktu powyżej linii i pół punktu poniżej linii spowoduje pełne wypełnienie rzędu pikseli powyżej i poniżej ze względu na ekran o wyższej rozdzielczości. Oznacza to, że linia wygląda tak, jakby miała 1 punkt szerokości i że kolor wygląda na całkowicie nieprzezroczysty.

Widzimy również, że wypełniony prostokąt wygląda tak samo.

retina


Aby rozwiązać ten problem, należy umieścić punkty dla swojej linii na pół pikseli.Gładzenie linii od środka na urządzeniu o niskiej rozdzielczości oznacza, że ​​linia rozciąga się o pół punktu w górę i pół punktu w dół. Ponieważ środek linii znajduje się teraz pośrodku tego punktu, oznacza to, że obrysowana linia w całości leży w pikselach, a linia wygląda ostro. Wykonanie tego nie będzie miało żadnego wpływu na linię siatkówki, ponieważ przesunięcie w dół (lub do góry) o połowę punktu, nadal oznacza, że ​​w pełni wypełnisz piksele powyżej i poniżej.

Na poniższej ilustracji (dla siatkówki) pokazałem zarówno siatkę punktów, jak i siatkę pikseli.

half pixel non-retina half pixel retina

2

Powodem uzyskać różne wyniki z udarem i wypełnić to, że ich interpretacje argumentów jest inna.

Skok dodaje połowę szerokości linii po każdej stronie współrzędnych. Twój punkt to 20.0, a szerokość linii to 1px. Rezultatem będzie 1-pikselowa czarna linia pomiędzy (19,5-20,5), teoretycznie. Ponieważ na ekranie urządzenia nie ma żadnego nieintegralnego piksela, zostanie on przekonwertowany na 2 piksele w kolorze szarym/rozmytym pomiędzy (19-21). aby to obejść, musisz zsumować każdą ze swoich współrzędnych z 0,5 (tak jak w CGPointMake (20,5, 10,5)), aby szerokość nie była już dzielona między piksele.

Jednak argumenty w wypełnieniu służą do ustawiania granic regionu do wypełnienia, CGRectMake (40.0, 10.0, 1.0, 390.0) implikuje region pomiędzy (40 - 41). W rezultacie nie ma części ułamkowej spadającej na piksele, aby wyglądać na zamgloną.