summaryrefslogtreecommitdiffstats
path: root/gfx/2d/CGTextDrawing.h
blob: b9e3b374a43c6047a6aaa74c074ba13a349d8c53 (plain)
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
#define _MOZILLA_GFX_SKIACGPOPUPDRAWER_H

#include <ApplicationServices/ApplicationServices.h>
#include "nsDebug.h"
#include "mozilla/Vector.h"
#include "ScaledFontMac.h"
#include "PathCG.h"
#include <dlfcn.h>

// This is used when we explicitly need CG to draw text to support things such
// as vibrancy and subpixel AA on transparent backgrounds. The current use cases
// are really only to enable Skia to support drawing text in those situations.

namespace mozilla {
namespace gfx {

typedef void (*CGContextSetFontSmoothingBackgroundColorFunc) (CGContextRef cgContext, CGColorRef color);

static CGContextSetFontSmoothingBackgroundColorFunc
GetCGContextSetFontSmoothingBackgroundColorFunc()
{
  static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr;
  static bool lookedUpFunc = false;
  if (!lookedUpFunc) {
    func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym(
      RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor");
    lookedUpFunc = true;
  }
  return func;
}

static CGColorRef
ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
{
  CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
  return CGColorCreate(aColorSpace, components);
}

static bool
SetFontSmoothingBackgroundColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace,
                                const GlyphRenderingOptions* aRenderingOptions)
{
  if (aRenderingOptions) {
    Color fontSmoothingBackgroundColor =
      static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
    if (fontSmoothingBackgroundColor.a > 0) {
      CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
        GetCGContextSetFontSmoothingBackgroundColorFunc();
      if (setFontSmoothingBGColorFunc) {
        CGColorRef color = ColorToCGColor(aColorSpace, fontSmoothingBackgroundColor);
        setFontSmoothingBGColorFunc(aCGContext, color);
        CGColorRelease(color);
        return true;
      }
    }
  }

  return false;
}

// Font rendering with a non-transparent font smoothing background color
// can leave pixels in our buffer where the rgb components exceed the alpha
// component. When this happens we need to clean up the data afterwards.
// The purpose of this is probably the following: Correct compositing of
// subpixel anti-aliased fonts on transparent backgrounds requires
// different alpha values per RGB component. Usually, premultiplied color
// values are derived by multiplying all components with the same per-pixel
// alpha value. However, if you multiply each component with a *different*
// alpha, and set the alpha component of the pixel to, say, the average
// of the alpha values that you used during the premultiplication of the
// RGB components, you can trick OVER compositing into doing a simplified
// form of component alpha compositing. (You just need to make sure to
// clamp the components of the result pixel to [0,255] afterwards.)
static void
EnsureValidPremultipliedData(CGContextRef aContext,
                             CGRect aTextBounds = CGRectInfinite)
{
  if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
      CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
    return;
  }

  uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
  CGRect bitmapBounds = CGRectMake(0, 0, CGBitmapContextGetWidth(aContext), CGBitmapContextGetHeight(aContext));
  int stride = CGBitmapContextGetBytesPerRow(aContext);

  CGRect bounds = CGRectIntersection(bitmapBounds, aTextBounds);
  int startX = bounds.origin.x;
  int endX = startX + bounds.size.width;
  MOZ_ASSERT(endX <= bitmapBounds.size.width);


  // CGRect assume that our origin is the bottom left.
  // The data assumes that the origin is the top left.
  // Have to switch the Y axis so that our coordinates are correct
  int startY = bitmapBounds.size.height - (bounds.origin.y + bounds.size.height);
  int endY = startY + bounds.size.height;
  MOZ_ASSERT(endY <= (int)CGBitmapContextGetHeight(aContext));

  for (int y = startY; y < endY; y++) {
    for (int x = startX; x < endX; x++) {
      int i = y * stride + x * 4;
      uint8_t a = bitmapData[i + 3];

      bitmapData[i + 0] = std::min(a, bitmapData[i+0]);
      bitmapData[i + 1] = std::min(a, bitmapData[i+1]);
      bitmapData[i + 2] = std::min(a, bitmapData[i+2]);
    }
  }
}

static CGRect
ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale)
{
  CGFloat x1, x2, y1, y2;
  if (count < 1)
    return CGRectZero;

  x1 = bboxes[0].origin.x + positions[0].x;
  x2 = bboxes[0].origin.x + positions[0].x + scale*bboxes[0].size.width;
  y1 = bboxes[0].origin.y + positions[0].y;
  y2 = bboxes[0].origin.y + positions[0].y + scale*bboxes[0].size.height;

  // accumulate max and minimum coordinates
  for (int i = 1; i < count; i++) {
    x1 = std::min(x1, bboxes[i].origin.x + positions[i].x);
    y1 = std::min(y1, bboxes[i].origin.y + positions[i].y);
    x2 = std::max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width);
    y2 = std::max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height);
  }

  CGRect extents = {{x1, y1}, {x2-x1, y2-y1}};
  return extents;
}

} // namespace gfx
} // namespace mozilla

#endif