Home | History | Annotate | Download | only in fpdf_render
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "../../../include/fpdfapi/fpdf_render.h"
      8 #include "../../../include/fpdfapi/fpdf_pageobj.h"
      9 #include "../../../include/fxge/fx_ge.h"
     10 #include "../fpdf_page/pageint.h"
     11 #include "render_int.h"
     12 #define SHADING_STEPS 256
     13 static void _DrawAxialShading(CFX_DIBitmap* pBitmap, CFX_AffineMatrix* pObject2Bitmap,
     14                               CPDF_Dictionary* pDict, CPDF_Function** pFuncs, int nFuncs,
     15                               CPDF_ColorSpace* pCS, int alpha)
     16 {
     17     ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
     18     CPDF_Array* pCoords = pDict->GetArray(FX_BSTRC("Coords"));
     19     if (pCoords == NULL) {
     20         return;
     21     }
     22     FX_FLOAT start_x = pCoords->GetNumber(0);
     23     FX_FLOAT start_y = pCoords->GetNumber(1);
     24     FX_FLOAT end_x = pCoords->GetNumber(2);
     25     FX_FLOAT end_y = pCoords->GetNumber(3);
     26     FX_FLOAT t_min = 0, t_max = 1.0f;
     27     CPDF_Array* pArray = pDict->GetArray(FX_BSTRC("Domain"));
     28     if (pArray) {
     29         t_min = pArray->GetNumber(0);
     30         t_max = pArray->GetNumber(1);
     31     }
     32     FX_BOOL bStartExtend = FALSE, bEndExtend = FALSE;
     33     pArray = pDict->GetArray(FX_BSTRC("Extend"));
     34     if (pArray) {
     35         bStartExtend = pArray->GetInteger(0);
     36         bEndExtend = pArray->GetInteger(1);
     37     }
     38     int width = pBitmap->GetWidth();
     39     int height = pBitmap->GetHeight();
     40     FX_FLOAT x_span = end_x - start_x;
     41     FX_FLOAT y_span = end_y - start_y;
     42     FX_FLOAT axis_len_square = FXSYS_Mul(x_span, x_span) + FXSYS_Mul(y_span, y_span);
     43     CFX_AffineMatrix matrix;
     44     matrix.SetReverse(*pObject2Bitmap);
     45     int total_results = 0;
     46     for (int j = 0; j < nFuncs; j ++) {
     47         if (pFuncs[j]) {
     48             total_results += pFuncs[j]->CountOutputs();
     49         }
     50     }
     51     if (pCS->CountComponents() > total_results) {
     52         total_results = pCS->CountComponents();
     53     }
     54     CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results);
     55     FX_FLOAT* pResults = result_array;
     56     FXSYS_memset32(pResults, 0, total_results * sizeof(FX_FLOAT));
     57     FX_DWORD rgb_array[SHADING_STEPS];
     58     for (int i = 0; i < SHADING_STEPS; i ++) {
     59         FX_FLOAT input = (t_max - t_min) * i / SHADING_STEPS + t_min;
     60         int offset = 0;
     61         for (int j = 0; j < nFuncs; j ++) {
     62             if (pFuncs[j]) {
     63                 int nresults = 0;
     64                 if (pFuncs[j]->Call(&input, 1, pResults + offset, nresults)) {
     65                     offset += nresults;
     66                 }
     67             }
     68         }
     69         FX_FLOAT R, G, B;
     70         pCS->GetRGB(pResults, R, G, B);
     71         rgb_array[i] = FXARGB_TODIB(FXARGB_MAKE(alpha, FXSYS_round(R * 255), FXSYS_round(G * 255), FXSYS_round(B * 255)));
     72     }
     73     int pitch = pBitmap->GetPitch();
     74     int Bpp = pBitmap->GetBPP() / 8;
     75     for (int row = 0; row < height; row ++) {
     76         FX_DWORD* dib_buf = (FX_DWORD*)(pBitmap->GetBuffer() + row * pitch);
     77         for (int column = 0; column < width; column ++) {
     78             FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row;
     79             matrix.Transform(x, y);
     80             FX_FLOAT scale = FXSYS_Div(FXSYS_Mul(x - start_x, x_span) + FXSYS_Mul(y - start_y, y_span), axis_len_square);
     81             int index = (FX_INT32)(scale * (SHADING_STEPS - 1));
     82             if (index < 0) {
     83                 if (!bStartExtend) {
     84                     continue;
     85                 }
     86                 index = 0;
     87             } else if (index >= SHADING_STEPS) {
     88                 if (!bEndExtend) {
     89                     continue;
     90                 }
     91                 index = SHADING_STEPS - 1;
     92             }
     93             dib_buf[column] = rgb_array[index];
     94         }
     95     }
     96 }
     97 static void _DrawRadialShading(CFX_DIBitmap* pBitmap, CFX_AffineMatrix* pObject2Bitmap,
     98                                CPDF_Dictionary* pDict, CPDF_Function** pFuncs, int nFuncs,
     99                                CPDF_ColorSpace* pCS, int alpha)
    100 {
    101     ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
    102     CPDF_Array* pCoords = pDict->GetArray(FX_BSTRC("Coords"));
    103     if (pCoords == NULL) {
    104         return;
    105     }
    106     FX_FLOAT start_x = pCoords->GetNumber(0);
    107     FX_FLOAT start_y = pCoords->GetNumber(1);
    108     FX_FLOAT start_r = pCoords->GetNumber(2);
    109     FX_FLOAT end_x = pCoords->GetNumber(3);
    110     FX_FLOAT end_y = pCoords->GetNumber(4);
    111     FX_FLOAT end_r = pCoords->GetNumber(5);
    112     CFX_AffineMatrix matrix;
    113     matrix.SetReverse(*pObject2Bitmap);
    114     FX_FLOAT t_min = 0, t_max = 1.0f;
    115     CPDF_Array* pArray = pDict->GetArray(FX_BSTRC("Domain"));
    116     if (pArray) {
    117         t_min = pArray->GetNumber(0);
    118         t_max = pArray->GetNumber(1);
    119     }
    120     FX_BOOL bStartExtend = FALSE, bEndExtend = FALSE;
    121     pArray = pDict->GetArray(FX_BSTRC("Extend"));
    122     if (pArray) {
    123         bStartExtend = pArray->GetInteger(0);
    124         bEndExtend = pArray->GetInteger(1);
    125     }
    126     int total_results = 0;
    127     for (int j = 0; j < nFuncs; j ++) {
    128         if (pFuncs[j]) {
    129             total_results += pFuncs[j]->CountOutputs();
    130         }
    131     }
    132     if (pCS->CountComponents() > total_results) {
    133         total_results = pCS->CountComponents();
    134     }
    135     CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results);
    136     FX_FLOAT* pResults = result_array;
    137     FXSYS_memset32(pResults, 0, total_results * sizeof(FX_FLOAT));
    138     FX_DWORD rgb_array[SHADING_STEPS];
    139     for (int i = 0; i < SHADING_STEPS; i ++) {
    140         FX_FLOAT input = (t_max - t_min) * i / SHADING_STEPS + t_min;
    141         int offset = 0;
    142         for (int j = 0; j < nFuncs; j ++) {
    143             if (pFuncs[j]) {
    144                 int nresults;
    145                 if (pFuncs[j]->Call(&input, 1, pResults + offset, nresults)) {
    146                     offset += nresults;
    147                 }
    148             }
    149         }
    150         FX_FLOAT R, G, B;
    151         pCS->GetRGB(pResults, R, G, B);
    152         rgb_array[i] = FXARGB_TODIB(FXARGB_MAKE(alpha, FXSYS_round(R * 255), FXSYS_round(G * 255), FXSYS_round(B * 255)));
    153     }
    154     FX_FLOAT a = FXSYS_Mul(start_x - end_x, start_x - end_x) +
    155                  FXSYS_Mul(start_y - end_y, start_y - end_y) - FXSYS_Mul(start_r - end_r, start_r - end_r);
    156     int width = pBitmap->GetWidth();
    157     int height = pBitmap->GetHeight();
    158     int pitch = pBitmap->GetPitch();
    159     int Bpp = pBitmap->GetBPP() / 8;
    160     FX_BOOL bDecreasing = FALSE;
    161     if (start_r > end_r) {
    162         int length = (int)FXSYS_sqrt((FXSYS_Mul(start_x - end_x, start_x - end_x) + FXSYS_Mul(start_y - end_y, start_y - end_y)));
    163         if (length < start_r - end_r) {
    164             bDecreasing = TRUE;
    165         }
    166     }
    167     for (int row = 0; row < height; row ++) {
    168         FX_DWORD* dib_buf = (FX_DWORD*)(pBitmap->GetBuffer() + row * pitch);
    169         for (int column = 0; column < width; column ++) {
    170             FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row;
    171             matrix.Transform(x, y);
    172             FX_FLOAT b = -2 * (FXSYS_Mul(x - start_x, end_x - start_x) + FXSYS_Mul(y - start_y, end_y - start_y) +
    173                                FXSYS_Mul(start_r, end_r - start_r));
    174             FX_FLOAT c = FXSYS_Mul(x - start_x, x - start_x) + FXSYS_Mul(y - start_y, y - start_y) -
    175                          FXSYS_Mul(start_r, start_r);
    176             FX_FLOAT s;
    177             if (a == 0) {
    178                 s = FXSYS_Div(-c, b);
    179             } else {
    180                 FX_FLOAT b2_4ac = FXSYS_Mul(b, b) - 4 * FXSYS_Mul(a, c);
    181                 if (b2_4ac < 0) {
    182                     continue;
    183                 }
    184                 FX_FLOAT root = FXSYS_sqrt(b2_4ac);
    185                 FX_FLOAT s1, s2;
    186                 if (a > 0) {
    187                     s1 = FXSYS_Div(-b - root, 2 * a);
    188                     s2 = FXSYS_Div(-b + root, 2 * a);
    189                 } else {
    190                     s2 = FXSYS_Div(-b - root, 2 * a);
    191                     s1 = FXSYS_Div(-b + root, 2 * a);
    192                 }
    193                 if (bDecreasing) {
    194                     if (s1 >= 0 || bStartExtend) {
    195                         s = s1;
    196                     } else {
    197                         s = s2;
    198                     }
    199                 } else {
    200                     if (s2 <= 1.0f || bEndExtend) {
    201                         s = s2;
    202                     } else {
    203                         s = s1;
    204                     }
    205                 }
    206                 if ((start_r + s * (end_r - start_r)) < 0) {
    207                     continue;
    208                 }
    209             }
    210             int index = (FX_INT32)(s * (SHADING_STEPS - 1));
    211             if (index < 0) {
    212                 if (!bStartExtend) {
    213                     continue;
    214                 }
    215                 index = 0;
    216             }
    217             if (index >= SHADING_STEPS) {
    218                 if (!bEndExtend) {
    219                     continue;
    220                 }
    221                 index = SHADING_STEPS - 1;
    222             }
    223             dib_buf[column] = rgb_array[index];
    224         }
    225     }
    226 }
    227 static void _DrawFuncShading(CFX_DIBitmap* pBitmap, CFX_AffineMatrix* pObject2Bitmap,
    228                              CPDF_Dictionary* pDict, CPDF_Function** pFuncs, int nFuncs,
    229                              CPDF_ColorSpace* pCS, int alpha)
    230 {
    231     ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
    232     CPDF_Array* pDomain = pDict->GetArray(FX_BSTRC("Domain"));
    233     FX_FLOAT xmin = 0, ymin = 0, xmax = 1.0f, ymax = 1.0f;
    234     if (pDomain) {
    235         xmin = pDomain->GetNumber(0);
    236         xmax = pDomain->GetNumber(1);
    237         ymin = pDomain->GetNumber(2);
    238         ymax = pDomain->GetNumber(3);
    239     }
    240     CFX_AffineMatrix mtDomain2Target = pDict->GetMatrix(FX_BSTRC("Matrix"));
    241     CFX_AffineMatrix matrix, reverse_matrix;
    242     matrix.SetReverse(*pObject2Bitmap);
    243     reverse_matrix.SetReverse(mtDomain2Target);
    244     matrix.Concat(reverse_matrix);
    245     int width = pBitmap->GetWidth();
    246     int height = pBitmap->GetHeight();
    247     int pitch = pBitmap->GetPitch();
    248     int Bpp = pBitmap->GetBPP() / 8;
    249     int total_results = 0;
    250     for (int j = 0; j < nFuncs; j ++) {
    251         if (pFuncs[j]) {
    252             total_results += pFuncs[j]->CountOutputs();
    253         }
    254     }
    255     if (pCS->CountComponents() > total_results) {
    256         total_results = pCS->CountComponents();
    257     }
    258     CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results);
    259     FX_FLOAT* pResults = result_array;
    260     FXSYS_memset32(pResults, 0, total_results * sizeof(FX_FLOAT));
    261     for (int row = 0; row < height; row ++) {
    262         FX_DWORD* dib_buf = (FX_DWORD*)(pBitmap->GetBuffer() + row * pitch);
    263         for (int column = 0; column < width; column ++) {
    264             FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row;
    265             matrix.Transform(x, y);
    266             if (x < xmin || x > xmax || y < ymin || y > ymax) {
    267                 continue;
    268             }
    269             FX_FLOAT input[2];
    270             int offset = 0;
    271             input[0] = x;
    272             input[1] = y;
    273             for (int j = 0; j < nFuncs; j ++) {
    274                 if (pFuncs[j]) {
    275                     int nresults;
    276                     if (pFuncs[j]->Call(input, 2, pResults + offset, nresults)) {
    277                         offset += nresults;
    278                     }
    279                 }
    280             }
    281             FX_FLOAT R, G, B;
    282             pCS->GetRGB(pResults, R, G, B);
    283             dib_buf[column] = FXARGB_TODIB(FXARGB_MAKE(alpha, (FX_INT32)(R * 255), (FX_INT32)(G * 255), (FX_INT32)(B * 255)));
    284         }
    285     }
    286 }
    287 FX_BOOL _GetScanlineIntersect(int y, FX_FLOAT x1, FX_FLOAT y1, FX_FLOAT x2, FX_FLOAT y2, FX_FLOAT& x)
    288 {
    289     if (y1 == y2) {
    290         return FALSE;
    291     }
    292     if (y1 < y2) {
    293         if (y < y1 || y > y2) {
    294             return FALSE;
    295         }
    296     } else {
    297         if (y < y2 || y > y1) {
    298             return FALSE;
    299         }
    300     }
    301     x = x1 + FXSYS_MulDiv(x2 - x1, y - y1, y2 - y1);
    302     return TRUE;
    303 }
    304 static void _DrawGouraud(CFX_DIBitmap* pBitmap, int alpha, CPDF_MeshVertex triangle[3])
    305 {
    306     FX_FLOAT min_y = triangle[0].y, max_y = triangle[0].y;
    307     for (int i = 1; i < 3; i ++) {
    308         if (min_y > triangle[i].y) {
    309             min_y = triangle[i].y;
    310         }
    311         if (max_y < triangle[i].y) {
    312             max_y = triangle[i].y;
    313         }
    314     }
    315     if (min_y == max_y) {
    316         return;
    317     }
    318     int min_yi = (int)FXSYS_floor(min_y), max_yi = (int)FXSYS_ceil(max_y);
    319     if (min_yi < 0) {
    320         min_yi = 0;
    321     }
    322     if (max_yi >= pBitmap->GetHeight()) {
    323         max_yi = pBitmap->GetHeight() - 1;
    324     }
    325     for (int y = min_yi; y <= max_yi; y ++) {
    326         int nIntersects = 0;
    327         FX_FLOAT inter_x[3], r[3], g[3], b[3];
    328         for (int i = 0; i < 3; i ++) {
    329             CPDF_MeshVertex& vertex1 = triangle[i];
    330             CPDF_MeshVertex& vertex2 = triangle[(i + 1) % 3];
    331             FX_BOOL bIntersect = _GetScanlineIntersect(y, vertex1.x, vertex1.y,
    332                                  vertex2.x, vertex2.y, inter_x[nIntersects]);
    333             if (!bIntersect) {
    334                 continue;
    335             }
    336             r[nIntersects] = vertex1.r + FXSYS_MulDiv(vertex2.r - vertex1.r, y - vertex1.y, vertex2.y - vertex1.y);
    337             g[nIntersects] = vertex1.g + FXSYS_MulDiv(vertex2.g - vertex1.g, y - vertex1.y, vertex2.y - vertex1.y);
    338             b[nIntersects] = vertex1.b + FXSYS_MulDiv(vertex2.b - vertex1.b, y - vertex1.y, vertex2.y - vertex1.y);
    339             nIntersects ++;
    340         }
    341         if (nIntersects != 2) {
    342             continue;
    343         }
    344         int min_x, max_x, start_index, end_index;
    345         if (inter_x[0] < inter_x[1]) {
    346             min_x = (int)FXSYS_floor(inter_x[0]);
    347             max_x = (int)FXSYS_ceil(inter_x[1]);
    348             start_index = 0;
    349             end_index = 1;
    350         } else {
    351             min_x = (int)FXSYS_floor(inter_x[1]);
    352             max_x = (int)FXSYS_ceil(inter_x[0]);
    353             start_index = 1;
    354             end_index = 0;
    355         }
    356         int start_x = min_x, end_x = max_x;
    357         if (start_x < 0) {
    358             start_x = 0;
    359         }
    360         if (end_x > pBitmap->GetWidth()) {
    361             end_x = pBitmap->GetWidth();
    362         }
    363         FX_LPBYTE dib_buf = pBitmap->GetBuffer() + y * pBitmap->GetPitch() + start_x * 4;
    364         FX_FLOAT r_unit = (r[end_index] - r[start_index]) / (max_x - min_x);
    365         FX_FLOAT g_unit = (g[end_index] - g[start_index]) / (max_x - min_x);
    366         FX_FLOAT b_unit = (b[end_index] - b[start_index]) / (max_x - min_x);
    367         FX_FLOAT R = r[start_index] + (start_x - min_x) * r_unit;
    368         FX_FLOAT G = g[start_index] + (start_x - min_x) * g_unit;
    369         FX_FLOAT B = b[start_index] + (start_x - min_x) * b_unit;
    370         for (int x = start_x; x < end_x; x ++) {
    371             R += r_unit;
    372             G += g_unit;
    373             B += b_unit;
    374             FXARGB_SETDIB(dib_buf, FXARGB_MAKE(alpha, (FX_INT32)(R * 255), (FX_INT32)(G * 255), (FX_INT32)(B * 255)));
    375             dib_buf += 4;
    376         }
    377     }
    378 }
    379 static void _DrawFreeGouraudShading(CFX_DIBitmap* pBitmap, CFX_AffineMatrix* pObject2Bitmap,
    380                                     CPDF_Stream* pShadingStream, CPDF_Function** pFuncs, int nFuncs,
    381                                     CPDF_ColorSpace* pCS, int alpha)
    382 {
    383     ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
    384     if (pShadingStream->GetType() != PDFOBJ_STREAM) {
    385         return;
    386     }
    387     CPDF_MeshStream stream;
    388     if (!stream.Load(pShadingStream, pFuncs, nFuncs, pCS)) {
    389         return;
    390     }
    391     CPDF_MeshVertex triangle[3];
    392     while (!stream.m_BitStream.IsEOF()) {
    393         CPDF_MeshVertex vertex;
    394         FX_DWORD flag = stream.GetVertex(vertex, pObject2Bitmap);
    395         if (flag == 0) {
    396             triangle[0] = vertex;
    397             for (int j = 1; j < 3; j ++) {
    398                 stream.GetVertex(triangle[j], pObject2Bitmap);
    399             }
    400         } else {
    401             if (flag == 1) {
    402                 triangle[0] = triangle[1];
    403             }
    404             triangle[1] = triangle[2];
    405             triangle[2] = vertex;
    406         }
    407         _DrawGouraud(pBitmap, alpha, triangle);
    408     }
    409 }
    410 static void _DrawLatticeGouraudShading(CFX_DIBitmap* pBitmap, CFX_AffineMatrix* pObject2Bitmap,
    411                                        CPDF_Stream* pShadingStream, CPDF_Function** pFuncs, int nFuncs,
    412                                        CPDF_ColorSpace* pCS, int alpha)
    413 {
    414     ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
    415     if (pShadingStream->GetType() != PDFOBJ_STREAM) {
    416         return;
    417     }
    418     int row_verts = pShadingStream->GetDict()->GetInteger("VerticesPerRow");
    419     if (row_verts < 2) {
    420         return;
    421     }
    422     CPDF_MeshStream stream;
    423     if (!stream.Load(pShadingStream, pFuncs, nFuncs, pCS)) {
    424         return;
    425     }
    426     CPDF_MeshVertex* vertex = FX_Alloc(CPDF_MeshVertex, row_verts * 2);
    427     if (!stream.GetVertexRow(vertex, row_verts, pObject2Bitmap)) {
    428         FX_Free(vertex);
    429         return;
    430     }
    431     int last_index = 0;
    432     while (1) {
    433         CPDF_MeshVertex* last_row = vertex + last_index * row_verts;
    434         CPDF_MeshVertex* this_row = vertex + (1 - last_index) * row_verts;
    435         if (!stream.GetVertexRow(this_row, row_verts, pObject2Bitmap)) {
    436             FX_Free(vertex);
    437             return;
    438         }
    439         CPDF_MeshVertex triangle[3];
    440         for (int i = 1; i < row_verts; i ++) {
    441             triangle[0] = last_row[i];
    442             triangle[1] = this_row[i - 1];
    443             triangle[2] = last_row[i - 1];
    444             _DrawGouraud(pBitmap, alpha, triangle);
    445             triangle[2] = this_row[i];
    446             _DrawGouraud(pBitmap, alpha, triangle);
    447         }
    448         last_index = 1 - last_index;
    449     }
    450     FX_Free(vertex);
    451 }
    452 struct Coon_BezierCoeff {
    453     float a, b, c, d;
    454     void FromPoints(float p0, float p1, float p2, float p3)
    455     {
    456         a = -p0 + 3 * p1 - 3 * p2 + p3;
    457         b = 3 * p0 - 6 * p1 + 3 * p2;
    458         c = -3 * p0 + 3 * p1;
    459         d = p0;
    460     }
    461     Coon_BezierCoeff first_half()
    462     {
    463         Coon_BezierCoeff result;
    464         result.a = a / 8;
    465         result.b = b / 4;
    466         result.c = c / 2;
    467         result.d = d;
    468         return result;
    469     }
    470     Coon_BezierCoeff second_half()
    471     {
    472         Coon_BezierCoeff result;
    473         result.a = a / 8;
    474         result.b = 3 * a / 8 + b / 4;
    475         result.c = 3 * a / 8 + b / 2 + c / 2;
    476         result.d = a / 8 + b / 4 + c / 2 + d;
    477         return result;
    478     }
    479     void GetPoints(float p[4])
    480     {
    481         p[0] = d;
    482         p[1] = c / 3 + p[0];
    483         p[2] = b / 3 - p[0] + 2 * p[1];
    484         p[3] = a + p[0] - 3 * p[1] + 3 * p[2];
    485     }
    486     void GetPointsReverse(float p[4])
    487     {
    488         p[3] = d;
    489         p[2] = c / 3 + p[3];
    490         p[1] = b / 3 - p[3] + 2 * p[2];
    491         p[0] = a + p[3] - 3 * p[2] + 3 * p[1];
    492     }
    493     void BezierInterpol(Coon_BezierCoeff& C1, Coon_BezierCoeff& C2, Coon_BezierCoeff& D1, Coon_BezierCoeff& D2)
    494     {
    495         a = (D1.a + D2.a) / 2;
    496         b = (D1.b + D2.b) / 2;
    497         c = (D1.c + D2.c) / 2 - (C1.a / 8 + C1.b / 4 + C1.c / 2) + (C2.a / 8 + C2.b / 4) + (-C1.d + D2.d) / 2 - (C2.a + C2.b) / 2;
    498         d = C1.a / 8 + C1.b / 4 + C1.c / 2 + C1.d;
    499     }
    500     float Distance()
    501     {
    502         float dis = a + b + c;
    503         return dis < 0 ? -dis : dis;
    504     }
    505 };
    506 struct Coon_Bezier {
    507     Coon_BezierCoeff x, y;
    508     void FromPoints(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)
    509     {
    510         x.FromPoints(x0, x1, x2, x3);
    511         y.FromPoints(y0, y1, y2, y3);
    512     }
    513     Coon_Bezier first_half()
    514     {
    515         Coon_Bezier result;
    516         result.x = x.first_half();
    517         result.y = y.first_half();
    518         return result;
    519     }
    520     Coon_Bezier second_half()
    521     {
    522         Coon_Bezier result;
    523         result.x = x.second_half();
    524         result.y = y.second_half();
    525         return result;
    526     }
    527     void BezierInterpol(Coon_Bezier& C1, Coon_Bezier& C2, Coon_Bezier& D1, Coon_Bezier& D2)
    528     {
    529         x.BezierInterpol(C1.x, C2.x, D1.x, D2.x);
    530         y.BezierInterpol(C1.y, C2.y, D1.y, D2.y);
    531     }
    532     void GetPoints(FX_PATHPOINT* pPoints)
    533     {
    534         float p[4];
    535         int i;
    536         x.GetPoints(p);
    537         for (i = 0; i < 4; i ++) {
    538             pPoints[i].m_PointX = p[i];
    539         }
    540         y.GetPoints(p);
    541         for (i = 0; i < 4; i ++) {
    542             pPoints[i].m_PointY = p[i];
    543         }
    544     }
    545     void GetPointsReverse(FX_PATHPOINT* pPoints)
    546     {
    547         float p[4];
    548         int i;
    549         x.GetPointsReverse(p);
    550         for (i = 0; i < 4; i ++) {
    551             pPoints[i].m_PointX = p[i];
    552         }
    553         y.GetPointsReverse(p);
    554         for (i = 0; i < 4; i ++) {
    555             pPoints[i].m_PointY = p[i];
    556         }
    557     }
    558     float Distance()
    559     {
    560         return x.Distance() + y.Distance();
    561     }
    562 };
    563 static int _BiInterpol(int c0, int c1, int c2, int c3, int x, int y, int x_scale, int y_scale)
    564 {
    565     int x1 = c0 + (c3 - c0) * x / x_scale;
    566     int x2 = c1 + (c2 - c1) * x / x_scale;
    567     return x1 + (x2 - x1) * y / y_scale;
    568 }
    569 struct Coon_Color {
    570     Coon_Color()
    571     {
    572         FXSYS_memset32(comp, 0, sizeof(int) * 3);
    573     }
    574     int		comp[3];
    575     void	BiInterpol(Coon_Color colors[4], int x, int y, int x_scale, int y_scale)
    576     {
    577         for (int i = 0; i < 3; i ++)
    578             comp[i] = _BiInterpol(colors[0].comp[i], colors[1].comp[i], colors[2].comp[i], colors[3].comp[i],
    579                                   x, y, x_scale, y_scale);
    580     }
    581     int		Distance(Coon_Color& o)
    582     {
    583         int max, diff;
    584         max = FXSYS_abs(comp[0] - o.comp[0]);
    585         diff = FXSYS_abs(comp[1] - o.comp[1]);
    586         if (max < diff) {
    587             max = diff;
    588         }
    589         diff = FXSYS_abs(comp[2] - o.comp[2]);
    590         if (max < diff) {
    591             max = diff;
    592         }
    593         return max;
    594     }
    595 };
    596 struct CPDF_PatchDrawer {
    597     Coon_Color			patch_colors[4];
    598     int					max_delta;
    599     CFX_PathData		path;
    600     CFX_RenderDevice*	pDevice;
    601     int					fill_mode;
    602     int					alpha;
    603     void Draw(int x_scale, int y_scale, int left, int bottom, Coon_Bezier C1, Coon_Bezier C2, Coon_Bezier D1, Coon_Bezier D2)
    604     {
    605         FX_BOOL bSmall = C1.Distance() < 2 && C2.Distance() < 2 && D1.Distance() < 2 && D2.Distance() < 2;
    606         Coon_Color div_colors[4];
    607         int d_bottom, d_left, d_top, d_right;
    608         div_colors[0].BiInterpol(patch_colors, left, bottom, x_scale, y_scale);
    609         if (!bSmall) {
    610             div_colors[1].BiInterpol(patch_colors, left, bottom + 1, x_scale, y_scale);
    611             div_colors[2].BiInterpol(patch_colors, left + 1, bottom + 1, x_scale, y_scale);
    612             div_colors[3].BiInterpol(patch_colors, left + 1, bottom, x_scale, y_scale);
    613             d_bottom = div_colors[3].Distance(div_colors[0]);
    614             d_left = div_colors[1].Distance(div_colors[0]);
    615             d_top = div_colors[1].Distance(div_colors[2]);
    616             d_right = div_colors[2].Distance(div_colors[3]);
    617         }
    618 #define COONCOLOR_THRESHOLD 4
    619         if (bSmall || (d_bottom < COONCOLOR_THRESHOLD && d_left < COONCOLOR_THRESHOLD &&
    620                        d_top < COONCOLOR_THRESHOLD && d_right < COONCOLOR_THRESHOLD)) {
    621             FX_PATHPOINT* pPoints = path.GetPoints();
    622             C1.GetPoints(pPoints);
    623             D2.GetPoints(pPoints + 3);
    624             C2.GetPointsReverse(pPoints + 6);
    625             D1.GetPointsReverse(pPoints + 9);
    626             int fillFlags = FXFILL_WINDING | FXFILL_FULLCOVER;
    627             if (fill_mode & RENDER_NOPATHSMOOTH) {
    628                 fillFlags |= FXFILL_NOPATHSMOOTH;
    629             }
    630             pDevice->DrawPath(&path, NULL, NULL, FXARGB_MAKE(alpha, div_colors[0].comp[0], div_colors[0].comp[1], div_colors[0].comp[2]), 0, fillFlags);
    631         } else {
    632             if (d_bottom < COONCOLOR_THRESHOLD && d_top < COONCOLOR_THRESHOLD) {
    633                 Coon_Bezier m1;
    634                 m1.BezierInterpol(D1, D2, C1, C2);
    635                 y_scale *= 2;
    636                 bottom *= 2;
    637                 Draw(x_scale, y_scale, left, bottom, C1, m1, D1.first_half(), D2.first_half());
    638                 Draw(x_scale, y_scale, left, bottom + 1, m1, C2, D1.second_half(), D2.second_half());
    639             } else if (d_left < COONCOLOR_THRESHOLD && d_right < COONCOLOR_THRESHOLD) {
    640                 Coon_Bezier m2;
    641                 m2.BezierInterpol(C1, C2, D1, D2);
    642                 x_scale *= 2;
    643                 left *= 2;
    644                 Draw(x_scale, y_scale, left, bottom, C1.first_half(), C2.first_half(), D1, m2);
    645                 Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), C2.second_half(), m2, D2);
    646             } else {
    647                 Coon_Bezier m1, m2;
    648                 m1.BezierInterpol(D1, D2, C1, C2);
    649                 m2.BezierInterpol(C1, C2, D1, D2);
    650                 Coon_Bezier m1f = m1.first_half();
    651                 Coon_Bezier m1s = m1.second_half();
    652                 Coon_Bezier m2f = m2.first_half();
    653                 Coon_Bezier m2s = m2.second_half();
    654                 x_scale *= 2;
    655                 y_scale *= 2;
    656                 left *= 2;
    657                 bottom *= 2;
    658                 Draw(x_scale, y_scale, left, bottom, C1.first_half(), m1f, D1.first_half(), m2f);
    659                 Draw(x_scale, y_scale, left, bottom + 1, m1f, C2.first_half(), D1.second_half(), m2s);
    660                 Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), m1s, m2f, D2.first_half());
    661                 Draw(x_scale, y_scale, left + 1, bottom + 1, m1s, C2.second_half(), m2s, D2.second_half());
    662             }
    663         }
    664     }
    665 };
    666 static void _DrawCoonPatchMeshes(FX_BOOL bTensor, CFX_DIBitmap* pBitmap, CFX_AffineMatrix* pObject2Bitmap,
    667                                  CPDF_Stream* pShadingStream, CPDF_Function** pFuncs, int nFuncs,
    668                                  CPDF_ColorSpace* pCS, int fill_mode, int alpha)
    669 {
    670     ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
    671     if (pShadingStream->GetType() != PDFOBJ_STREAM) {
    672         return;
    673     }
    674     CFX_FxgeDevice device;
    675     device.Attach(pBitmap);
    676     CPDF_MeshStream stream;
    677     if (!stream.Load(pShadingStream, pFuncs, nFuncs, pCS)) {
    678         return;
    679     }
    680     CPDF_PatchDrawer patch;
    681     patch.alpha = alpha;
    682     patch.pDevice = &device;
    683     patch.fill_mode = fill_mode;
    684     patch.path.SetPointCount(13);
    685     FX_PATHPOINT* pPoints = patch.path.GetPoints();
    686     pPoints[0].m_Flag = FXPT_MOVETO;
    687     for (int i = 1; i < 13; i ++) {
    688         pPoints[i].m_Flag = FXPT_BEZIERTO;
    689     }
    690     CFX_FloatPoint coords[16];
    691     int point_count = bTensor ? 16 : 12;
    692     while (!stream.m_BitStream.IsEOF()) {
    693         FX_DWORD flag = stream.GetFlag();
    694         int iStartPoint = 0, iStartColor = 0, i;
    695         if (flag) {
    696             iStartPoint = 4;
    697             iStartColor = 2;
    698             CFX_FloatPoint tempCoords[4];
    699             for (int i = 0; i < 4; i ++) {
    700                 tempCoords[i] = coords[(flag * 3 + i) % 12];
    701             }
    702             FXSYS_memcpy32(coords, tempCoords, sizeof(CFX_FloatPoint) * 4);
    703             Coon_Color tempColors[2];
    704             tempColors[0] = patch.patch_colors[flag];
    705             tempColors[1] = patch.patch_colors[(flag + 1) % 4];
    706             FXSYS_memcpy32(patch.patch_colors, tempColors, sizeof(Coon_Color) * 2);
    707         }
    708         for (i = iStartPoint; i < point_count; i ++) {
    709             stream.GetCoords(coords[i].x, coords[i].y);
    710             pObject2Bitmap->Transform(coords[i].x, coords[i].y);
    711         }
    712         for (i = iStartColor; i < 4; i ++) {
    713             FX_FLOAT r, g, b;
    714             stream.GetColor(r, g, b);
    715             patch.patch_colors[i].comp[0] = (FX_INT32)(r * 255);
    716             patch.patch_colors[i].comp[1] = (FX_INT32)(g * 255);
    717             patch.patch_colors[i].comp[2] = (FX_INT32)(b * 255);
    718         }
    719         CFX_FloatRect bbox = CFX_FloatRect::GetBBox(coords, point_count);
    720         if (bbox.right <= 0 || bbox.left >= (FX_FLOAT)pBitmap->GetWidth() || bbox.top <= 0 ||
    721                 bbox.bottom >= (FX_FLOAT)pBitmap->GetHeight()) {
    722             continue;
    723         }
    724         Coon_Bezier C1, C2, D1, D2;
    725         C1.FromPoints(coords[0].x, coords[0].y, coords[11].x, coords[11].y, coords[10].x, coords[10].y,
    726                       coords[9].x, coords[9].y);
    727         C2.FromPoints(coords[3].x, coords[3].y, coords[4].x, coords[4].y, coords[5].x, coords[5].y,
    728                       coords[6].x, coords[6].y);
    729         D1.FromPoints(coords[0].x, coords[0].y, coords[1].x, coords[1].y, coords[2].x, coords[2].y,
    730                       coords[3].x, coords[3].y);
    731         D2.FromPoints(coords[9].x, coords[9].y, coords[8].x, coords[8].y, coords[7].x, coords[7].y,
    732                       coords[6].x, coords[6].y);
    733         patch.Draw(1, 1, 0, 0, C1, C2, D1, D2);
    734     }
    735 }
    736 void CPDF_RenderStatus::DrawShading(CPDF_ShadingPattern* pPattern, CFX_AffineMatrix* pMatrix,
    737                                     FX_RECT& clip_rect, int alpha, FX_BOOL bAlphaMode)
    738 {
    739     int width = clip_rect.Width();
    740     int height = clip_rect.Height();
    741     CPDF_Function** pFuncs = pPattern->m_pFunctions;
    742     int nFuncs = pPattern->m_nFuncs;
    743     CPDF_Dictionary* pDict = pPattern->m_pShadingObj->GetDict();
    744     CPDF_ColorSpace* pColorSpace = pPattern->m_pCS;
    745     if (pColorSpace == NULL) {
    746         return;
    747     }
    748     FX_ARGB background = 0;
    749     if (!pPattern->m_bShadingObj && pPattern->m_pShadingObj->GetDict()->KeyExist(FX_BSTRC("Background"))) {
    750         CPDF_Array* pBackColor = pPattern->m_pShadingObj->GetDict()->GetArray(FX_BSTRC("Background"));
    751         if (pBackColor && pBackColor->GetCount() >= (FX_DWORD)pColorSpace->CountComponents()) {
    752             CFX_FixedBufGrow<FX_FLOAT, 16> comps(pColorSpace->CountComponents());
    753             for (int i = 0; i < pColorSpace->CountComponents(); i ++) {
    754                 comps[i] = pBackColor->GetNumber(i);
    755             }
    756             FX_FLOAT R, G, B;
    757             pColorSpace->GetRGB(comps, R, G, B);
    758             background = ArgbEncode(255, (FX_INT32)(R * 255), (FX_INT32)(G * 255), (FX_INT32)(B * 255));
    759         }
    760     }
    761     if (pDict->KeyExist(FX_BSTRC("BBox"))) {
    762         CFX_FloatRect rect = pDict->GetRect(FX_BSTRC("BBox"));
    763         rect.Transform(pMatrix);
    764         clip_rect.Intersect(rect.GetOutterRect());
    765     }
    766     CPDF_DeviceBuffer buffer;
    767     buffer.Initialize(m_pContext, m_pDevice, &clip_rect, m_pCurObj, 150);
    768     CFX_AffineMatrix FinalMatrix = *pMatrix;
    769     FinalMatrix.Concat(*buffer.GetMatrix());
    770     CFX_DIBitmap* pBitmap = buffer.GetBitmap();
    771     if (pBitmap->GetBuffer() == NULL) {
    772         return;
    773     }
    774     pBitmap->Clear(background);
    775     int fill_mode = m_Options.m_Flags;
    776     switch (pPattern->m_ShadingType) {
    777         case 1:
    778             _DrawFuncShading(pBitmap, &FinalMatrix, pDict, pFuncs, nFuncs, pColorSpace, alpha);
    779             break;
    780         case 2:
    781             _DrawAxialShading(pBitmap, &FinalMatrix, pDict, pFuncs, nFuncs, pColorSpace, alpha);
    782             break;
    783         case 3:
    784             _DrawRadialShading(pBitmap, &FinalMatrix, pDict, pFuncs, nFuncs, pColorSpace, alpha);
    785             break;
    786         case 4: {
    787                 _DrawFreeGouraudShading(pBitmap, &FinalMatrix, (CPDF_Stream*)pPattern->m_pShadingObj,
    788                                         pFuncs, nFuncs, pColorSpace, alpha);
    789             }
    790             break;
    791         case 5: {
    792                 _DrawLatticeGouraudShading(pBitmap, &FinalMatrix, (CPDF_Stream*)pPattern->m_pShadingObj,
    793                                            pFuncs, nFuncs, pColorSpace, alpha);
    794             }
    795             break;
    796         case 6:
    797         case 7: {
    798                 _DrawCoonPatchMeshes(pPattern->m_ShadingType - 6, pBitmap, &FinalMatrix, (CPDF_Stream*)pPattern->m_pShadingObj,
    799                                      pFuncs, nFuncs, pColorSpace, fill_mode, alpha);
    800             }
    801             break;
    802     }
    803     if (bAlphaMode) {
    804         pBitmap->LoadChannel(FXDIB_Red, pBitmap, FXDIB_Alpha);
    805     }
    806     if (m_Options.m_ColorMode == RENDER_COLOR_GRAY) {
    807         pBitmap->ConvertColorScale(m_Options.m_ForeColor, m_Options.m_BackColor);
    808     }
    809     buffer.OutputToDevice();
    810 }
    811 void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern, CPDF_PageObject* pPageObj, const CFX_AffineMatrix* pObj2Device, FX_BOOL bStroke)
    812 {
    813     if (!pattern->Load()) {
    814         return;
    815     }
    816     m_pDevice->SaveState();
    817     if (pPageObj->m_Type == PDFPAGE_PATH) {
    818         if (!SelectClipPath((CPDF_PathObject*)pPageObj, pObj2Device, bStroke)) {
    819             m_pDevice->RestoreState();
    820             return;
    821         }
    822     } else if (pPageObj->m_Type == PDFPAGE_IMAGE) {
    823         FX_RECT rect = pPageObj->GetBBox(pObj2Device);
    824         m_pDevice->SetClip_Rect(&rect);
    825     } else {
    826         return;
    827     }
    828     FX_RECT rect;
    829     if (GetObjectClippedRect(pPageObj, pObj2Device, FALSE, rect)) {
    830         m_pDevice->RestoreState();
    831         return;
    832     }
    833     CFX_AffineMatrix matrix = pattern->m_Pattern2Form;
    834     matrix.Concat(*pObj2Device);
    835     GetScaledMatrix(matrix);
    836     int alpha = pPageObj->m_GeneralState.GetAlpha(bStroke);
    837     DrawShading(pattern, &matrix, rect, alpha, m_Options.m_ColorMode == RENDER_COLOR_ALPHA);
    838     m_pDevice->RestoreState();
    839 }
    840 FX_BOOL CPDF_RenderStatus::ProcessShading(CPDF_ShadingObject* pShadingObj, const CFX_AffineMatrix* pObj2Device)
    841 {
    842     FX_RECT rect = pShadingObj->GetBBox(pObj2Device);
    843     FX_RECT clip_box = m_pDevice->GetClipBox();
    844     rect.Intersect(clip_box);
    845     if (rect.IsEmpty()) {
    846         return TRUE;
    847     }
    848     CFX_AffineMatrix matrix = pShadingObj->m_Matrix;
    849     matrix.Concat(*pObj2Device);
    850     DrawShading(pShadingObj->m_pShading, &matrix, rect, pShadingObj->m_GeneralState.GetAlpha(FALSE),
    851                 m_Options.m_ColorMode == RENDER_COLOR_ALPHA);
    852 #ifdef _FPDFAPI_MINI_
    853     if (m_DitherBits) {
    854         DitherObjectArea(pShadingObj, pObj2Device);
    855     }
    856 #endif
    857     return TRUE;
    858 }
    859 static CFX_DIBitmap* DrawPatternBitmap(CPDF_Document* pDoc, CPDF_PageRenderCache* pCache,
    860                                        CPDF_TilingPattern* pPattern, const CFX_AffineMatrix* pObject2Device,
    861                                        int width, int height, int flags)
    862 {
    863     CFX_DIBitmap* pBitmap = FX_NEW CFX_DIBitmap;
    864     if (!pBitmap->Create(width, height, pPattern->m_bColored ? FXDIB_Argb : FXDIB_8bppMask)) {
    865         delete pBitmap;
    866         return NULL;
    867     }
    868     CFX_FxgeDevice bitmap_device;
    869     bitmap_device.Attach(pBitmap);
    870     pBitmap->Clear(0);
    871     CFX_FloatRect cell_bbox = pPattern->m_BBox;
    872     pPattern->m_Pattern2Form.TransformRect(cell_bbox);
    873     pObject2Device->TransformRect(cell_bbox);
    874     CFX_FloatRect bitmap_rect(0.0f, 0.0f, (FX_FLOAT)width, (FX_FLOAT)height);
    875     CFX_AffineMatrix mtAdjust;
    876     mtAdjust.MatchRect(bitmap_rect, cell_bbox);
    877     CFX_AffineMatrix mtPattern2Bitmap = *pObject2Device;
    878     mtPattern2Bitmap.Concat(mtAdjust);
    879     CPDF_RenderOptions options;
    880     if (!pPattern->m_bColored) {
    881         options.m_ColorMode = RENDER_COLOR_ALPHA;
    882     }
    883     flags |= RENDER_FORCE_HALFTONE;
    884     options.m_Flags = flags;
    885     CPDF_RenderContext context;
    886     context.Create(pDoc, pCache, NULL);
    887     context.DrawObjectList(&bitmap_device, pPattern->m_pForm, &mtPattern2Bitmap, &options);
    888     return pBitmap;
    889 }
    890 void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pPattern, CPDF_PageObject* pPageObj, const CFX_AffineMatrix* pObj2Device, FX_BOOL bStroke)
    891 {
    892     if (!pPattern->Load()) {
    893         return;
    894     }
    895     m_pDevice->SaveState();
    896     if (pPageObj->m_Type == PDFPAGE_PATH) {
    897         if (!SelectClipPath((CPDF_PathObject*)pPageObj, pObj2Device, bStroke)) {
    898             m_pDevice->RestoreState();
    899             return;
    900         }
    901     } else if (pPageObj->m_Type == PDFPAGE_IMAGE) {
    902         FX_RECT rect = pPageObj->GetBBox(pObj2Device);
    903         m_pDevice->SetClip_Rect(&rect);
    904     } else {
    905         return;
    906     }
    907     FX_RECT clip_box = m_pDevice->GetClipBox();
    908     if (clip_box.IsEmpty()) {
    909         m_pDevice->RestoreState();
    910         return;
    911     }
    912     CFX_Matrix dCTM = m_pDevice->GetCTM();
    913     FX_FLOAT sa = FXSYS_fabs(dCTM.a);
    914     FX_FLOAT sd = FXSYS_fabs(dCTM.d);
    915     clip_box.right = clip_box.left + (FX_INT32)FXSYS_ceil(clip_box.Width() * sa);
    916     clip_box.bottom = clip_box.top + (FX_INT32)FXSYS_ceil(clip_box.Height() * sd);
    917     CFX_AffineMatrix mtPattern2Device = pPattern->m_Pattern2Form;
    918     mtPattern2Device.Concat(*pObj2Device);
    919     GetScaledMatrix(mtPattern2Device);
    920     FX_BOOL bAligned = FALSE;
    921     if (pPattern->m_BBox.left == 0 && pPattern->m_BBox.bottom == 0 &&
    922             pPattern->m_BBox.right == pPattern->m_XStep && pPattern->m_BBox.top == pPattern->m_YStep &&
    923             (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated())) {
    924         bAligned = TRUE;
    925     }
    926     CFX_FloatRect cell_bbox = pPattern->m_BBox;
    927     mtPattern2Device.TransformRect(cell_bbox);
    928     int width = (int)FXSYS_ceil(cell_bbox.Width());
    929     int height = (int)FXSYS_ceil(cell_bbox.Height());
    930     if (width == 0) {
    931         width = 1;
    932     }
    933     if (height == 0) {
    934         height = 1;
    935     }
    936     int min_col, max_col, min_row, max_row;
    937     CFX_AffineMatrix mtDevice2Pattern;
    938     mtDevice2Pattern.SetReverse(mtPattern2Device);
    939     CFX_FloatRect clip_box_p(clip_box);
    940     clip_box_p.Transform(&mtDevice2Pattern);
    941     min_col = (int)FXSYS_ceil(FXSYS_Div(clip_box_p.left - pPattern->m_BBox.right, pPattern->m_XStep));
    942     max_col = (int)FXSYS_floor(FXSYS_Div(clip_box_p.right - pPattern->m_BBox.left, pPattern->m_XStep));
    943     min_row = (int)FXSYS_ceil(FXSYS_Div(clip_box_p.bottom - pPattern->m_BBox.top, pPattern->m_YStep));
    944     max_row = (int)FXSYS_floor(FXSYS_Div(clip_box_p.top - pPattern->m_BBox.bottom, pPattern->m_YStep));
    945     if (width > clip_box.Width() || height > clip_box.Height() || width * height > clip_box.Width() * clip_box.Height()) {
    946         CPDF_GraphicStates* pStates = NULL;
    947         if (!pPattern->m_bColored) {
    948             pStates = CloneObjStates(pPageObj, bStroke);
    949         }
    950         CPDF_Dictionary* pFormResource = NULL;
    951         if (pPattern->m_pForm->m_pFormDict) {
    952             pFormResource = pPattern->m_pForm->m_pFormDict->GetDict(FX_BSTRC("Resources"));
    953         }
    954         for (int col = min_col; col <= max_col; col ++)
    955             for (int row = min_row; row <= max_row; row ++) {
    956                 FX_FLOAT orig_x, orig_y;
    957                 orig_x = col * pPattern->m_XStep;
    958                 orig_y = row * pPattern->m_YStep;
    959                 mtPattern2Device.Transform(orig_x, orig_y);
    960                 CFX_AffineMatrix matrix = *pObj2Device;
    961                 matrix.Translate(orig_x - mtPattern2Device.e, orig_y - mtPattern2Device.f);
    962                 m_pDevice->SaveState();
    963                 CPDF_RenderStatus status;
    964                 status.Initialize(m_Level + 1, m_pContext, m_pDevice, NULL, NULL, this, pStates, &m_Options,
    965                                   pPattern->m_pForm->m_Transparency, m_bDropObjects, pFormResource);
    966                 status.RenderObjectList(pPattern->m_pForm, &matrix);
    967                 m_pDevice->RestoreState();
    968             }
    969         m_pDevice->RestoreState();
    970         if (pStates) {
    971             delete pStates;
    972         }
    973         return;
    974     }
    975     if (bAligned) {
    976         int orig_x = FXSYS_round(mtPattern2Device.e);
    977         int orig_y = FXSYS_round(mtPattern2Device.f);
    978         min_col = (clip_box.left - orig_x) / width;
    979         if (clip_box.left < orig_x) {
    980             min_col --;
    981         }
    982         max_col = (clip_box.right - orig_x) / width;
    983         if (clip_box.right <= orig_x) {
    984             max_col --;
    985         }
    986         min_row = (clip_box.top - orig_y) / height;
    987         if (clip_box.top < orig_y) {
    988             min_row --;
    989         }
    990         max_row = (clip_box.bottom - orig_y) / height;
    991         if (clip_box.bottom <= orig_y) {
    992             max_row --;
    993         }
    994     }
    995     FX_FLOAT left_offset = cell_bbox.left - mtPattern2Device.e;
    996     FX_FLOAT top_offset = cell_bbox.bottom - mtPattern2Device.f;
    997     CFX_DIBitmap* pPatternBitmap = NULL;
    998     if (width * height < 16) {
    999         CFX_DIBitmap* pEnlargedBitmap = DrawPatternBitmap(m_pContext->m_pDocument, m_pContext->m_pPageCache, pPattern, pObj2Device, 8, 8, m_Options.m_Flags);
   1000         pPatternBitmap = pEnlargedBitmap->StretchTo(width, height);
   1001         delete pEnlargedBitmap;
   1002     } else {
   1003         pPatternBitmap = DrawPatternBitmap(m_pContext->m_pDocument, m_pContext->m_pPageCache, pPattern, pObj2Device, width, height, m_Options.m_Flags);
   1004     }
   1005     if (pPatternBitmap == NULL) {
   1006         m_pDevice->RestoreState();
   1007         return;
   1008     }
   1009     if (m_Options.m_ColorMode == RENDER_COLOR_GRAY) {
   1010         pPatternBitmap->ConvertColorScale(m_Options.m_ForeColor, m_Options.m_BackColor);
   1011     }
   1012     FX_ARGB fill_argb = GetFillArgb(pPageObj);
   1013     int clip_width = clip_box.right - clip_box.left;
   1014     int clip_height = clip_box.bottom - clip_box.top;
   1015     CFX_DIBitmap screen;
   1016     if (!screen.Create(clip_width, clip_height, FXDIB_Argb)) {
   1017         return;
   1018     }
   1019     screen.Clear(0);
   1020     FX_DWORD* src_buf = (FX_DWORD*)pPatternBitmap->GetBuffer();
   1021     for (int col = min_col; col <= max_col; col ++) {
   1022         for (int row = min_row; row <= max_row; row ++) {
   1023             int start_x, start_y;
   1024             if (bAligned) {
   1025                 start_x = FXSYS_round(mtPattern2Device.e) + col * width - clip_box.left;
   1026                 start_y = FXSYS_round(mtPattern2Device.f) + row * height - clip_box.top;
   1027             } else {
   1028                 FX_FLOAT orig_x = col * pPattern->m_XStep;
   1029                 FX_FLOAT orig_y = row * pPattern->m_YStep;
   1030                 mtPattern2Device.Transform(orig_x, orig_y);
   1031                 start_x = FXSYS_round(orig_x + left_offset) - clip_box.left;
   1032                 start_y = FXSYS_round(orig_y + top_offset) - clip_box.top;
   1033             }
   1034             if (width == 1 && height == 1) {
   1035                 if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 || start_y >= clip_box.Height()) {
   1036                     continue;
   1037                 }
   1038                 FX_DWORD* dest_buf = (FX_DWORD*)(screen.GetBuffer() + screen.GetPitch() * start_y + start_x * 4);
   1039                 if (pPattern->m_bColored) {
   1040                     *dest_buf = *src_buf;
   1041                 } else {
   1042                     *dest_buf = (*(FX_LPBYTE)src_buf << 24) | (fill_argb & 0xffffff);
   1043                 }
   1044             } else {
   1045                 if (pPattern->m_bColored) {
   1046                     screen.CompositeBitmap(start_x, start_y, width, height, pPatternBitmap, 0, 0);
   1047                 } else {
   1048                     screen.CompositeMask(start_x, start_y, width, height, pPatternBitmap, fill_argb, 0, 0);
   1049                 }
   1050             }
   1051         }
   1052     }
   1053     CompositeDIBitmap(&screen, clip_box.left, clip_box.top, 0, 255, FXDIB_BLEND_NORMAL, FALSE);
   1054     m_pDevice->RestoreState();
   1055     delete pPatternBitmap;
   1056 }
   1057 void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* pPathObj, const CFX_AffineMatrix* pObj2Device, CPDF_Color* pColor, FX_BOOL bStroke)
   1058 {
   1059     CPDF_Pattern* pattern = pColor->GetPattern();
   1060     if (pattern == NULL) {
   1061         return;
   1062     }
   1063     if(pattern->m_PatternType == PATTERN_TILING) {
   1064         DrawTilingPattern((CPDF_TilingPattern*)pattern, pPathObj, pObj2Device, bStroke);
   1065     } else {
   1066         DrawShadingPattern((CPDF_ShadingPattern*)pattern, pPathObj, pObj2Device, bStroke);
   1067     }
   1068 }
   1069 void CPDF_RenderStatus::ProcessPathPattern(CPDF_PathObject* pPathObj, const CFX_AffineMatrix* pObj2Device, int& filltype, FX_BOOL& bStroke)
   1070 {
   1071     FX_BOOL bPattern = FALSE;
   1072     if(filltype) {
   1073         CPDF_Color& FillColor = *pPathObj->m_ColorState.GetFillColor();
   1074         if(FillColor.m_pCS && FillColor.m_pCS->GetFamily() == PDFCS_PATTERN) {
   1075             DrawPathWithPattern(pPathObj, pObj2Device, &FillColor, FALSE);
   1076             filltype = 0;
   1077             bPattern = TRUE;
   1078         }
   1079     }
   1080     if(bStroke) {
   1081         CPDF_Color& StrokeColor = *pPathObj->m_ColorState.GetStrokeColor();
   1082         if(StrokeColor.m_pCS && StrokeColor.m_pCS->GetFamily() == PDFCS_PATTERN) {
   1083             DrawPathWithPattern(pPathObj, pObj2Device, &StrokeColor, TRUE);
   1084             bStroke = FALSE;
   1085             bPattern = TRUE;
   1086         }
   1087     }
   1088 #ifdef _FPDFAPI_MINI_
   1089     if (bPattern && m_DitherBits) {
   1090         DitherObjectArea(pPathObj, pObj2Device);
   1091     }
   1092 #endif
   1093 }
   1094