diff options
Diffstat (limited to 'Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/charstring-il.c')
-rw-r--r-- | Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/charstring-il.c | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/charstring-il.c b/Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/charstring-il.c new file mode 100644 index 00000000000..d73f4dd57b5 --- /dev/null +++ b/Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/charstring-il.c @@ -0,0 +1,518 @@ +#include "charstring-il.h" +#include "table/glyf.h" + +// Glyph building +static void ensureThereIsSpace(cff_CharstringIL *il) { + if (il->free) return; + il->free = 0x100; + RESIZE(il->instr, il->length + il->free); +} + +void il_push_operand(cff_CharstringIL *il, double x) { + ensureThereIsSpace(il); + il->instr[il->length].type = IL_ITEM_OPERAND; + il->instr[il->length].d = x; + il->instr[il->length].arity = 0; + il->length++; + il->free--; +} +static void il_push_VQ(cff_CharstringIL *il, VQ x) { + il_push_operand(il, iVQ.getStill(x)); +} +void il_push_special(cff_CharstringIL *il, int32_t s) { + ensureThereIsSpace(il); + il->instr[il->length].type = IL_ITEM_SPECIAL; + il->instr[il->length].i = s; + il->instr[il->length].arity = 0; + il->length++; + il->free--; +} +void il_push_op(cff_CharstringIL *il, int32_t op) { + ensureThereIsSpace(il); + il->instr[il->length].type = IL_ITEM_OPERATOR; + il->instr[il->length].i = op; + il->instr[il->length].arity = cff_getStandardArity(op); + il->length++; + il->free--; +} +static void il_moveto(cff_CharstringIL *il, VQ dx, VQ dy) { + il_push_VQ(il, dx); + il_push_VQ(il, dy); + il_push_op(il, op_rmoveto); +} +static void il_lineto(cff_CharstringIL *il, VQ dx, VQ dy) { + il_push_VQ(il, dx); + il_push_VQ(il, dy); + il_push_op(il, op_rlineto); +} +static void il_curveto(cff_CharstringIL *il, VQ dx1, VQ dy1, VQ dx2, VQ dy2, VQ dx3, VQ dy3) { + il_push_VQ(il, dx1); + il_push_VQ(il, dy1); + il_push_VQ(il, dx2); + il_push_VQ(il, dy2); + il_push_VQ(il, dx3); + il_push_VQ(il, dy3); + il_push_op(il, op_rrcurveto); +} + +static void _il_push_maskgroup(cff_CharstringIL *il, // il seq + glyf_MaskList *masks, // masks array + uint16_t contours, // contous drawn + uint16_t points, // points drawn + uint16_t nh, uint16_t nv, // quantity of stems + uint16_t *jm, // index of cur mask + int32_t op) { // mask operator + shapeid_t n = masks->length; + while (*jm < n && (masks->items[*jm].contoursBefore < contours || + (masks->items[*jm].contoursBefore == contours && + masks->items[*jm].pointsBefore <= points))) { + il_push_op(il, op); + uint8_t maskByte = 0; + uint8_t bits = 0; + for (uint16_t j = 0; j < nh; j++) { + maskByte = maskByte << 1 | (masks->items[*jm].maskH[j] & 1); + bits += 1; + if (bits == 8) { + il_push_special(il, maskByte); + bits = 0; + } + } + for (uint16_t j = 0; j < nv; j++) { + maskByte = maskByte << 1 | (masks->items[*jm].maskV[j] & 1); + bits += 1; + if (bits == 8) { + il_push_special(il, maskByte); + bits = 0; + } + } + if (bits) { + maskByte = maskByte << (8 - bits); + il_push_special(il, maskByte); + } + *jm += 1; + } +} +static void il_push_masks(cff_CharstringIL *il, glyf_Glyph *g, // meta + uint16_t contours, // contours sofar + uint16_t points, // points sofar + uint16_t *jh, // index of pushed cmasks + uint16_t *jm // index of pushed hmasks +) { + if (!g->stemH.length && !g->stemV.length) return; + _il_push_maskgroup(il, &g->contourMasks, contours, points, // + g->stemH.length, g->stemV.length, jh, op_cntrmask); + _il_push_maskgroup(il, &g->hintMasks, contours, points, // + g->stemH.length, g->stemV.length, jm, op_hintmask); +} + +static void _il_push_stemgroup(cff_CharstringIL *il, // il seq + glyf_StemDefList *stems, // stem array + bool hasmask, bool haswidth, int32_t ophm, int32_t oph) { + if (!stems || !stems->length) return; + pos_t ref = 0; + uint16_t nn = haswidth ? 1 : 0; + for (uint16_t j = 0; j < stems->length; j++) { + il_push_operand(il, stems->items[j].position - ref); + il_push_operand(il, stems->items[j].width); + ref = stems->items[j].position + stems->items[j].width; + nn++; + if (nn >= type2_argument_stack) { + if (hasmask) { + il_push_op(il, op_hstemhm); + } else { + il_push_op(il, op_hstem); + } + il->instr[il->length - 1].arity = nn; + nn = 0; + } + } + if (hasmask) { + il_push_op(il, ophm); + } else { + il_push_op(il, oph); + } + il->instr[il->length - 1].arity = nn; +} +static void il_push_stems(cff_CharstringIL *il, glyf_Glyph *g, bool hasmask, bool haswidth) { + _il_push_stemgroup(il, &g->stemH, hasmask, haswidth, op_hstemhm, op_hstem); + _il_push_stemgroup(il, &g->stemV, hasmask, haswidth, op_vstemhm, op_vstem); +} +cff_CharstringIL *cff_compileGlyphToIL(glyf_Glyph *g, uint16_t defaultWidth, + uint16_t nominalWidth) { + cff_CharstringIL *il; + NEW(il); + // Convert absolute positions to deltas + glyf_Contour *tempContours = NULL; + { + VQ x = iVQ.neutral(); + VQ y = iVQ.neutral(); + NEW(tempContours, g->contours.length); + for (uint16_t c = 0; c < g->contours.length; c++) { + glyf_Contour *contour = &(g->contours.items[c]); + glyf_Contour *newcontour = &(tempContours[c]); + glyf_iContour.init(newcontour); + for (shapeid_t j = 0; j < contour->length; j++) { + glyf_iContour.push(newcontour, glyf_iPoint.dup(contour->items[j])); + } + + if (newcontour->length > 2 && !newcontour->items[newcontour->length - 1].onCurve) { + // Duplicate first point for proper CurveTo generation + glyf_iContour.push(newcontour, glyf_iPoint.dup(newcontour->items[0])); + } + + for (shapeid_t j = 0; j < newcontour->length; j++) { + VQ dx = iVQ.minus(newcontour->items[j].x, x); + VQ dy = iVQ.minus(newcontour->items[j].y, y); + iVQ.copyReplace(&x, newcontour->items[j].x); + iVQ.copyReplace(&y, newcontour->items[j].y); + iVQ.replace(&newcontour->items[j].x, dx); + iVQ.replace(&newcontour->items[j].y, dy); + } + } + iVQ.dispose(&x); + iVQ.dispose(&y); + } + + bool hasmask = + g->hintMasks.length || g->contourMasks.length; // we have hint masks or contour masks + const pos_t glyphADWConst = iVQ.getStill(g->advanceWidth); + bool haswidth = glyphADWConst != defaultWidth; // we have width operand here + // Write IL + if (haswidth) { il_push_operand(il, (int)(glyphADWConst) - (int)(nominalWidth)); } + il_push_stems(il, g, hasmask, haswidth); + // Write contour + shapeid_t contoursSofar = 0; + shapeid_t pointsSofar = 0; + shapeid_t jh = 0; + shapeid_t jm = 0; + if (hasmask) il_push_masks(il, g, contoursSofar, pointsSofar, &jh, &jm); + for (shapeid_t c = 0; c < g->contours.length; c++) { + glyf_Contour *contour = &(tempContours[c]); + shapeid_t n = contour->length; + if (n == 0) continue; + il_moveto(il, contour->items[0].x, contour->items[0].y); + pointsSofar++; + if (hasmask) il_push_masks(il, g, contoursSofar, pointsSofar, &jh, &jm); + // TODO: Generate BLENDs + for (shapeid_t j = 1; j < n; j++) { + if (contour->items[j].onCurve) { // A line-to + il_lineto(il, contour->items[j].x, contour->items[j].y); + pointsSofar += 1; + } else if (j < n - 2 // have enough points + && !contour->items[j + 1].onCurve // next is offcurve + && contour->items[j + 2].onCurve // and next is oncurve + ) { // means this is an bezier curve strand + il_curveto(il, contour->items[j].x, + contour->items[j].y, // dz1 + contour->items[j + 1].x, + contour->items[j + 1].y, // dz2 + contour->items[j + 2].x, + contour->items[j + 2].y); // dz3 + pointsSofar += 3; + j += 2; + } else { // invalid offcurve, treat as oncurve + il_lineto(il, contour->items[j].x, contour->items[j].y); + pointsSofar++; + } + if (hasmask) il_push_masks(il, g, contoursSofar, pointsSofar, &jh, &jm); + } + contoursSofar += 1; + pointsSofar = 0; + } + il_push_op(il, op_endchar); + // delete temp contour array + for (shapeid_t c = 0; c < g->contours.length; c++) { + glyf_iContour.dispose(&tempContours[c]); + } + FREE(tempContours); + return il; +} + +// Pattern-based peephole optimization +static bool il_matchtype(cff_CharstringIL *il, uint32_t j, uint32_t k, cff_InstructionType t) { + if (k >= il->length) return false; + for (uint32_t m = j; m < k; m++) { + if (il->instr[m].type != t) return false; + } + return true; +} +static bool il_matchop(cff_CharstringIL *il, uint32_t j, int32_t op) { + if (il->instr[j].type != IL_ITEM_OPERATOR) return false; + if (il->instr[j].i != op) return false; + return true; +} +static uint8_t zroll(cff_CharstringIL *il, uint32_t j, int32_t op, int32_t op2, ...) { + uint8_t arity = cff_getStandardArity(op); + if (arity > 16 || j + arity >= il->length) return 0; + if ((j == 0 || // We are at the beginning of charstring + !il_matchtype(il, j - 1, j, + IL_ITEM_PHANTOM_OPERATOR)) // .. or we are right after a solid operator + && il_matchop(il, j + arity, op) // The next operator is <op> + && il_matchtype(il, j, j + arity, IL_ITEM_OPERAND) // And we have correct number of operands + ) { + va_list ap; + uint8_t check = true; + uint8_t resultArity = arity; + bool mask[16]; + va_start(ap, op2); + for (uint32_t m = 0; m < arity; m++) { + int checkzero = va_arg(ap, int); + mask[m] = checkzero; + if (checkzero) { + resultArity -= 1; + check = check && il->instr[j + m].d == 0; + } + } + va_end(ap); + if (check) { + for (uint32_t m = 0; m < arity; m++) { + if (mask[m]) { il->instr[j + m].type = IL_ITEM_PHANTOM_OPERAND; } + } + il->instr[j + arity].i = op2; + il->instr[j + arity].arity = resultArity; + return arity; + } else { + return 0; + } + } else { + return 0; + } +} +static uint8_t opop_roll(cff_CharstringIL *il, uint32_t j, int32_t op1, int32_t arity, int32_t op2, + int32_t resultop) { + if (j + 1 + arity >= il->length) return 0; + cff_CharstringInstruction *current = &(il->instr[j]); + cff_CharstringInstruction *nextop = &(il->instr[j + 1 + arity]); + if (il_matchop(il, j, op1) // match this operator + && il_matchtype(il, j + 1, j + 1 + arity, IL_ITEM_OPERAND) // match operands + && il_matchop(il, j + 1 + arity, op2) // match next operator + && current->arity + nextop->arity <= type2_argument_stack // stack is not full + ) { + current->type = IL_ITEM_PHANTOM_OPERATOR; + nextop->i = resultop; + nextop->arity += current->arity; + return arity + 1; + } else { + return 0; + } +} +static uint8_t hvlineto_roll(cff_CharstringIL *il, uint32_t j) { + if (j + 3 >= il->length) return 0; + cff_CharstringInstruction *current = &(il->instr[j]); + // We will check whether operand <checkdelta> is zero + // ODD EVEN -- current arity + // hlineto X Y + // vlineto Y X + uint32_t checkdelta = ((bool)(current->arity & 1) ^ (bool)(current->i == op_vlineto) ? 1 : 2); + if ((il_matchop(il, j, op_hlineto) || il_matchop(il, j, op_vlineto)) // a hlineto/vlineto + && il_matchop(il, j + 3, op_rlineto) // followed by a lineto + && il_matchtype(il, j + 1, j + 3, IL_ITEM_OPERAND) // have enough operands + && il->instr[j + checkdelta].d == 0 // and it is a h/v + && current->arity + 1 <= type2_argument_stack // we have enough stack space + ) { + il->instr[j + checkdelta].type = IL_ITEM_PHANTOM_OPERAND; + il->instr[j].type = IL_ITEM_PHANTOM_OPERATOR; + il->instr[j + 3].i = current->i; + il->instr[j + 3].arity = current->arity + 1; + return 3; + } else { + return 0; + } +} +static uint8_t hvvhcurve_roll(cff_CharstringIL *il, uint32_t j) { + if (!il_matchop(il, j, op_hvcurveto) && !il_matchop(il, j, op_vhcurveto)) return 0; + cff_CharstringInstruction *current = &(il->instr[j]); + // Exit in case of array not long enough or we have already ended + if (j + 7 >= il->length || current->arity & 1) return 0; + bool hvcase = (bool)((current->arity >> 2) & 1) ^ (bool)(current->i == op_hvcurveto); + // We will check whether operand <checkdelta> is zero + // ODD EVEN -- current arity divided by 4 + // hvcurveto X Y + // vhcurveto Y X + uint32_t checkdelta1 = hvcase ? 2 : 1; + uint32_t checkdelta2 = hvcase ? 5 : 6; + if (il_matchop(il, j + 7, op_rrcurveto) // followed by a curveto + && il_matchtype(il, j + 1, j + 7, IL_ITEM_OPERAND) // have enough operands + && il->instr[j + checkdelta1].d == 0 // and it is a h/v + ) { + if (il->instr[j + checkdelta2].d == 0 && current->arity + 4 <= type2_argument_stack) { + // The Standard case + il->instr[j + checkdelta1].type = IL_ITEM_PHANTOM_OPERAND; + il->instr[j + checkdelta2].type = IL_ITEM_PHANTOM_OPERAND; + il->instr[j].type = IL_ITEM_PHANTOM_OPERATOR; + il->instr[j + 7].i = current->i; + il->instr[j + 7].arity = current->arity + 4; + return 7; + } else if (current->arity + 5 <= type2_argument_stack) { + // The trailing case + il->instr[j + checkdelta1].type = IL_ITEM_PHANTOM_OPERAND; + il->instr[j].type = IL_ITEM_PHANTOM_OPERATOR; + il->instr[j + 7].i = current->i; + il->instr[j + 7].arity = current->arity + 5; + if (hvcase) { + // Swap the last two operands because hvcurveto's trailing operand is in y-x order + double t = il->instr[j + 5].d; + il->instr[j + 5].d = il->instr[j + 6].d; + il->instr[j + 6].d = t; + } + return 7; + } else { + return 0; + } + } else { + return 0; + } +} +static uint8_t hhvvcurve_roll(cff_CharstringIL *il, uint32_t j) { + if (!il_matchop(il, j, op_hhcurveto) && !il_matchop(il, j, op_vvcurveto)) return 0; + cff_CharstringInstruction *current = &(il->instr[j]); + // Exit in case of array not long enough or we have already ended + if (j + 7 >= il->length) return 0; + bool hh = current->i == op_hhcurveto; + uint32_t checkdelta1 = hh ? 2 : 1; + uint32_t checkdelta2 = hh ? 6 : 5; + if (il_matchop(il, j + 7, op_rrcurveto) // followed by a curveto + && il_matchtype(il, j + 1, j + 7, IL_ITEM_OPERAND) // have enough operands + && il->instr[j + checkdelta1].d == 0 // and it is a h/v + && il->instr[j + checkdelta2].d == 0 // and it is a h/v + && current->arity + 4 <= type2_argument_stack) { + il->instr[j + checkdelta1].type = IL_ITEM_PHANTOM_OPERAND; + il->instr[j + checkdelta2].type = IL_ITEM_PHANTOM_OPERAND; + il->instr[j].type = IL_ITEM_PHANTOM_OPERATOR; + il->instr[j + 7].i = current->i; + il->instr[j + 7].arity = current->arity + 4; + return 7; + } else { + return 0; + } +} +static uint32_t nextstop(cff_CharstringIL *il, uint32_t j) { + uint32_t delta = 0; + for (; j + delta < il->length && il->instr[j + delta].type == IL_ITEM_OPERAND; delta++) + ; + return delta; +} +#define ROLL_FALL(x) \ + if ((r = (x))) return r; +static uint8_t decideAdvance(cff_CharstringIL *il, uint32_t j, uint8_t optimizeLevel) { + uint8_t r = 0; + ROLL_FALL(zroll(il, j, op_rlineto, op_hlineto, 0, 1)); // rlineto -> hlineto + ROLL_FALL(zroll(il, j, op_rlineto, op_vlineto, 1, 0)); // rlineto -> vlineto + ROLL_FALL(zroll(il, j, op_rmoveto, op_hmoveto, 0, 1)); // rmoveto -> hmoveto + ROLL_FALL(zroll(il, j, op_rmoveto, op_vmoveto, 1, 0)); // rmoveto -> vmoveto + ROLL_FALL(zroll(il, j, op_rrcurveto, op_hvcurveto, 0, 1, 0, 0, 1, 0)); // rrcurveto->hvcurveto + ROLL_FALL(zroll(il, j, op_rrcurveto, op_vhcurveto, 1, 0, 0, 0, 0, 1)); // rrcurveto->vhcurveto + ROLL_FALL(zroll(il, j, op_rrcurveto, op_hhcurveto, 0, 1, 0, 0, 0, 1)); // rrcurveto->hhcurveto + ROLL_FALL(zroll(il, j, op_rrcurveto, op_vvcurveto, 1, 0, 0, 0, 1, 0)); // rrcurveto->vvcurveto + ROLL_FALL(opop_roll(il, j, op_rrcurveto, 6, op_rrcurveto, op_rrcurveto)); // rrcurveto roll + ROLL_FALL(opop_roll(il, j, op_rrcurveto, 2, op_rlineto, op_rcurveline)); // rcurveline roll + ROLL_FALL(opop_roll(il, j, op_rlineto, 6, op_rrcurveto, op_rlinecurve)); // rlinecurve roll + ROLL_FALL(opop_roll(il, j, op_rlineto, 2, op_rlineto, op_rlineto)); // rlineto roll + ROLL_FALL(opop_roll(il, j, op_hstemhm, 0, op_hintmask, op_hintmask)); // hintmask roll + ROLL_FALL(opop_roll(il, j, op_vstemhm, 0, op_hintmask, op_hintmask)); // hintmask roll + ROLL_FALL(opop_roll(il, j, op_hstemhm, 0, op_cntrmask, op_cntrmask)); // cntrmask roll + ROLL_FALL(opop_roll(il, j, op_vstemhm, 0, op_cntrmask, op_cntrmask)); // cntrmask roll + ROLL_FALL(hvlineto_roll(il, j)); // hlineto-vlineto roll + ROLL_FALL(hhvvcurve_roll(il, j)); // hhcurveto-vvcurveto roll + ROLL_FALL(hvvhcurve_roll(il, j)); // hvcurveto-vhcurveto roll + ROLL_FALL(nextstop(il, j)); // move to next stop for operand match + return 1; // nothing match +} + +void cff_optimizeIL(cff_CharstringIL *il, const otfcc_Options *options) { + if (!options->cff_rollCharString) return; + uint32_t j = 0; + while (j < il->length) { + j += decideAdvance(il, j, options->cff_rollCharString); + } +} + +// IL to buffer conversion +caryll_Buffer *cff_build_IL(cff_CharstringIL *il) { + caryll_Buffer *blob = bufnew(); + + for (uint16_t j = 0; j < il->length; j++) { + switch (il->instr[j].type) { + case IL_ITEM_OPERAND: { + cff_mergeCS2Operand(blob, il->instr[j].d); + break; + } + case IL_ITEM_OPERATOR: { + cff_mergeCS2Operator(blob, il->instr[j].i); + break; + } + case IL_ITEM_SPECIAL: { + cff_mergeCS2Special(blob, il->instr[j].i); + break; + } + default: + break; + } + } + return blob; +} + +cff_CharstringIL *cff_shrinkIL(cff_CharstringIL *il) { + cff_CharstringIL *out; + NEW(out); + for (uint16_t j = 0; j < il->length; j++) { + switch (il->instr[j].type) { + case IL_ITEM_OPERAND: { + il_push_operand(out, il->instr[j].d); + break; + } + case IL_ITEM_OPERATOR: { + il_push_op(out, il->instr[j].i); + break; + } + case IL_ITEM_SPECIAL: { + il_push_special(out, il->instr[j].i); + break; + } + default: + break; + } + } + return out; +} + +void cff_ILmergeIL(cff_CharstringIL *self, cff_CharstringIL *il) { + for (uint16_t j = 0; j < il->length; j++) { + switch (il->instr[j].type) { + case IL_ITEM_OPERAND: { + il_push_operand(self, il->instr[j].d); + break; + } + case IL_ITEM_OPERATOR: { + il_push_op(self, il->instr[j].i); + break; + } + case IL_ITEM_SPECIAL: { + il_push_special(self, il->instr[j].i); + break; + } + default: + break; + } + } +} + +bool instruction_eq(cff_CharstringInstruction *z1, cff_CharstringInstruction *z2) { + if (z1->type == z2->type) { + if (z1->type == IL_ITEM_OPERAND || z1->type == IL_ITEM_PHANTOM_OPERAND) { + return z1->d == z2->d; + } else { + return z1->i == z2->i; + } + } else { + return false; + } +} + +bool cff_ilEqual(cff_CharstringIL *a, cff_CharstringIL *b) { + if (!a || !b) return false; + if (a->length != b->length) return false; + for (uint32_t j = 0; j < a->length; j++) + if (!instruction_eq(a->instr + j, b->instr + j)) { return false; } + return true; +} |