2014-02-14 1 views
0

Я устанавливаю .contents CALayer в CGImage, полученный от рисования в NSBitMapImageRep.Как можно уменьшить края изображения CALayer при растяжении во время изменения размера?

Насколько я понимаю из документов docs и WWDC, устанавливая объект .contentsCenter слоя на NSRect, например {{0.5, 0.5}, {0, 0}}, в сочетании с .contentsGravity kCAGravityResize должен привести к Core Animation изменяет размер слоя, растягивая средний пиксель, верхний и нижний горизонтально, а стороны вертикально.

Это почти работает, но не совсем. Слой изменяется более или менее правильно, но если я рисую линии на краю растрового изображения, то при изменении размера окна можно увидеть, что линии очень незначительно колеблются. Это достаточно тонко, чтобы быть едва ли проблемой, пока изменение размера не уменьшится примерно до 1/4 от размера исходного слоя, ниже того, что линии могут быть тонкие и полностью исчезнуть. Если я рисую растровые изображения несколько раз при разных размерах, небольшие различия в толщине линии очень очевидны.

Первоначально я искал проблему выравнивания пикселей, но это не может быть так, потому что толщина неподвижного края LH (например) будет колебаться при изменении размера края RH. Это происходит на экранах 1x и 2x.


Вот несколько тестовых кодов. Это метод updateLayer из слоя спинками NSView подкласса (я использую альтернативный, не DrawRect рисовать путь):

- (void)updateLayer { 

    id image = [self imageForCurrentScaleFactor]; // CGImage 

    self.layer.contents = image; 
    // self.backingScaleFactor is set from the window's backingScaleFactor 
    self.layer.contentsScale = self.backingScaleFactor; 
    self.layer.contentsCenter = NSMakeRect(0.5, 0.5, 0, 0); 
    self.layer.contentsGravity = kCAGravityResize; 

} 

А вот некоторые тест рисунок кода (создание изображения, поступающего от imageForCurrentScaleFactor выше):

CGFloat width = rect.size.width; 
    CGFloat height = rect.size.height; 
    NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL 
                     pixelsWide: width * scaleFactor 
                     pixelsHigh: height * scaleFactor 
                     bitsPerSample: 8 
                    samplesPerPixel: 4 
                      hasAlpha: YES 
                      isPlanar: NO 
                    colorSpaceName: NSCalibratedRGBColorSpace 
                     bytesPerRow: 0 
                     bitsPerPixel: 0]; 

    [imageRep setSize:rect.size]; 

    [NSGraphicsContext saveGraphicsState]; 

    NSGraphicsContext *ctx = [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep]; 
    [NSGraphicsContext setCurrentContext:ctx]; 

    [[NSColor whiteColor] setFill]; 
    [NSBezierPath fillRect:rect]; 

    [[NSColor blackColor] setStroke]; 
    [NSBezierPath setDefaultLineWidth:1.0f]; 
    [NSBezierPath strokeRect:insetRect]; 

    [NSGraphicsContext restoreGraphicsState]; 

    // image for CALayer.contents is now [imageRep CGImage] 

ответ

0

Я нашел практический ответ, но был бы заинтересован в том, чтобы комментарии были подробно заполнены от всех, кто знает, как это работает.

Проблема была связана с тем, как растягивается CALayer. Я рисовал в растровое изображение произвольного размера, основываясь на том, что (как предлагают CALayer docs) использование .contentsCenter с нулевой шириной и высотой фактически приведет к растягиванию с девятью частями, выбирая один центральный пиксель как центральной вытяжной части. С этим растровым изображением в качестве содержимого слоя я мог бы изменить размер CALayer на любой желаемый размер (вниз или вверх).

Оказывается, проблема «размер библиотеки» была проблемой. Что-то странное происходит в том, как CALayer растягивает края (по крайней мере, при изменении размера). Вместо того, чтобы сделать начальную рамку для рисования крошечной (т. Е. Достаточно большой, чтобы соответствовать моему контурному рисунку плюс пара пикселей для центральной растягивающей части), ничто не врезается в края во время растяжки.

Растровое протягивается должным образом, если они созданы прямоугольником только достаточно большим, чтобы соответствовать содержанию и растяжение центра пикселя, т.е. .:

NSRect rect = NSMakeRect(0, 0, lineWidth * 2 + 2, lineWidth * 2 + 2); 

Это крошечное изображение растягивается до любого большего размера идеально.

+0

Я могу принять свой собственный ответ, поскольку он решает проблему. Но я до сих пор не понимаю, почему сохранение растрового прямоугольника малого размера имеет такое значение. Создание содержимогоЦентр 1 пиксель (т. Е. 0,5, 0,5, 0, 0) должен помешать вытянутым линиям вверху вертикально (или по сторонам, растянутым горизонтально), независимо от размера прямоугольника. Я приму ответ с убедительным объяснением этого. – Cris

0

Решение (если вы говорите о проблеме, о которой, я думаю, вы говорите), должно иметь запас прозрачных пикселей, образующих внешние края изображения. Один пиксель толщиной, все вокруг, сделает это. Причина в том, что проблема (если это проблема, я думаю, это) возникает только с видимыми пикселями, которые касаются внешнего края изображения. Поэтому идея состоит в том, чтобы иметь no видимые пиксели касаются внешнего края изображения.

+0

Думаю, мы должны решать различные проблемы. Я попробовал ваше решение (ни в коем случае), прежде чем придумать свой собственный ответ (теперь опубликовано). – Cris

+0

На самом деле ваше решение - это то, что я говорил. Ваше первоначальное изображение никогда не касается каких-либо краев, поэтому растяжка работает по вашему желанию. Вы предоставили прозрачный запас, о котором я говорил. – matt

+0

Я так не думаю. Прямоугольник в моем ответе - это только начальный размер для растрового изображения, и это небольшой начальный размер, который делает последующее изменение размера правильной работы. Это как-то связано с тем, как Core Animation обрабатывает условные 9 частей, подразумеваемых 1-пиксельным содержимым. И нет прозрачных пикселей в растровом изображении (я заполняю весь прямоугольник). – Cris