// 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); } } } }
// This code draws a bitmap assuming that we only have translation and scale, // which allows us to perform certain optimizations... void drawBitmapSimple(const GBitmap &bm, const GPaint &paint) { const GBitmap &ctxbm = GetInternalBitmap(); GRect ctxRect = GRect::MakeXYWH(0, 0, ctxbm.width(), ctxbm.height()); GRect bmRect = GRect::MakeXYWH(0, 0, bm.width(), bm.height()); GRect pixelRect = GetTransformedBoundingBox(bmRect); GRect rect; if(!(rect.setIntersection(ctxRect, pixelRect))) { return; } // We know that since we're only doing scale and translation, that all of the pixel // centers contained in rect are going to be drawn, so we only need to know the // dimensions of the mapping... GVec3f origin(0, 0, 1); GVec3f offset(1, 1, 1); origin = m_CTM * origin; offset = m_CTM * offset; float xScale = 1.0f / (offset.X() - origin.X()); float yScale = 1.0f / (offset.Y() - origin.Y()); GVec2f start = GVec2f(0, 0); if(xScale < 0.0f) { start.X() = pixelRect.fRight - 1.0f; } if(yScale < 0.0f) { start.Y() = pixelRect.fBottom - 1.0f; } GIRect dstRect = rect.round(); // Construct new bitmap int32_t offsetX = ::std::max(0, -dstRect.fLeft); int32_t offsetY = ::std::max(0, -dstRect.fTop); GBitmap fbm; fbm.fWidth = bm.width(); fbm.fHeight = bm.height(); fbm.fPixels = GetRow(bm, offsetY) + offsetX; fbm.fRowBytes = bm.fRowBytes; BlendFunc blend = blend_srcover; float alpha = paint.getAlpha(); if(alpha >= kOpaqueAlpha) { for(uint32_t j = 0; j < dstRect.height(); j++) { uint32_t srcIdxY = static_cast<uint32_t>(start.Y() + static_cast<float>(j) * yScale); GPixel *srcPixels = GetRow(fbm, Clamp<int>(srcIdxY, 0, fbm.height())); GPixel *dstPixels = GetRow(ctxbm, dstRect.fTop + j) + dstRect.fLeft; for(uint32_t i = 0; i < dstRect.width(); i++) { uint32_t srcIdxX = static_cast<uint32_t>(start.X() + static_cast<float>(i) * xScale); dstPixels[i] = blend(dstPixels[i], srcPixels[Clamp<int>(srcIdxX, 0, fbm.width())]); } } } else { const uint32_t alphaVal = static_cast<uint32_t>((alpha * 255.0f) + 0.5f); for(uint32_t j = 0; j < dstRect.height(); j++) { uint32_t srcIdxY = static_cast<uint32_t>(start.Y() + static_cast<float>(j) * yScale); GPixel *srcPixels = GetRow(fbm, srcIdxY); GPixel *dstPixels = GetRow(ctxbm, dstRect.fTop + j) + dstRect.fLeft; for(uint32_t i = 0; i < dstRect.width(); i++) { uint32_t srcIdxX = static_cast<uint32_t>(start.X() + static_cast<float>(i) * xScale); uint32_t srcA = fixed_multiply(GPixel_GetA(srcPixels[srcIdxX]), alphaVal); uint32_t srcR = fixed_multiply(GPixel_GetR(srcPixels[srcIdxX]), alphaVal); uint32_t srcG = fixed_multiply(GPixel_GetG(srcPixels[srcIdxX]), alphaVal); uint32_t srcB = fixed_multiply(GPixel_GetB(srcPixels[srcIdxX]), alphaVal); GPixel src = GPixel_PackARGB(srcA, srcR, srcG, srcB); dstPixels[i] = blend(dstPixels[i], src); } } } }