gfx/2d/ScaledFontMac.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/2d/ScaledFontMac.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,274 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "ScaledFontMac.h"
    1.10 +#ifdef USE_SKIA
    1.11 +#include "PathSkia.h"
    1.12 +#include "skia/SkPaint.h"
    1.13 +#include "skia/SkPath.h"
    1.14 +#include "skia/SkTypeface_mac.h"
    1.15 +#endif
    1.16 +#include "DrawTargetCG.h"
    1.17 +#include <vector>
    1.18 +#include <dlfcn.h>
    1.19 +
    1.20 +// prototype for private API
    1.21 +extern "C" {
    1.22 +CGPathRef CGFontGetGlyphPath(CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph);
    1.23 +};
    1.24 +
    1.25 +
    1.26 +namespace mozilla {
    1.27 +namespace gfx {
    1.28 +
    1.29 +ScaledFontMac::CTFontDrawGlyphsFuncT* ScaledFontMac::CTFontDrawGlyphsPtr = nullptr;
    1.30 +bool ScaledFontMac::sSymbolLookupDone = false;
    1.31 +
    1.32 +ScaledFontMac::ScaledFontMac(CGFontRef aFont, Float aSize)
    1.33 +  : ScaledFontBase(aSize)
    1.34 +{
    1.35 +  if (!sSymbolLookupDone) {
    1.36 +    CTFontDrawGlyphsPtr =
    1.37 +      (CTFontDrawGlyphsFuncT*)dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
    1.38 +    sSymbolLookupDone = true;
    1.39 +  }
    1.40 +
    1.41 +  // XXX: should we be taking a reference
    1.42 +  mFont = CGFontRetain(aFont);
    1.43 +  if (CTFontDrawGlyphsPtr != nullptr) {
    1.44 +    // only create mCTFont if we're going to be using the CTFontDrawGlyphs API
    1.45 +    mCTFont = CTFontCreateWithGraphicsFont(aFont, aSize, nullptr, nullptr);
    1.46 +  } else {
    1.47 +    mCTFont = nullptr;
    1.48 +  }
    1.49 +}
    1.50 +
    1.51 +ScaledFontMac::~ScaledFontMac()
    1.52 +{
    1.53 +  if (mCTFont) {
    1.54 +    CFRelease(mCTFont);
    1.55 +  }
    1.56 +  CGFontRelease(mFont);
    1.57 +}
    1.58 +
    1.59 +#ifdef USE_SKIA
    1.60 +SkTypeface* ScaledFontMac::GetSkTypeface()
    1.61 +{
    1.62 +  if (!mTypeface) {
    1.63 +    if (mCTFont) {
    1.64 +      mTypeface = SkCreateTypefaceFromCTFont(mCTFont);
    1.65 +    } else {
    1.66 +      CTFontRef fontFace = CTFontCreateWithGraphicsFont(mFont, mSize, nullptr, nullptr);
    1.67 +      mTypeface = SkCreateTypefaceFromCTFont(fontFace);
    1.68 +      CFRelease(fontFace);
    1.69 +    }
    1.70 +  }
    1.71 +  return mTypeface;
    1.72 +}
    1.73 +#endif
    1.74 +
    1.75 +// private API here are the public options on OS X
    1.76 +// CTFontCreatePathForGlyph
    1.77 +// ATSUGlyphGetCubicPaths
    1.78 +// we've used this in cairo sucessfully for some time.
    1.79 +// Note: cairo dlsyms it. We could do that but maybe it's
    1.80 +// safe just to use?
    1.81 +
    1.82 +TemporaryRef<Path>
    1.83 +ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
    1.84 +{
    1.85 +  if (aTarget->GetType() == BackendType::COREGRAPHICS || aTarget->GetType() == BackendType::COREGRAPHICS_ACCELERATED) {
    1.86 +      CGMutablePathRef path = CGPathCreateMutable();
    1.87 +
    1.88 +      for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
    1.89 +          // XXX: we could probably fold both of these transforms together to avoid extra work
    1.90 +          CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
    1.91 +          CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
    1.92 +
    1.93 +          CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize,
    1.94 +                                                           aBuffer.mGlyphs[i].mPosition.x,
    1.95 +                                                           aBuffer.mGlyphs[i].mPosition.y);
    1.96 +          CGPathAddPath(path, &matrix, glyphPath);
    1.97 +          CGPathRelease(glyphPath);
    1.98 +      }
    1.99 +      TemporaryRef<Path> ret = new PathCG(path, FillRule::FILL_WINDING);
   1.100 +      CGPathRelease(path);
   1.101 +      return ret;
   1.102 +  } else {
   1.103 +      return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
   1.104 +  }
   1.105 +}
   1.106 +
   1.107 +void
   1.108 +ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
   1.109 +{
   1.110 +  if (!(aBackendType == BackendType::COREGRAPHICS || aBackendType == BackendType::COREGRAPHICS_ACCELERATED)) {
   1.111 +    ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
   1.112 +    return;
   1.113 +  }
   1.114 +
   1.115 +  PathBuilderCG *pathBuilderCG =
   1.116 +    static_cast<PathBuilderCG*>(aBuilder);
   1.117 +  // XXX: check builder type
   1.118 +  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
   1.119 +    // XXX: we could probably fold both of these transforms together to avoid extra work
   1.120 +    CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
   1.121 +    CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
   1.122 +
   1.123 +    CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize,
   1.124 +                                                     aBuffer.mGlyphs[i].mPosition.x,
   1.125 +                                                     aBuffer.mGlyphs[i].mPosition.y);
   1.126 +    CGPathAddPath(pathBuilderCG->mCGPath, &matrix, glyphPath);
   1.127 +    CGPathRelease(glyphPath);
   1.128 +  }
   1.129 +}
   1.130 +
   1.131 +uint32_t
   1.132 +CalcTableChecksum(const uint32_t *tableStart, uint32_t length, bool skipChecksumAdjust = false)
   1.133 +{
   1.134 +    uint32_t sum = 0L;
   1.135 +    const uint32_t *table = tableStart;
   1.136 +    const uint32_t *end = table+((length+3) & ~3) / sizeof(uint32_t);
   1.137 +    while (table < end) {
   1.138 +        if (skipChecksumAdjust && (table - tableStart) == 2) {
   1.139 +            table++;
   1.140 +        } else {
   1.141 +            sum += CFSwapInt32BigToHost(*table++);
   1.142 +        }
   1.143 +    }
   1.144 +    return sum;
   1.145 +}
   1.146 +
   1.147 +struct TableRecord {
   1.148 +    uint32_t tag;
   1.149 +    uint32_t checkSum;
   1.150 +    uint32_t offset;
   1.151 +    uint32_t length;
   1.152 +    CFDataRef data;
   1.153 +};
   1.154 +
   1.155 +int maxPow2LessThan(int a)
   1.156 +{
   1.157 +    int x = 1;
   1.158 +    int shift = 0;
   1.159 +    while ((x<<(shift+1)) < a) {
   1.160 +        shift++;
   1.161 +    }
   1.162 +    return shift;
   1.163 +}
   1.164 +
   1.165 +struct writeBuf
   1.166 +{
   1.167 +    writeBuf(int size)
   1.168 +    {
   1.169 +        this->data = new unsigned char [size];
   1.170 +        this->offset = 0;
   1.171 +    }
   1.172 +    ~writeBuf() {
   1.173 +        delete this->data;
   1.174 +    }
   1.175 +
   1.176 +    template <class T>
   1.177 +    void writeElement(T a)
   1.178 +    {
   1.179 +        *reinterpret_cast<T*>(&this->data[this->offset]) = a;
   1.180 +        this->offset += sizeof(T);
   1.181 +    }
   1.182 +
   1.183 +    void writeMem(const void *data, unsigned long length)
   1.184 +    {
   1.185 +        memcpy(&this->data[this->offset], data, length);
   1.186 +        this->offset += length;
   1.187 +    }
   1.188 +
   1.189 +    void align()
   1.190 +    {
   1.191 +        while (this->offset & 3) {
   1.192 +            this->data[this->offset] = 0;
   1.193 +            this->offset++;
   1.194 +        }
   1.195 +    }
   1.196 +
   1.197 +    unsigned char *data;
   1.198 +    int offset;
   1.199 +};
   1.200 +
   1.201 +bool
   1.202 +ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
   1.203 +{
   1.204 +    // We'll reconstruct a TTF font from the tables we can get from the CGFont
   1.205 +    CFArrayRef tags = CGFontCopyTableTags(mFont);
   1.206 +    CFIndex count = CFArrayGetCount(tags);
   1.207 +
   1.208 +    TableRecord *records = new TableRecord[count];
   1.209 +    uint32_t offset = 0;
   1.210 +    offset += sizeof(uint32_t)*3;
   1.211 +    offset += sizeof(uint32_t)*4*count;
   1.212 +    bool CFF = false;
   1.213 +    for (CFIndex i = 0; i<count; i++) {
   1.214 +        uint32_t tag = (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags, i);
   1.215 +        if (tag == 0x43464620) // 'CFF '
   1.216 +            CFF = true;
   1.217 +        CFDataRef data = CGFontCopyTableForTag(mFont, tag);
   1.218 +        records[i].tag = tag;
   1.219 +        records[i].offset = offset;
   1.220 +        records[i].data = data;
   1.221 +        records[i].length = CFDataGetLength(data);
   1.222 +        bool skipChecksumAdjust = (tag == 0x68656164); // 'head'
   1.223 +        records[i].checkSum = CalcTableChecksum(reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data)),
   1.224 +                                                records[i].length, skipChecksumAdjust);
   1.225 +        offset += records[i].length;
   1.226 +        // 32 bit align the tables
   1.227 +        offset = (offset + 3) & ~3;
   1.228 +    }
   1.229 +    CFRelease(tags);
   1.230 +
   1.231 +    struct writeBuf buf(offset);
   1.232 +    // write header/offset table
   1.233 +    if (CFF) {
   1.234 +      buf.writeElement(CFSwapInt32HostToBig(0x4f54544f));
   1.235 +    } else {
   1.236 +      buf.writeElement(CFSwapInt32HostToBig(0x00010000));
   1.237 +    }
   1.238 +    buf.writeElement(CFSwapInt16HostToBig(count));
   1.239 +    buf.writeElement(CFSwapInt16HostToBig((1<<maxPow2LessThan(count))*16));
   1.240 +    buf.writeElement(CFSwapInt16HostToBig(maxPow2LessThan(count)));
   1.241 +    buf.writeElement(CFSwapInt16HostToBig(count*16-((1<<maxPow2LessThan(count))*16)));
   1.242 +
   1.243 +    // write table record entries
   1.244 +    for (CFIndex i = 0; i<count; i++) {
   1.245 +        buf.writeElement(CFSwapInt32HostToBig(records[i].tag));
   1.246 +        buf.writeElement(CFSwapInt32HostToBig(records[i].checkSum));
   1.247 +        buf.writeElement(CFSwapInt32HostToBig(records[i].offset));
   1.248 +        buf.writeElement(CFSwapInt32HostToBig(records[i].length));
   1.249 +    }
   1.250 +
   1.251 +    // write tables
   1.252 +    int checkSumAdjustmentOffset = 0;
   1.253 +    for (CFIndex i = 0; i<count; i++) {
   1.254 +        if (records[i].tag == 0x68656164) {
   1.255 +            checkSumAdjustmentOffset = buf.offset + 2*4;
   1.256 +        }
   1.257 +        buf.writeMem(CFDataGetBytePtr(records[i].data), CFDataGetLength(records[i].data));
   1.258 +        buf.align();
   1.259 +        CFRelease(records[i].data);
   1.260 +    }
   1.261 +    delete[] records;
   1.262 +
   1.263 +    // clear the checksumAdjust field before checksumming the whole font
   1.264 +    memset(&buf.data[checkSumAdjustmentOffset], 0, sizeof(uint32_t));
   1.265 +    uint32_t fontChecksum = CFSwapInt32HostToBig(0xb1b0afba - CalcTableChecksum(reinterpret_cast<const uint32_t*>(buf.data), offset));
   1.266 +    // set checkSumAdjust to the computed checksum
   1.267 +    memcpy(&buf.data[checkSumAdjustmentOffset], &fontChecksum, sizeof(fontChecksum));
   1.268 +
   1.269 +    // we always use an index of 0
   1.270 +    aDataCallback(buf.data, buf.offset, 0, mSize, aBaton);
   1.271 +
   1.272 +    return true;
   1.273 +
   1.274 +}
   1.275 +
   1.276 +}
   1.277 +}

mercurial