203#if ENABLE(CSS_FILTERS)
204typedef QRgb (*PixelFilterFunction)(QRgb, const double&);
205
206static QRgb grayscale(QRgb color, const double& amount)
207{
208 int red = qRed(color);
209 int green = qGreen(color);
210 int blue = qBlue(color);
211 return qRgba(
212 red * (0.2126 + 0.7874 * (1. - amount)) + green * (0.7152 - 0.7152 * (1. - amount)) + blue * (0.0722 - 0.0722 * (1. - amount)),
213 red * (0.2126 - 0.2126 * (1. - amount)) + green * (0.7152 + 0.2848 * (1. - amount)) + blue * (0.0722 - 0.0722 * (1. - amount)),
214 red * (0.2126 - 0.2126 * (1. - amount)) + green * (0.7152 - 0.7152 * (1. - amount)) + blue * (0.0722 + 0.9278 * (1. - amount)),
215 qAlpha(color)
216 );
217}
218
219static QRgb clampRgba(int r, int g, int b, int a)
220{
221 return qRgba(std::min(255, std::max(0, r)), std::min(255, std::max(0, g)), std::min(255, std::max(0, b)), std::min(255, std::max(0, a)));
222}
223
224static QRgb sepia(QRgb color, const double& amount)
225{
226 int red = qRed(color);
227 int green = qGreen(color);
228 int blue = qBlue(color);
229 return clampRgba(
230 red * (0.393 + 0.607 * (1. - amount)) + green * (0.769 - 0.769 * (1. - amount)) + blue * (0.189 - 0.189 * (1. - amount)),
231 red * (0.349 - 0.349 * (1. - amount)) + green * (0.686 + 0.314 * (1. - amount)) + blue * (0.168 - 0.168 * (1. - amount)),
232 red * (0.272 - 0.272 * (1. - amount)) + green * (0.534 - 0.534 * (1. - amount)) + blue * (0.131 + 0.869 * (1. - amount)),
233 qAlpha(color)
234 );
235}
236
237static QRgb saturate(QRgb color, const double& amount)
238{
239 int red = qRed(color);
240 int green = qGreen(color);
241 int blue = qBlue(color);
242 return clampRgba(
243 red * (0.2126 + 0.7873 * amount) + green * (0.7152 - 0.7152 * amount) + blue * (0.0722 - 0.0722 * amount),
244 red * (0.2126 - 0.2126 * amount) + green * (0.7152 + 0.2848 * amount) + blue * (0.0722 - 0.0722 * amount),
245 red * (0.2126 - 0.2126 * amount) + green * (0.7152 - 0.7152 * amount) + blue * (0.0722 + 0.9278 * amount),
246 qAlpha(color)
247 );
248}
249
250static QRgb hueRotate(QRgb color, const double& amount)
251{
252 double rad = amount * piFloat / 180.;
253 double hueSin = sin(rad);
254 double hueCos = cos(rad);
255
256 int red = qRed(color);
257 int green = qGreen(color);
258 int blue = qBlue(color);
259 return clampRgba(
260 red * (0.2126 + hueCos * 0.7873 - hueSin * 0.2126) + green * (0.7152 + hueCos * -0.7152 - hueSin * 0.7152) + blue * (0.0722 + hueCos * -0.0722 + hueSin * 0.9278),
261 red * (0.2126 + hueCos * -0.2126 + hueSin * 0.143) + green * (0.7152 + hueCos * 0.2848 + hueSin * 0.14) + blue * (0.0722 + hueCos * -0.0722 - hueSin * 0.283),
262 red * (0.2126 + hueCos * -0.2126 - hueSin * 0.7873) + green * (0.7152 + hueCos * -0.7152 + hueSin * 0.7152) + blue * (0.0722 + hueCos * 0.9278 + hueSin * 0.0722),
263 qAlpha(color)
264 );
265}
266
267static QRgb invert(QRgb color, const double& amount)
268{
269 int red = qRed(color);
270 int green = qGreen(color);
271 int blue = qBlue(color);
272 return clampRgba(
273 red * (1 - amount) + (254 - red) * amount,
274 green * (1 - amount) + (255 - green) * amount,
275 blue * (1 - amount) + (255 - blue) * amount,
276 qAlpha(color)
277 );
278}
279
280static QRgb opacity(QRgb color, const double& amount)
281{
282 return qRgba(qRed(color), qGreen(color), qBlue(color), std::min(255, std::max(0, int(qAlpha(color) * amount))));
283}
284
285static QRgb brightness(QRgb color, const double& amount)
286{
287 int red = qRed(color);
288 int green = qGreen(color);
289 int blue = qBlue(color);
290 return clampRgba(
291 red * amount,
292 green * amount,
293 blue * amount,
294 qAlpha(color)
295 );
296}
297
298static QRgb contrast(QRgb color, const double& amount)
299{
300 int red = qRed(color);
301 int green = qGreen(color);
302 int blue = qBlue(color);
303 int intercept = 0.5 + amount / 2;
304 return clampRgba(
305 red * amount - intercept,
306 green * amount - intercept,
307 blue * amount - intercept,
308 qAlpha(color)
309 );
310}
311
312static void pixelFilter(const QImage& sourceImage, QImage& targetImage, const FloatRect& sourceRect, FilterOperation::OperationType type, const double& amount)
313{
314 ASSERT(sourceRect.size() == targetRect.size());
315
316 static const PixelFilterFunction functions[] = {0, grayscale, sepia, saturate, hueRotate, invert, opacity, brightness, contrast };
317 PixelFilterFunction function = functions[type];
318
319 for (int y = sourceRect.y(); y < sourceRect.maxY(); ++y) {
320 QRgb* targetPixels = reinterpret_cast<QRgb*>(targetImage.scanLine(y)) + int(sourceRect.x());
321 const QRgb* sourcePixels = reinterpret_cast<const QRgb*>(sourceImage.scanLine(y)) + int(sourceRect.x());
322
323 for (int x = 0; x < sourceRect.width(); ++x, ++targetPixels, ++sourcePixels)
324 *targetPixels = function(*sourcePixels, amount);
325 }
326}
327
328static void dropShadow(QImage& targetImage, const QImage& sourceImage, const FloatRect& targetRect, const FloatRect& sourceRect, int x, int y, int stdDeviation, const Color& color)
329{
330 targetImage.fill(0);
331
332 FloatRect targetShadowRect(targetRect);
333 targetShadowRect.move(x, y);
334
335 // Paint the image in shadow coordinates.
336 {
337 QPainter painter(&targetImage);
338 painter.setCompositionMode(QPainter::CompositionMode_Source);
339 painter.drawImage(targetShadowRect, sourceImage, sourceRect);
340 }
341
342 // Blur image's alpha if applicable.
343 if (stdDeviation) {
344 ShadowBlur(FloatSize(stdDeviation, stdDeviation), FloatSize(x, y), Color(), ColorSpaceDeviceRGB)
345 .blurLayerImage(targetImage.bits(), targetImage.size(), targetImage.bytesPerLine());
346 }
347
348 // Fill a rectangle with the shadow color, using the exiting alpha (source-in).
349 QPainter painter(&targetImage);
350 painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
351 painter.fillRect(targetShadowRect, color);
352
353 // Draw the original on top.
354 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
355 painter.drawImage(targetRect, sourceImage, sourceRect);
356}
357
358// This is a "trick" algorithm for fast gaussian blur. It's not as accurate as FEGaussianBlur,
359// but can be potentially faster.
360static void quickBlur(QImage& image, const double& stdDeviation)
361{
362 int boxSize = floor(stdDeviation * (3 / 4 * sqrtf(2 * piFloat)) + 0.5);
363 int width = image.width();
364 int height = image.height();
365
366 // First, we create a color-sum map. Each item in the map holds the sum of channels before it.
367 typedef unsigned long ColorSum[4];
368 ColorSum colorSums[width * height];
369 ColorSum* sumPointer = colorSums;
370 for (int y = 0; y < height; ++y) {
371 const uchar* pixels = image.scanLine(y);
372 const QRgb* rgb = reinterpret_cast<const QRgb*>(pixels);
373
374 for (int x = 0; x < width; ++x, ++rgb, ++sumPointer) {
375 (*sumPointer)[0] = qRed(*rgb);
376 (*sumPointer)[1] = qGreen(*rgb);
377 (*sumPointer)[2] = qBlue(*rgb);
378 (*sumPointer)[3] = qAlpha(*rgb);
379 for (int i = 0; i < 4; ++i) {
380 unsigned long& total = (*sumPointer)[i];
381 total += x ? (*(sumPointer - 1))[i] : 0;
382 total += y ? (*(sumPointer - width))[i] : 0;
383 total -= (x && y) ? (*(sumPointer - width - 1))[i] : 0;
384 }
385 }
386 }
387
388 // We use the sums created in the previous phase, to determine a rough average that we use for the blur.
389 float multiplier = 1. / (pow(boxSize * 2 + 1, 2));
390 for (int y = 0; y < height; ++y) {
391 uchar* pixels = image.scanLine(y);
392 QRgb* rgb = reinterpret_cast<QRgb*>(pixels);
393 for (int x = 0; x < width; ++x, ++rgb) {
394 ColorSum sum;
395 for (int i = 0; i < 4; ++i) {
396 sum[i] =
397 colorSums[std::min(width - 1, x + boxSize) + width * std::min(height - 1, y + boxSize)][i]
398 + colorSums[std::max(0, x - boxSize) + width * std::max(0, y - boxSize)][i]
399 - colorSums[std::max(0, x - boxSize) + width * std::min(height - 1, y + boxSize)][i]
400 - colorSums[std::min(width - 1, x + boxSize) + width * std::max(0, y - boxSize)][i];
401 }
402 *rgb = clampRgba(sum[0] * multiplier, sum[1] * multiplier, sum[2] * multiplier, sum[3] * multiplier);
403 }
404 }
405}
406
407void TextureMapperQt::renderFilter(const BitmapTexture& texture, const BitmapTexture& contentTexture, const FloatRect& targetRect, const FloatRect& sourceRect, const FilterOperation& filter)
408{
409 RefPtr<BitmapTexture> surface = m_currentSurface;
410 const QPixmap& sourcePixmap = static_cast<const BitmapTextureQt&>(texture).m_pixmap;
411 bindSurface(0);
412 QPixmap& targetPixmap = static_cast<BitmapTextureQt&>(*surface.get()).m_pixmap;
413 QImage sourceImage = sourcePixmap.toImage().convertToFormat(QImage::Format_ARGB32);
414 QImage targetImage = targetPixmap.toImage().convertToFormat(QImage::Format_ARGB32);
415
416 // We manually detach the target pixmap to avoid a deep copy caused by implicit sharing.
417 targetPixmap = QPixmap();
418
419 switch (filter.getOperationType()) {
420 case FilterOperation::GRAYSCALE:
421 case FilterOperation::SEPIA:
422 case FilterOperation::SATURATE:
423 case FilterOperation::HUE_ROTATE:
424 pixelFilter(sourceImage, targetImage, sourceRect, filter.getOperationType(), static_cast<const BasicColorMatrixFilterOperation&>(filter).amount());
425 break;
426 case FilterOperation::INVERT:
427 case FilterOperation::BRIGHTNESS:
428 case FilterOperation::CONTRAST:
429 case FilterOperation::OPACITY:
430 pixelFilter(sourceImage, targetImage, sourceRect, filter.getOperationType(), static_cast<const BasicComponentTransferFilterOperation&>(filter).amount());
431 break;
432 case FilterOperation::BLUR: {
433 targetImage.fill(0);
434 {
435 QPainter painter(&targetImage);
436 painter.drawImage(targetRect, sourceImage, sourceRect);
437 }
438 float blurSize = static_cast<const BlurFilterOperation&>(filter).stdDeviation().calcFloatValue(500);
439 if (blurSize)
440 quickBlur(targetImage, blurSize);
441 break;
442 }
443 case FilterOperation::DROP_SHADOW: {
444 const DropShadowFilterOperation& shadowOperation = static_cast<const DropShadowFilterOperation&>(filter);
445 dropShadow(targetImage, sourceImage, targetRect, sourceRect, shadowOperation.x(), shadowOperation.y(), shadowOperation.stdDeviation(), shadowOperation.color());
446 break;
447 }
448 }
449 targetPixmap = QPixmap::fromImage(targetImage);
450 bindSurface(surface.get());
451}
452#endif
453
454