void MyCanvas::shadeRect(const GRect& rect, GShader* shader) { assert(shader != nullptr); if (rect.isEmpty()) { printf("Error: ShadeRect rectangle is empty\n"); return; } /* Convert the rectangle into points, then CTM the resulting points */ auto Points = Utility::RectToPoints(rect); CTMPoints(Points); if ( !CTM.preservesRect()) //If the CTM does not preserve a rectangle, draw a polygon { auto Edges = pointsToEdges(Points); shadeDevicePolygon(Edges, shader); return; } //Convert the CTM'd points back into a rect since we know it preserves it GIRect ConvertedRect = Utility::PointsToRect(Points).round(); // Make sure rect is not an empty one and clip the edges from the bitmap with intersect if (ConvertedRect.isEmpty() || !ConvertedRect.intersect(BmpRect)) { return; } shadeDeviceRect(ConvertedRect, shader); }
void MyCanvas::shadeDeviceRect(const GIRect& rect, GShader* shader) { assert(shader != nullptr); if (rect.isEmpty()) { printf("Error: ShadeDeviceRect rect is empty can't draw\n"); return; } // Get dst bitmap address and offset to the rect top GPixel* DstPixels = (GPixel*)((char*)Bitmap.pixels() + Bitmap.fRowBytes * rect.top()); // Set the context of the shader to the CTM float TwoRows[6]; CTM.GetTwoRows(TwoRows); shader->setContext(TwoRows); //All rows will have the same number of pixels so we can allocate once for the whole rect int count = rect.width(); GPixel* row = new GPixel[count]; for (int y = rect.top(); y < rect.bottom(); ++y) { shader->shadeRow(rect.left(), y, count, row); blendRow(DstPixels, rect.left(), row, count); DstPixels = (GPixel*)((char*)DstPixels + Bitmap.fRowBytes); } delete[] row; }
// This code draws a bitmap using the full m_CTM transform without any thought // to whether or not the transform has any special properties. void drawBitmapXForm(const GBitmap &bm, const GPaint &paint) { const GBitmap &ctxbm = GetInternalBitmap(); GRect ctxRect = GRect::MakeXYWH(0, 0, ctxbm.width(), ctxbm.height()); GIRect bmRect = GIRect::MakeXYWH(0, 0, bm.width(), bm.height()); GRect pixelRect = GetTransformedBoundingBox(bmRect); GRect rect; if(!(rect.setIntersection(ctxRect, pixelRect))) { return; } // Rein everything back into integer land GIRect dstRect = rect.round(); if(dstRect.isEmpty()) { return; } float alpha = paint.getAlpha(); if(alpha >= kOpaqueAlpha) { for(uint32_t j = 0; j < dstRect.height(); j++) { for(uint32_t i = 0; i < dstRect.width(); i++) { drawXFormPixel(i, j, dstRect, bmRect, bm, ctxbm); } } } else { const uint32_t alphaVal = static_cast<uint32_t>((alpha * 255.0f) + 0.5f); for(uint32_t j = 0; j < dstRect.height(); j++) { for(uint32_t i = 0; i < dstRect.width(); i++) { drawXFormPixelWithAlpha(i, j, dstRect, bmRect, bm, ctxbm, alphaVal); } } } }
void drawRect(const GRect &rect, const GPaint &p) { const GBitmap &ctxbm = GetInternalBitmap(); GRect ctxRect = GRect::MakeXYWH(0, 0, ctxbm.width(), ctxbm.height()); GRect pixelRect = GetTransformedBoundingBox(rect); if(pixelRect.isEmpty()) { return; } GRect trRect; if(!(trRect.setIntersection(ctxRect, pixelRect))) { return; } // Rein everything back into integer land GIRect dstRect = trRect.round(); if(dstRect.isEmpty()) { return; } if(!(CheckSkew(m_CTM))) { fillIRect(dstRect, p.getColor(), eBlendOp_SrcOver); return; } GPixel clearValue = ColorToPixel(p.getColor()); // If the alpha value is above this value, then it will round to // an opaque pixel during quantization. const float kOpaqueAlpha = (254.5f / 255.0f); float alpha = p.getAlpha(); // Blend func is currently just srcover BlendFunc blend = blend_srcover; for(uint32_t j = 0; j < dstRect.height(); j++) { for(uint32_t i = 0; i < dstRect.width(); i++) { GVec3f ctxPt(static_cast<float>(dstRect.fLeft + i) + 0.5f, static_cast<float>(dstRect.fTop + j) + 0.5f, 1.0f); ctxPt = m_CTMInv * ctxPt; if(ContainsPoint(rect, ctxPt[0], ctxPt[1])) { uint32_t x = static_cast<uint32_t>(ctxPt[0] - rect.fLeft); uint32_t y = static_cast<uint32_t>(ctxPt[1] - rect.fTop); GPixel *dstRow = GetRow(ctxbm, j+dstRect.fTop) + dstRect.fLeft; dstRow[i] = blend(dstRow[i], clearValue); } } } }