При раскраске изображения альфа-канал игнорируется - почему и как исправить?

Вот что я пытаюсь сделать: Слева - обычное неокрашенное изображение RGBA, которое я создал за пределами экрана и кэшировал для повышения скорости (изначально создается очень медленно, но очень быстро создается). раскрасить любым цветом позже, если потребуется). Это квадратное изображение с круговым завитком. Внутри круга изображение имеет альфа / непрозрачность 1. Вне круга оно имеет альфа / непрозрачность 0. Я отобразил его здесь внутри UIView с цветом фона [UIColor scrollViewTexturedBackgroundColor]. Справа показано, что происходит, когда я пытаюсь раскрасить изображение, закрашивая сплошной красный прямоугольник поверх него после установки CGContextSetBlendMode(context, kCGBlendModeColor).

Uncolorized«Неправильно

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

На самом деле я хочу вот что:

Раскрашено правильно

Теперь, в данном конкретном случае, я могу установить область отсечения в виде круга, чтобы область за пределами круга оставалась нетронутой - и это то, что я сделал здесь в качестве обходного пути.

Но в моем приложении мне также нужно иметь возможность раскрашивать произвольные формы, где я не знаю траектории обрезки / контура. Одним из примеров является раскрашивание белого текста путем наложения градиента. Как это сделать? Я подозреваю, что должен быть какой-то способ сделать это эффективно - и, как правило, без каких-либо странных трюков с контурами / обрезкой - с использованием масок изображений ... но мне еще предстоит найти учебник по этому поводу. Очевидно, это возможно, потому что я видел текст с цветным градиентом в других играх.

Между прочим, я не могу начать с градиента и обрезать / убрать части, которые мне не нужны, потому что (как показано в примере выше) мои неокрашенные исходные изображения, как правило, имеют оттенки серого, а не чисто белые. Так что мне действительно нужно начать с неокрашенного изображения, а затем раскрасить его.

p.s. - kCGBlendModeMultiply также имеет те же недостатки / недостатки / особенности, когда дело доходит до раскрашивания частично прозрачных изображений. Кто-нибудь знает, почему Apple решила поступить именно так? Это как если бы код раскраски Quartz обрабатывает RGBA (0,0,0,0) как RGBA (0,0,0,1), то есть полностью игнорирует и уничтожает альфа-канал.


person Todd Lehman    schedule 20.06.2012    source источник


Ответы (1)


Один из подходов, который вы можете использовать и который будет работать, - это создать маску из исходного изображения, а затем вызвать метод CGContextClipToMask () перед рендерингом вашего изображения с установленным режимом наложения умножения. Вот код CoreGraphics, который устанавливает маску перед отрисовкой изображения в цвет.

  CGContextRef context = [frameBuffer createBitmapContext];
  CGRect bounds = CGRectMake( 0.0f, 0.0f, width, height );
  CGContextClipToMask(context, bounds, maskImage.CGImage);
  CGContextDrawImage(context, bounds, greyImage.CGImage);

Немного сложнее будет взять исходное изображение и сгенерировать maskImage. Что вы можете сделать для этого, так это написать цикл, который будет проверять каждый пиксель и записывать черный или белый пиксель в качестве значения маски. Если исходный пиксель в изображении для раскрашивания полностью прозрачен, запишите черный пиксель, иначе запишите белый пиксель. Обратите внимание, что значением маски будет изображение 24BPP. Вот код, который даст вам правильное представление.

  uint32_t *inPixels = (uint32_t*) MEMORY_ADDR_OF_ORIGINAL_IMAGE;
  uint32_t *maskPixels = malloc(numPixels * sizeof(uint32_t));
  uint32_t *maskPixelsPtr = maskPixels;

  for (int rowi = 0; rowi < height; rowi++) {
    for (int coli = 0; coli < width; coli++) {
      uint32_t inPixel = *inPixels++;
      uint32_t inAlpha = (inPixel >> 24) & 0xFF;
      uint32_t cval = 0;
      if (inAlpha != 0) {
        cval = 0xFF;
      }
      uint32_t outPixel = (0xFF << 24) | (cval << 16) | (cval << 8) | cval;
      *maskPixelsPtr++ = outPixel;
    }
  }

Конечно, вам нужно будет заполнить все детали и создать графические контексты и так далее. Но общая идея состоит в том, чтобы просто создать свою собственную маску, чтобы отфильтровать рисование красных частей за пределами круга.

person MoDJ    schedule 21.03.2013