| |
| |
| #include "trop.h" |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| namespace { |
| template <typename PIX> |
| class PixelSelector { |
| public: |
| typedef PIX pixel_type; |
| |
| private: |
| int m_thresh; |
| |
| public: |
| PixelSelector(int thresh) : m_thresh(thresh) {} |
| |
| bool areEqual(const PIX &a, const PIX &b) const { |
| return std::max({abs((int)a.r - b.r), abs((int)a.g - b.g), |
| abs((int)a.b - b.b), abs((int)a.m - b.m)}) < m_thresh; |
| } |
| }; |
| |
| |
| |
| template <> |
| class PixelSelector<TPixelCM32> { |
| public: |
| typedef TPixelCM32 pixel_type; |
| |
| private: |
| int m_thresh; |
| |
| public: |
| PixelSelector(int thresh) : m_thresh(thresh) {} |
| |
| bool areEqual(const TPixelCM32 &a, const TPixelCM32 &b) const { |
| return (a.getInk() == b.getInk()) && |
| (abs(a.getTone() - b.getTone()) < m_thresh); |
| } |
| }; |
| |
| |
| |
| template <typename PIX> |
| inline void weightPix(PIX *out, const PIX *a, const PIX *b, double weightA, |
| double weightB) { |
| out->r = a->r * weightA + b->r * weightB; |
| out->g = a->g * weightA + b->g * weightB; |
| out->b = a->b * weightA + b->b * weightB; |
| out->m = a->m * weightA + b->m * weightB; |
| } |
| |
| |
| |
| template <> |
| inline void weightPix<TPixelCM32>(TPixelCM32 *out, const TPixelCM32 *a, |
| const TPixelCM32 *b, double weightA, |
| double weightB) { |
| *out = |
| TPixelCM32(out->isPurePaint() ? b->getInk() : a->getInk(), a->getPaint(), |
| a->getTone() * weightA + b->getTone() * weightB); |
| } |
| |
| |
| |
| |
| template <typename PIX, typename SELECTOR> |
| inline bool checkNeighbourHood(int x, int y, PIX *pix, int lx, int ly, int dx, |
| int dy, const SELECTOR &sel) { |
| int count1 = 0, count2 = 0; |
| int dx2 = 2 * dx, dy2 = 2 * dy; |
| if (y > 1) { |
| |
| count1 += (int)sel.areEqual(*(pix - dx), *(pix - dy2)) + |
| (int)sel.areEqual(*(pix - dx), *(pix - dy2 - dx)); |
| count2 += (int)sel.areEqual(*pix, *(pix - dy2)) + |
| (int)sel.areEqual(*pix, *(pix - dy2 - dx)); |
| } |
| if (y < ly - 1) { |
| |
| count1 += (int)sel.areEqual(*(pix - dx), *(pix + dy)) + |
| (int)sel.areEqual(*(pix - dx), *(pix + dy - dx)); |
| count2 += (int)sel.areEqual(*pix, *(pix + dy)) + |
| (int)sel.areEqual(*pix, *(pix + dy - dx)); |
| } |
| if (x > 1) { |
| |
| count1 += (int)sel.areEqual(*(pix - dx), *(pix - dx2)) + |
| (int)sel.areEqual(*(pix - dx), *(pix - dx2 - dy)); |
| count2 += (int)sel.areEqual(*pix, *(pix - dx2)) + |
| (int)sel.areEqual(*pix, *(pix - dx2 - dy)); |
| } |
| if (x < lx - 1) { |
| |
| count1 += (int)sel.areEqual(*(pix - dx), *(pix + dx)) + |
| (int)sel.areEqual(*(pix - dx), *(pix + dx - dy)); |
| count2 += (int)sel.areEqual(*pix, *(pix + dx)) + |
| (int)sel.areEqual(*pix, *(pix + dx - dy)); |
| } |
| |
| |
| |
| |
| return count1 > count2; |
| } |
| } |
| |
| |
| |
| template <typename PIX> |
| inline void filterLine(PIX *inLPix, PIX *inUPix, PIX *outLPix, PIX *outUPix, |
| int ll, int inDl, int outLDl, int outUDl, double hStart, |
| double slope, bool filterLower) { |
| assert(hStart >= 0.0 && slope > 0.0); |
| |
| double h0 = hStart, h1, area; |
| double base = hStart / slope; |
| |
| int i, end = std::min(tfloor(base), ll); |
| if (filterLower) { |
| |
| for (i = 0; i < end; |
| ++i, h0 = h1, inLPix += inDl, inUPix += inDl, outLPix += outLDl) { |
| h1 = h0 - slope; |
| area = 0.5 * (h0 + h1); |
| weightPix(outLPix, outLPix, inUPix, 1.0 - area, area); |
| } |
| |
| if (i < ll) { |
| double remnant = base - end; |
| area = 0.5 * remnant * h0; |
| weightPix(outLPix, outLPix, inUPix, 1.0 - area, area); |
| } |
| } else { |
| |
| for (i = 0; i < end; |
| ++i, h0 = h1, inLPix += inDl, inUPix += inDl, outUPix += outUDl) { |
| h1 = h0 - slope; |
| area = 0.5 * (h0 + h1); |
| weightPix(outUPix, outUPix, inLPix, 1.0 - area, area); |
| } |
| |
| if (i < ll) { |
| double remnant = base - end; |
| area = 0.5 * remnant * h0; |
| weightPix(outUPix, outUPix, inLPix, 1.0 - area, area); |
| } |
| } |
| } |
| |
| |
| |
| template <typename PIX, typename SELECTOR> |
| inline bool checkLength(int lLine, int y, int ly, int dy, PIX *pixL1, |
| PIX *pixU1, PIX *pixL2, PIX *pixU2, bool uniteU, |
| bool do1Line, const SELECTOR &sel) { |
| |
| |
| |
| |
| return (lLine > 1) || |
| (do1Line && ((uniteU && (y > 1 && |
| !(sel.areEqual(*pixL1, *(pixL1 - dy)) && |
| sel.areEqual(*pixL2, *(pixL2 - dy))))) || |
| (y < ly - 1 && |
| !(sel.areEqual(*pixU1, *(pixU1 + dy)) && |
| sel.areEqual(*pixU2, *(pixU2 + dy)))))); |
| } |
| |
| |
| |
| template <typename PIX, typename SELECTOR> |
| void processLine(int r, int lx, int ly, PIX *inLRow, PIX *inURow, PIX *outLRow, |
| PIX *outURow, int inDx, int inDy, int outLDx, int outUDx, |
| bool do1Line, double hStart, double slope, |
| const SELECTOR &sel) { |
| |
| ++r; |
| |
| |
| |
| |
| PIX *inLL = inLRow, *inLR, *inUL = inURow, *inUR; |
| PIX *inLL_1, *inUL_1, *inLR_1, *inUR_1; |
| PIX *inLEnd = inLRow + lx * inDx; |
| int x, lLine; |
| bool uniteLL, uniteUL, uniteLR, uniteUR; |
| |
| |
| if (!sel.areEqual(*inLL, *inUL)) { |
| |
| for (inLR = inLL + inDx, inUR = inUL + inDx; |
| inLR != inLEnd && sel.areEqual(*inLL, *inLR) && |
| sel.areEqual(*inUL, *inUR); |
| inLR += inDx, inUR += inDx) |
| ; |
| |
| if (inLR != inLEnd) { |
| |
| lLine = (inLR - inLL) / inDx; |
| |
| inLR_1 = inLR - inDx, inUR_1 = inUR - inDx; |
| x = (inLR_1 - inLRow) / inDx; |
| |
| uniteUR = sel.areEqual(*inUR_1, *inLR); |
| uniteLR = sel.areEqual(*inLR_1, *inUR); |
| if (uniteUR || uniteLR) { |
| if (uniteUR && uniteLR) |
| |
| |
| uniteUR = |
| !checkNeighbourHood(x + 1, r, inUR, lx, ly, inDx, inDy, sel); |
| |
| if (checkLength(lLine, r, ly, inDy, inLR_1, inUR_1, inLR, inUR, uniteUR, |
| do1Line, sel)) |
| filterLine(inLR_1, inUR_1, outLRow + x * outLDx, outURow + x * outUDx, |
| lLine, -inDx, -outLDx, -outUDx, hStart, |
| slope / (lLine << 1), uniteUR); |
| } |
| } |
| |
| |
| inLL = inLR, inUL = inUR; |
| } |
| |
| |
| for (; inLL != inLEnd && sel.areEqual(*inLL, *inUL); |
| inLL += inDx, inUL += inDx) |
| ; |
| |
| while (inLL != inLEnd) { |
| |
| for (inLR = inLL + inDx, inUR = inUL + inDx; |
| inLR != inLEnd && sel.areEqual(*inLL, *inLR) && |
| sel.areEqual(*inUL, *inUR); |
| inLR += inDx, inUR += inDx) |
| ; |
| |
| if (inLR == inLEnd) break; |
| |
| |
| lLine = (inLR - inLL) / inDx; |
| |
| |
| inLL_1 = inLL - inDx, inUL_1 = inUL - inDx; |
| x = (inLL - inLRow) / inDx; |
| |
| uniteUL = sel.areEqual(*inUL, *inLL_1); |
| uniteLL = sel.areEqual(*inLL, *inUL_1); |
| if (uniteUL || uniteLL) { |
| if (uniteUL && uniteLL) |
| uniteUL = checkNeighbourHood(x, r, inUL, lx, ly, inDx, inDy, sel); |
| |
| if (checkLength(lLine, r, ly, inDy, inLL_1, inUL_1, inLL, inUL, uniteUL, |
| do1Line, sel)) |
| filterLine(inLL, inUL, outLRow + x * outLDx, outURow + x * outUDx, |
| lLine, inDx, outLDx, outUDx, hStart, slope / lLine, uniteUL); |
| } |
| |
| |
| inLR_1 = inLR - inDx, inUR_1 = inUR - inDx; |
| x = (inLR_1 - inLRow) / inDx; |
| |
| uniteUR = sel.areEqual(*inUR_1, *inLR); |
| uniteLR = sel.areEqual(*inLR_1, *inUR); |
| if (uniteUR || uniteLR) { |
| if (uniteUR && uniteLR) |
| uniteUR = !checkNeighbourHood(x + 1, r, inUR, lx, ly, inDx, inDy, sel); |
| |
| if (checkLength(lLine, r, ly, inDy, inLR_1, inUR_1, inLR, inUR, uniteUR, |
| do1Line, sel)) |
| filterLine(inLR_1, inUR_1, outLRow + x * outLDx, outURow + x * outUDx, |
| lLine, -inDx, -outLDx, -outUDx, hStart, slope / lLine, |
| uniteUR); |
| } |
| |
| |
| inLL = inLR, inUL = inUR; |
| for (; inLL != inLEnd && sel.areEqual(*inLL, *inUL); |
| inLL += inDx, inUL += inDx) |
| ; |
| } |
| |
| |
| if (inLL != inLEnd) { |
| |
| lLine = (inLR - inLL) / inDx; |
| |
| inLL_1 = inLL - inDx, inUL_1 = inUL - inDx; |
| x = (inLL - inLRow) / inDx; |
| |
| uniteUL = sel.areEqual(*inUL, *inLL_1); |
| uniteLL = sel.areEqual(*inLL, *inUL_1); |
| if (uniteUL || uniteLL) { |
| if (uniteUL && uniteLL) |
| uniteUL = checkNeighbourHood(x, r, inUL, lx, ly, inDx, inDy, sel); |
| |
| if (checkLength(lLine, r, ly, inDy, inLL_1, inUL_1, inLL, inUL, uniteUL, |
| do1Line, sel)) |
| filterLine(inLL, inUL, outLRow + x * outLDx, outURow + x * outUDx, |
| lLine, inDx, outLDx, outUDx, hStart, slope / (lLine << 1), |
| uniteUL); |
| } |
| } |
| } |
| |
| |
| |
| template <typename PIX> |
| void makeAntialias(const TRasterPT<PIX> &src, TRasterPT<PIX> &dst, |
| int threshold, int softness) { |
| dst->copy(src); |
| if (softness == 0) return; |
| |
| double slope = (50.0 / softness); |
| double hStart = 0.5; |
| |
| src->lock(); |
| dst->lock(); |
| |
| PixelSelector<PIX> sel(threshold); |
| |
| |
| int x, y, lx = src->getLx(), ly = src->getLy(), lx_1 = lx - 1, ly_1 = ly - 1; |
| for (y = 0; y < ly_1; ++y) { |
| processLine(y, lx, ly, src->pixels(y), src->pixels(y + 1), dst->pixels(y), |
| dst->pixels(y + 1), 1, src->getWrap(), 1, 1, true, hStart, |
| slope, sel); |
| } |
| |
| |
| for (x = 0; x < lx_1; ++x) { |
| processLine(x, ly, lx, src->pixels(0) + x, src->pixels(0) + x + 1, |
| dst->pixels(0) + x, dst->pixels(0) + x + 1, src->getWrap(), 1, |
| dst->getWrap(), dst->getWrap(), false, hStart, slope, sel); |
| } |
| |
| dst->unlock(); |
| src->unlock(); |
| } |
| |
| |
| |
| void TRop::antialias(const TRasterP &src, const TRasterP &dst, int threshold, |
| int softness) { |
| assert(src->getSize() == dst->getSize()); |
| |
| TRaster32P src32(src), dst32(dst); |
| if (src32 && dst32) { |
| makeAntialias<TPixel32>(src32, dst32, threshold, softness); |
| return; |
| } |
| |
| TRaster64P src64(src), dst64(dst); |
| if (src64 && dst64) { |
| makeAntialias<TPixel64>(src64, dst64, threshold << 8, softness); |
| return; |
| } |
| |
| TRasterCM32P srcCM(src), dstCM(dst); |
| if (srcCM && dstCM) { |
| makeAntialias<TPixelCM32>(srcCM, dstCM, threshold, softness); |
| return; |
| } |
| |
| assert(!"Source and destination rasters must be of the same type!"); |
| } |
| |