|
Май
31
|
В моем приложении Postcards есть меню дизайна, где можно настраивать цвета объектов. Соответственно, мне понадобился цветоподборщик. Такой инструмент практически на любой платформе легко создать на основе любого изображения цветовой палитры. Выводим его на экран, чтобы пользователь мог щелкнуть по нужному участку, и выполняем запрос типа getPixelColorAtScreenLocation(x,y).
Еще в 1983 г. можно было проделывать это на ZX-Spectrum, а вот в Cocoa Touch такая возможность не предусмотрена (если ошибаюсь, поправьте). Какие есть варианты?
- Раскопать цветовую палитру и смешивать цвета бегунками. Не самый удобный вариант для обычного пользователя.
- Вручную создать подстановочную таблицу для поиска цвета. Сложная работа, чреватая проблемами при смене изображения.
- Самостоятельно проанализировать файл с изображением и найти координаты x и y для цвета с помощью библиотеки третьего разработчика. Возможно…
- Подчинить графику силе собственной воли, заставив ее «проанализировать» изображение самостоятельно, отобразив пиксели без компрессии.
Поиск информации в интернете натолкнул на мысль о реализации последнего варианта, ведь мы работаем с готовыми интерфейсами прикладной программы, принимающими любые изображения:
- Создаем off-screen контекст битового образа — без компрессии и с точной схемой преобразования битов в пиксели.
- Переносим в данный контекст изображение цветоподборщика.
- Превращаем координаты касания x, y в определенную позицию битового буфера контекста.
- Байты пикселей со скорректированными координатами преобразуем в “UIColor“.
Всю эту композицию помещаем в “ColorPickerImageView” — подкласс “UIImageView“. При щелчке этот инструмент получает соответствующий пиксель цвета. С ним можно зарегистрировать простого делегата, чтобы с выбранным цветом получать обратный вызов. Добавляете собственное изображение, получив компактную цветовую палитру в углу или растянув ее на весь экран. Работа с производной “UIImageView” ничем не отличается от операций с “UIImageView” в коде или редакторе IB. Исходный код для получения пикселя (с полным классом) представлен ниже. Код ColorRicker можно загрузить и скопировать [здесь].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | - (UIColor*) getPixelColorAtLocation:(CGPoint)point { UIColor* color = nil; CGImageRef inImage = self.image.CGImage; // Создаем off-screen контекст битового образа, в котором будет рисоваться изображение. Формат ARGB — 4 байта на пиксель: Alpa, Red, Green, Blue CGContextRef cgctx = [self createARGBBitmapContextFromImage:inImage]; if (cgctx == NULL) { return nil; /* error */ } size_t w = CGImageGetWidth(inImage); size_t h = CGImageGetHeight(inImage); CGRect rect = {{0,0},{w,h}}; // Добавляем к контексту битового образа изображение. При рисовании // память, выделенная для преобразования контекста, будет содержать // необработанные данные изображения в заданном цветовом пространстве. CGContextDrawImage(cgctx, rect, inImage); // Теперь получаем подборщик для данных изображения, ассоциированных // с контекстом битового образа. unsigned char* data = CGBitmapContextGetData (cgctx); if (data != NULL) { //смещение находит пиксель в данных по x,y. //4 на 4 байта данных на пиксель, w — ширина строки данных. int offset = 4*((w*round(point.y))+round(point.x)); int alpha = data[offset]; int red = data[offset+1]; int green = data[offset+2]; int blue = data[offset+3]; NSLog(@"offset: %i colors: RGB A %i %i %i %i",offset,red,green,blue,alpha); color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f) alpha:(alpha/255.0f)]; } // По окончании освободите контекст CGContextRelease(cgctx); // Свободная память изображения для контекста if (data) { free(data); } return color; } - (CGContextRef) createARGBBitmapContextFromImage:(CGImageRef) inImage { CGContextRef context = NULL; CGColorSpaceRef colorSpace; void * bitmapData; int bitmapByteCount; int bitmapBytesPerRow; // Получаем ширину, высоту изображения. Нам оно понадобится целиком. size_t pixelsWide = CGImageGetWidth(inImage); size_t pixelsHigh = CGImageGetHeight(inImage); // Декларируем число байт на строку. Пиксель битовой матрицы в примере // представлен четырьмя байтами; по 8 бит на красный, зеленый, синий и // альфа-канал. bitmapBytesPerRow = (pixelsWide * 4); bitmapByteCount = (bitmapBytesPerRow * pixelsHigh); // Работаем со стандартным цветовым пространством RGB. colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); if (colorSpace == NULL) { fprintf(stderr, "Error allocating color space\n"); return NULL; } // Выделяем память для данных изображения. Это область памяти, // где будет визуализироваться любое изображение для контекста. bitmapData = malloc( bitmapByteCount ); if (bitmapData == NULL) { fprintf (stderr, "Memory not allocated!"); CGColorSpaceRelease( colorSpace ); return NULL; } // Создайте контекст битового образа. Нужны предварительно // размноженные ARGB, по 8 бит на компонент. Независимо от формата // исходного изображения(CMYK, Grayscale и пр.), оно будет // преобразовано в формат, заданный через "CGBitmapContextCreate". context = CGBitmapContextCreate (bitmapData, pixelsWide, pixelsHigh, 8, // бит на компонент bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst); if (context == NULL) { free (bitmapData); fprintf (stderr, "Context not created!"); } // Обязательно освободите цветовое пространство перед возвратом CGColorSpaceRelease( colorSpace ); return context; } |
Вот и все. Удачи


Последние комментарии
Подскажите пожалуйста… Вот...
Код не открывает страницы по простой причине -...