Главная » iPhone » Вывод на поверхность экрана iPhone

0

Даже после того как вы добавите уровень поверхности к виду, ничего не будет отображено, поскольку сам видеобуфер пуст. Объект CoreSurfaceBuffer содержит указатель на необработанный видеобуфер. Чтобы получить доступ к базовому адресу этого необработанного видеобуфера, воспользуйтесь функцией CoreSurfaceBufferGetBaseAddress:

unsigned long *baseAddress = CoreSurfaceBufferGetBaseAddress (screenSurface);

Буфер является одномерным массивом, даже несмотря на то, что ваше приложение и пользователь рассматривают его как двумерный. Пикселы записываются слева направо и сверху вниз. Например, baseAddress[0] адресует левый верхний пиксел на поверхности, baseAddress[319] адресует правый верхний пиксел для разрешения 320×480, используемого в данной поверхности, a baseAddress [320] адресует самый крайний левый пиксел во второй строке.

Поскольку в данном примере используется тип пикселов RGBA, то каждый пиксел имеет длину в четыре байта. Использование указателя unsigned long (который также имеет длину в четыре байта) позволяет всем пикселам быть адресованными как элементы в массиве.

Вы также можете предпочесть использование указателя unsigned char, который позволит вам адресовать отдельные значения пиксела (один байт для каждого канала). Однако имейте в виду, что для того чтобы получить следующий пиксел, вам необходимо увеличивать ваш указатель на четыре байта за раз (исходя из четырехбайтового типа пикселов). Например, (unsigned char *) baseAddress[0] ссылается на красный канал первого пиксела. baseAddress [1] — на зеленый канал, baseAddress[2] — на голубой канал, baseAddress [3] —на альфа-канал и, наконец, baseAddress [4 ] —на красный канал следующего пиксела.

16-битные форматы пикселов

16-битные форматы пикселов широко используются вместо 32-битного формата пикселов RGBA для обеспечения большего быстродействия, когда точность передачи цветов не является критичной, а еще потому, что только дорогие настольные дисплеи могут поддерживать оборудование реалистичной 32-битной цветопередачи, и нет особых причин использовать ее на сотовых телефонах. Поскольку в наличии имеется не так уж много памяти, то 16- бигный фрейм может быть скопирован в два раза быстрее 32-битного. Чтобы использовать этот формат пикселов, до создания поверхности экрана внесите следующие изменения в свойства словаря этой поверхности: int х = 320, у = 480, pitch = х*2, size = 2*х*у; char *pixelFormat = "565L";

16-битный формат пикселов для каждого пиксела использует по два байта и обозначается как формат пикселов 565L. После того как поверхность будет создана, доступ к ней можно получить с помощью указателя unsignedshort, который имеет длину не четыре, а два байта:

unsigned short *baseAddress =

CoreSurfaceBufferGetBaseAddress(screenSurface) ;

Чтобы конвертировать значения RGB в 16-битные значения, подходящие /для записи в такой буфер, вы можете определить макрос, позволяющий вашему приложению плавно перемещаться между 16-битным и 32-битным RGB:

#define RGB2565L(R, G, В) ((R » 3) « 11) i ((G » 2) « 5) I ((B » 3) « 0)

Буфер фрейма

Буфер фрейма (frame buffer)— это вторичный буфер памяти, используемый для построения видеофрейма прежде, чем он будет отображен на поверхности экрана. Это может помочь предотвратить мерцание приложения, выводящего графику по одной строке за раз (пример— эмуляторы), а также может быть использовано для синхронизации программ, которые должны отображаться во фреймах за секунду. Использование буфера фрейма полезно также для объединения нескольких видеоуровней вместе до отображения финального фрейма. Например, если вы пишете игру, которая рисует несколько различных видеоуровней (фон, спрайты и HUD), то каждый отдельный видеоуровень может быть нарисован во внутреннем буфере фрейма до того, как финальный фрейм будет выведен на экран. Настройка и копирование буфера фрейма, если вы решите использовать его, — это работа приложения. Когда поверхность экрана будет создана, ее размер рассчитывается как размер пиксела, умноженный на ширину и высоту поверхности. Чтобы использовать буфер фрейма, создайте буфер такого же размера: workBuffer = malloc(2 * х * у);

Теперь переместите всю вашу обработку видео так, чтобы она занимала место в этом рабочем буфере до тех пор, пока программа не закончит вывод фрейма. Когда фрейм будет готов, придет время скопировать его в видеобуфер поверхности. Период между завершением фрейма и выводом следующего фрейма называется периодом v-blank; это такой период, когда рабочий буфер может быть безопасно выведен в буфер поверхности экрана: memcpy(baseAddress, workBuffer, 2 * х * у); [ screenView setNeedsDisplay ];

Пример: случайный снег

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

Чтобы построить данный пример, необходимо подключить платформы Core Surface и Quartz Core:

$ arm-apple-darwin9-gcc -о MyExample MyExample.m -lobjc

-framework CoreFoundation -framework Foundation -framework UIKit -framework CoreSurface -framework QuartzCore

#impoгt <СоreFoundation/CoreFoundation.h> #import <UIKit/UIKit.h> #import <CoreSurface/CoreSurface.h>

@interface MainView : UlView {

CoreSurfaceBufferRef screenSurface; unsigned short *baseAddress; CALayer *screenLayer;

}

-   (id)initwithFrame:(CGRect)frame;

-   (void)drawRect:(CGRect)rect;

-   (CoreSurfaceBufferRef)screenSurface;

-   (void)dealloc; @end

@interface MyApp : UIApplication

{

UlWindow *window; MainView *mainView;

}

В листингах 5.1 и 5.2 приведен код приложения.

#inport "MyExample.h"

int main(int argc, char **argv) {

-   (void)applicationDidFinishLaunching:(NSNotification *)aNotification; @end

NSAutoreleasePool *autoreleasePool =

[ [ NSAutoreleasePool alloc ] init ]; int returnCode = UIApplicationMain(argc, argv, @"MyApp", @"MyApp"); [ autoreleasePool release ]; return returnCode;

}

@implementation MyApp

-    (void)applicationDidFinishLaunching:(NSNotification *)aNotification { window = [ [ UlWindow alloc ] initWithContentRect:

[ UIHardware fullScreenApplicationContentRect ]

] ;

CGRect rect = [ UIHardware fullScreenApplicationContentRect ]; rect.origin.x = rect.origin.у = O.Of;

mainView = [ [ MainView alloc ] initWithFrame: rect ];

[ window setContentView: mainView ]; [ window orderFront: self ]; [ window makeKey: self ]; [ window _setHidden: NO ];

NSTimer *timer = [ NSTimer scheduledTimerWithTimelnterval: 0.05 target: self

selector: ^selector(handleTimer:) userlnfo: nil repeats: YES ];

)

-    (void) handleTimer: (NSTimer *) timer

{

CoreSurfaceBufferRef screenSurface; unsigned short *baseAddress; int i;

screenSurface = [ mainView screenSurface ];

baseAddress = CoreSurfaceBufferGetBaseAddress(screenSurface);

for(i=0;i < 320 * 480;i++)

baseAddress[i] = rand( ) % OxFFFF; [ mainView setNeedsDisplay ];

}

@end

@implementation MainView – (id)initwithFrame:(CGRect)rect {

self = [ super initwithFrame: rect ]; if (nil != self) {

CFMutableDictionaryRef diet;

int x = 320, у = 480, pitch = x * 2, size = 2 * x * y, i; char *pixelFormat = "565L";

/* Создаем поверхность экрана */

diet = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,

SkCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

CFPictionarySetValue(diet, kCoreSurfaceBufferGlobal, kCFBooleanTrue);

CFDictionarySetValue(diet, kCoreSurfaceBufferPitch,

CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pitch));

CFDictionarySetValue(diet, kCoreSurfaceBufferWidth,

CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &x));

CFDictionarySetValue(diet, kCoreSurfaceBufferHeight,

CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &y));

CFDictionarySetValue(diet, kCoreSurfaceBufferPixelFormat, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, pixelFormat));

CFDictionarySetValue(diet, kCoreSurfaceBufferAllocSize,

CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &size));

screenSurface = CoreSurfaceBufferCreate(diet);

screenLayer = [ [ CALayer layer ] retain ]; [ screenLayer setFrame: rect ]; [ screenLayer setContents: screenSurface ]; [ screenLayer setOpaque: YES ];

CoreSurfaceBufferLock(screenSurface, 3); [ [ self flayer ] addSublayer: screenLayer ];

CoreSurfaceBufferUnlock(screenSurface);

}

return self;

)

-   (void)drawRect:(CGRect)rect { }

-   (CoreSurfaceBufferRef)screenSurface { return screenSurface;

}

-   (void)dealloc {

[ screenLayer release ]; [ self dealloc ]; [ super dealloc ];

}

@end

Как это работает

Пример с сумасшедшим снегом работает следующим образом:

1.    При порождении приложения создается объект MainView и вызывается его метод initWithFrame.

2.     initWithFrame создает 16-битную поверхность экрана размером 320×480 и привязывает ее к ее собственному объекту CALayer. Затем он добавляет этот уровень к основному виду.

3. Создается объект NSTimer, в результате чего каждые 0,05 секунд вызывается метод handieTimer. Эта процедура проходит по всей поверхности экрана и применяет случайное значение цвета к каждому пикселу. Кроме того, вызывается метод setNeedsDisplay данного объекта, приказывающий объекту перерисовать экран.

Источник: Здзиарски Дж. iPhone. Разработка приложений с открытым кодом: Пер„с англ. — 2-е изд., перераб. и доп. — СПб.: БХВ-Петербург, 2009. — 368 е.: ил.

По теме:

  • Комментарии