// determine the kind of transparency we need for this image: if the only alpha // values it has are 0 (transparent) and 0xff (opaque) then we can simply // create a mask for it, we should be ok with a simple mask but otherwise we // need a full blown alpha channel in wxImage // // parameters: // lines raw PNG data // x, y starting position // w, h size of the image // numColBytes number of colour bytes (1 for grey scale, 3 for RGB) // (NB: alpha always follows the colour bytes) Transparency CheckTransparency(unsigned char **lines, png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h, size_t numColBytes) { // suppose that a mask will suffice and check all the remaining alpha // values to see if it does for ( ; y < h; y++ ) { // each pixel is numColBytes+1 bytes, offset into the current line by // the current x position unsigned const char *ptr = lines[y] + (x * (numColBytes + 1)); for ( png_uint_32 x2 = x; x2 < w; x2++ ) { // skip the grey or colour byte(s) ptr += numColBytes; unsigned char a2 = *ptr++; if ( !IsTransparent(a2) && !IsOpaque(a2) ) { // not fully opaque nor fully transparent, hence need alpha return Transparency_Alpha; } } // during the next loop iteration check all the pixels in the row x = 0; } // mask will be enough return Transparency_Mask; }
DrawTargetCaptureImpl::DrawTargetCaptureImpl(BackendType aBackend, const IntSize& aSize, SurfaceFormat aFormat) : mSize(aSize), mSnapshot(nullptr), mStride(0), mSurfaceAllocationSize(0), mFlushBytes(0) { RefPtr<DrawTarget> screenRefDT = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); mFormat = aFormat; SetPermitSubpixelAA(IsOpaque(mFormat)); if (aBackend == screenRefDT->GetBackendType()) { mRefDT = screenRefDT; } else { // This situation can happen if a blur operation decides to // use an unaccelerated path even if the system backend is // Direct2D. // // We don't really want to encounter the reverse scenario: // we shouldn't pick an accelerated backend if the system // backend is skia. if (aBackend == BackendType::DIRECT2D1_1) { gfxWarning() << "Creating a RefDT in DrawTargetCapture."; } // Create a 1x1 size ref dt to create assets // If we have to snapshot, we'll just create the real DT IntSize size(1, 1); mRefDT = Factory::CreateDrawTarget(aBackend, size, mFormat); } }
RasterImage::WillDrawOpaqueNow() { if (!IsOpaque()) { return false; } if (mAnimationState) { // We never discard frames of animated images. return true; } // If we are not locked our decoded data could get discard at any time (ie // between the call to this function and when we are asked to draw), so we // have to return false if we are unlocked. if (IsUnlocked()) { return false; } LookupResult result = SurfaceCache::LookupBestMatch(ImageKey(this), RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eStatic)); MatchType matchType = result.Type(); if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING || !result.Surface()->IsFinished()) { return false; } return true; }
// Suitable for leaf items only, overridden by nsDisplayWrapList PRBool nsDisplayItem::OptimizeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) { nsRect bounds = GetBounds(aBuilder); if (!aVisibleRegion->Intersects(bounds)) return PR_FALSE; nsIFrame* f = GetUnderlyingFrame(); NS_ASSERTION(f, "GetUnderlyingFrame() must return non-null for leaf items"); PRBool isMoving = aBuilder->IsMovingFrame(f); if (IsOpaque(aBuilder)) { nsRect opaqueArea = bounds; if (isMoving) { // The display list should include items for both the before and after // states (see nsLayoutUtils::ComputeRepaintRegionForCopy. So the // only area we want to cover is the the area that was opaque in the // before state and in the after state. opaqueArea.IntersectRect(bounds - aBuilder->GetMoveDelta(), bounds); } aVisibleRegion->SimpleSubtract(opaqueArea); } return PR_TRUE; }
void Container::Draw(GraphicsPtr graphics) { if (IsOpaque()) { graphics->setColor(getBaseColor()); graphics->fillRectangle(Rectangle(0, 0, GetWidth(), GetHeight())); } DrawChildren(graphics); }
bool DrawTargetCaptureImpl::Init(const IntSize& aSize, DrawTarget* aRefDT) { if (!aRefDT) { return false; } mRefDT = aRefDT; mSize = aSize; mFormat = aRefDT->GetFormat(); SetPermitSubpixelAA(IsOpaque(mFormat)); return true; }
// convert data from RGB to wxImage format static void CopyDataFromPNG(wxImage *image, unsigned char **lines, png_uint_32 width, png_uint_32 height, int color_type) { Transparency transparency = Transparency_None; // only non NULL if transparency == Transparency_Alpha unsigned char *alpha = NULL; // RGB of the mask colour if transparency == Transparency_Mask // (but init them anyhow to avoid compiler warnings) unsigned char rMask = 0, gMask = 0, bMask = 0; unsigned char *ptrDst = image->GetData(); if ( !(color_type & PNG_COLOR_MASK_COLOR) ) { // grey image: GAGAGA... where G == grey component and A == alpha for ( png_uint_32 y = 0; y < height; y++ ) { const unsigned char *ptrSrc = lines[y]; for ( png_uint_32 x = 0; x < width; x++ ) { unsigned char g = *ptrSrc++; unsigned char a = *ptrSrc++; // the first time we encounter a transparent pixel we must // decide about what to do about them if ( !IsOpaque(a) && transparency == Transparency_None ) { // we'll need at least the mask for this image and // maybe even full alpha channel info: the former is // only enough if we have alpha values of 0 and 0xff // only, otherwisewe need the latter transparency = CheckTransparency ( lines, x, y, width, height, 1 ); if ( transparency == Transparency_Mask ) { // let's choose this colour for the mask: this is // not a problem here as all the other pixels are // grey, i.e. R == G == B which is not the case for // this one so no confusion is possible rMask = 0xff; gMask = 0; bMask = 0xff; } else // transparency == Transparency_Alpha { alpha = InitAlpha(image, x, y); } } switch ( transparency ) { case Transparency_Mask: if ( IsTransparent(a) ) { *ptrDst++ = rMask; *ptrDst++ = gMask; *ptrDst++ = bMask; break; } // else: !transparent // must be opaque then as otherwise we shouldn't be // using the mask at all wxASSERT_MSG( IsOpaque(a), wxT("logic error") ); // fall through case Transparency_Alpha: if ( alpha ) *alpha++ = a; // fall through case Transparency_None: *ptrDst++ = g; *ptrDst++ = g; *ptrDst++ = g; break; } } } } else // colour image: RGBRGB... { for ( png_uint_32 y = 0; y < height; y++ ) { const unsigned char *ptrSrc = lines[y]; for ( png_uint_32 x = 0; x < width; x++ ) { unsigned char r = *ptrSrc++; unsigned char g = *ptrSrc++; unsigned char b = *ptrSrc++; unsigned char a = *ptrSrc++; // the logic here is the same as for the grey case except // where noted if ( !IsOpaque(a) && transparency == Transparency_None ) { transparency = CheckTransparency ( lines, x, y, width, height, 3 ); if ( transparency == Transparency_Mask ) { FindMaskColour(lines, width, height, rMask, gMask, bMask); } else // transparency == Transparency_Alpha { alpha = InitAlpha(image, x, y); } } switch ( transparency ) { case Transparency_Mask: if ( IsTransparent(a) ) { *ptrDst++ = rMask; *ptrDst++ = gMask; *ptrDst++ = bMask; break; } else // !transparent { // must be opaque then as otherwise we shouldn't be // using the mask at all wxASSERT_MSG( IsOpaque(a), wxT("logic error") ); // if we couldn't find a unique colour for the // mask, we can have real pixels with the same // value as the mask and it's better to slightly // change their colour than to make them // transparent if ( r == rMask && g == gMask && b == bMask ) { r++; } } // fall through case Transparency_Alpha: if ( alpha ) *alpha++ = a; // fall through case Transparency_None: *ptrDst++ = r; *ptrDst++ = g; *ptrDst++ = b; break; } } } } if ( transparency == Transparency_Mask ) { image->SetMaskColour(rMask, gMask, bMask); } }
void GSRendererDX::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) { GSDrawingEnvironment& env = m_env; GSDrawingContext* context = m_context; const GSVector2i& rtsize = rt->GetSize(); const GSVector2& rtscale = rt->GetScale(); bool DATE = m_context->TEST.DATE && context->FRAME.PSM != PSM_PSMCT24; GSTexture* rtcopy = NULL; ASSERT(m_dev != NULL); GSDeviceDX* dev = (GSDeviceDX*)m_dev; if(DATE) { if(dev->HasStencil()) { GSVector4 s = GSVector4(rtscale.x / rtsize.x, rtscale.y / rtsize.y); GSVector4 o = GSVector4(-1.0f, 1.0f); GSVector4 src = ((m_vt.m_min.p.xyxy(m_vt.m_max.p) + o.xxyy()) * s.xyxy()).sat(o.zzyy()); GSVector4 dst = src * 2.0f + o.xxxx(); GSVertexPT1 vertices[] = { {GSVector4(dst.x, -dst.y, 0.5f, 1.0f), GSVector2(src.x, src.y)}, {GSVector4(dst.z, -dst.y, 0.5f, 1.0f), GSVector2(src.z, src.y)}, {GSVector4(dst.x, -dst.w, 0.5f, 1.0f), GSVector2(src.x, src.w)}, {GSVector4(dst.z, -dst.w, 0.5f, 1.0f), GSVector2(src.z, src.w)}, }; dev->SetupDATE(rt, ds, vertices, m_context->TEST.DATM); } else { rtcopy = dev->CreateRenderTarget(rtsize.x, rtsize.y, false, rt->GetFormat()); // I'll use VertexTrace when I consider it more trustworthy dev->CopyRect(rt, rtcopy, GSVector4i(rtsize).zwxy()); } } // dev->BeginScene(); // om GSDeviceDX::OMDepthStencilSelector om_dssel; if(context->TEST.ZTE) { om_dssel.ztst = context->TEST.ZTST; om_dssel.zwe = !context->ZBUF.ZMSK; } else { om_dssel.ztst = ZTST_ALWAYS; } if(m_fba) { om_dssel.fba = context->FBA.FBA; } GSDeviceDX::OMBlendSelector om_bsel; if(!IsOpaque()) { om_bsel.abe = PRIM->ABE || PRIM->AA1 && m_vt.m_primclass == GS_LINE_CLASS; om_bsel.a = context->ALPHA.A; om_bsel.b = context->ALPHA.B; om_bsel.c = context->ALPHA.C; om_bsel.d = context->ALPHA.D; if(env.PABE.PABE) { if(om_bsel.a == 0 && om_bsel.b == 1 && om_bsel.c == 0 && om_bsel.d == 1) { // this works because with PABE alpha blending is on when alpha >= 0x80, but since the pixel shader // cannot output anything over 0x80 (== 1.0) blending with 0x80 or turning it off gives the same result om_bsel.abe = 0; } else { //Breath of Fire Dragon Quarter triggers this in battles. Graphics are fine though. //ASSERT(0); } } } om_bsel.wrgba = ~GSVector4i::load((int)context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask(); // vs GSDeviceDX::VSSelector vs_sel; vs_sel.tme = PRIM->TME; vs_sel.fst = PRIM->FST; vs_sel.logz = dev->HasDepth32() ? 0 : m_logz ? 1 : 0; vs_sel.rtcopy = !!rtcopy; // The real GS appears to do no masking based on the Z buffer format and writing larger Z values // than the buffer supports seems to be an error condition on the real GS, causing it to crash. // We are probably receiving bad coordinates from VU1 in these cases. if(om_dssel.ztst >= ZTST_ALWAYS && om_dssel.zwe) { if(context->ZBUF.PSM == PSM_PSMZ24) { if(m_vt.m_max.p.z > 0xffffff) { ASSERT(m_vt.m_min.p.z > 0xffffff); // Fixme :Following conditional fixes some dialog frame in Wild Arms 3, but may not be what was intended. if (m_vt.m_min.p.z > 0xffffff) { vs_sel.bppz = 1; om_dssel.ztst = ZTST_ALWAYS; } } } else if(context->ZBUF.PSM == PSM_PSMZ16 || context->ZBUF.PSM == PSM_PSMZ16S) { if(m_vt.m_max.p.z > 0xffff) { ASSERT(m_vt.m_min.p.z > 0xffff); // sfex capcom logo // Fixme : Same as above, I guess. if (m_vt.m_min.p.z > 0xffff) { vs_sel.bppz = 2; om_dssel.ztst = ZTST_ALWAYS; } } } } GSDeviceDX::VSConstantBuffer vs_cb; float sx = 2.0f * rtscale.x / (rtsize.x << 4); float sy = 2.0f * rtscale.y / (rtsize.y << 4); float ox = (float)(int)context->XYOFFSET.OFX; float oy = (float)(int)context->XYOFFSET.OFY; float ox2 = 2.0f * m_pixelcenter.x / rtsize.x; float oy2 = 2.0f * m_pixelcenter.y / rtsize.y; //This hack subtracts around half a pixel from OFX and OFY. (Cannot do this directly, //because DX10 and DX9 have a different pixel center.) // //The resulting shifted output aligns better with common blending / corona / blurring effects, //but introduces a few bad pixels on the edges. if(rt->LikelyOffset) { // DX9 has pixelcenter set to 0.0, so give it some value here if(m_pixelcenter.x == 0 && m_pixelcenter.y == 0) { ox2 = -0.0003f; oy2 = -0.0003f; } ox2 *= rt->OffsetHack_modx; oy2 *= rt->OffsetHack_mody; } vs_cb.VertexScale = GSVector4(sx, -sy, ldexpf(1, -32), 0.0f); vs_cb.VertexOffset = GSVector4(ox * sx + ox2 + 1, -(oy * sy + oy2 + 1), 0.0f, -1.0f); // gs GSDeviceDX::GSSelector gs_sel; gs_sel.iip = PRIM->IIP; gs_sel.prim = m_vt.m_primclass; // ps GSDeviceDX::PSSelector ps_sel; GSDeviceDX::PSSamplerSelector ps_ssel; GSDeviceDX::PSConstantBuffer ps_cb; if(DATE) { if(dev->HasStencil()) { om_dssel.date = 1; } else { ps_sel.date = 1 + context->TEST.DATM; } } if(env.COLCLAMP.CLAMP == 0 && /* hack */ !tex && PRIM->PRIM != GS_POINTLIST) { ps_sel.colclip = 1; } ps_sel.clr1 = om_bsel.IsCLR1(); ps_sel.fba = context->FBA.FBA; ps_sel.aout = context->FRAME.PSM == PSM_PSMCT16 || context->FRAME.PSM == PSM_PSMCT16S || (context->FRAME.FBMSK & 0xff000000) == 0x7f000000 ? 1 : 0; if(UserHacks_AlphaHack) ps_sel.aout = 1; if(PRIM->FGE) { ps_sel.fog = 1; ps_cb.FogColor_AREF = GSVector4::rgba32(env.FOGCOL.u32[0]) / 255; } if(context->TEST.ATE) { ps_sel.atst = context->TEST.ATST; switch(ps_sel.atst) { case ATST_LESS: ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF - 1); break; case ATST_GREATER: ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF + 1); break; default: ps_cb.FogColor_AREF.a = (float)(int)context->TEST.AREF; break; } } else { ps_sel.atst = ATST_ALWAYS; } if(tex) { ps_sel.wms = context->CLAMP.WMS; ps_sel.wmt = context->CLAMP.WMT; ps_sel.fmt = tex->m_fmt; ps_sel.aem = env.TEXA.AEM; ps_sel.tfx = context->TEX0.TFX; ps_sel.tcc = context->TEX0.TCC; ps_sel.ltf = m_filter == 2 ? m_vt.IsLinear() : m_filter; ps_sel.rt = tex->m_target; int w = tex->m_texture->GetWidth(); int h = tex->m_texture->GetHeight(); int tw = (int)(1 << context->TEX0.TW); int th = (int)(1 << context->TEX0.TH); GSVector4 WH(tw, th, w, h); if(PRIM->FST) { vs_cb.TextureScale = GSVector4(1.0f / 16) / WH.xyxy(); //Maybe better? //vs_cb.TextureScale = GSVector4(1.0f / 16) * GSVector4(tex->m_texture->GetScale()).xyxy() / WH.zwzw(); ps_sel.fst = 1; } ps_cb.WH = WH; ps_cb.HalfTexel = GSVector4(-0.5f, 0.5f).xxyy() / WH.zwzw(); ps_cb.MskFix = GSVector4i(context->CLAMP.MINU, context->CLAMP.MINV, context->CLAMP.MAXU, context->CLAMP.MAXV); GSVector4 clamp(ps_cb.MskFix); GSVector4 ta(env.TEXA & GSVector4i::x000000ff()); ps_cb.MinMax = clamp / WH.xyxy(); ps_cb.MinF_TA = (clamp + 0.5f).xyxy(ta) / WH.xyxy(GSVector4(255, 255)); ps_ssel.tau = (context->CLAMP.WMS + 3) >> 1; ps_ssel.tav = (context->CLAMP.WMT + 3) >> 1; ps_ssel.ltf = ps_sel.ltf; } else {
// convert data from RGB to wxImage format static void CopyDataFromPNG(wxImage *image, unsigned char **lines, png_uint_32 width, png_uint_32 height, int color_type) { // allocated on demand if we have any non-opaque pixels unsigned char *alpha = NULL; unsigned char *ptrDst = image->GetData(); if ( !(color_type & PNG_COLOR_MASK_COLOR) ) { // grey image: GAGAGA... where G == grey component and A == alpha for ( png_uint_32 y = 0; y < height; y++ ) { const unsigned char *ptrSrc = lines[y]; for ( png_uint_32 x = 0; x < width; x++ ) { unsigned char g = *ptrSrc++; unsigned char a = *ptrSrc++; // the first time we encounter a transparent pixel we must // allocate alpha channel for the image if ( !IsOpaque(a) && !alpha ) alpha = InitAlpha(image, x, y); if ( alpha ) *alpha++ = a; *ptrDst++ = g; *ptrDst++ = g; *ptrDst++ = g; } } } else // colour image: RGBRGB... { for ( png_uint_32 y = 0; y < height; y++ ) { const unsigned char *ptrSrc = lines[y]; for ( png_uint_32 x = 0; x < width; x++ ) { unsigned char r = *ptrSrc++; unsigned char g = *ptrSrc++; unsigned char b = *ptrSrc++; unsigned char a = *ptrSrc++; // the logic here is the same as for the grey case if ( !IsOpaque(a) && !alpha ) alpha = InitAlpha(image, x, y); if ( alpha ) *alpha++ = a; *ptrDst++ = r; *ptrDst++ = g; *ptrDst++ = b; } } } }
DrawableSurface RasterImage::LookupFrame(const IntSize& aSize, uint32_t aFlags, PlaybackType aPlaybackType) { MOZ_ASSERT(NS_IsMainThread()); // If we're opaque, we don't need to care about premultiplied alpha, because // that can only matter for frames with transparency. if (IsOpaque()) { aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA; } IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags) ? aSize : mSize; if (requestedSize.IsEmpty()) { return DrawableSurface(); // Can't decode to a surface of zero size. } LookupResult result = LookupFrameInternal(requestedSize, aFlags, aPlaybackType); if (!result && !mHasSize) { // We can't request a decode without knowing our intrinsic size. Give up. return DrawableSurface(); } if (result.Type() == MatchType::NOT_FOUND || result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND || ((aFlags & FLAG_SYNC_DECODE) && !result)) { // We don't have a copy of this frame, and there's no decoder working on // one. (Or we're sync decoding and the existing decoder hasn't even started // yet.) Trigger decoding so it'll be available next time. MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated || !mAnimationState || mAnimationState->KnownFrameCount() < 1, "Animated frames should be locked"); Decode(requestedSize, aFlags, aPlaybackType); // If we can sync decode, we should already have the frame. if (aFlags & FLAG_SYNC_DECODE) { result = LookupFrameInternal(requestedSize, aFlags, aPlaybackType); } } if (!result) { // We still weren't able to get a frame. Give up. return DrawableSurface(); } if (result.Surface()->GetCompositingFailed()) { return DrawableSurface(); } MOZ_ASSERT(!result.Surface()->GetIsPaletted(), "Should not have a paletted frame"); // Sync decoding guarantees that we got the frame, but if it's owned by an // async decoder that's currently running, the contents of the frame may not // be available yet. Make sure we get everything. if (mHasSourceData && (aFlags & FLAG_SYNC_DECODE)) { result.Surface()->WaitUntilFinished(); } // If we could have done some decoding in this function we need to check if // that decoding encountered an error and hence aborted the surface. We want // to avoid calling IsAborted if we weren't passed any sync decode flag because // IsAborted acquires the monitor for the imgFrame. if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) && result.Surface()->IsAborted()) { return DrawableSurface(); } return Move(result.Surface()); }
NS_IMETHODIMP RasterImage::Decode(const IntSize& aSize, uint32_t aFlags, PlaybackType aPlaybackType) { MOZ_ASSERT(NS_IsMainThread()); if (mError) { return NS_ERROR_FAILURE; } // If we don't have a size yet, we can't do any other decoding. if (!mHasSize) { mWantFullDecode = true; return NS_OK; } // We're about to decode again, which may mean that some of the previous sizes // we've decoded at aren't useful anymore. We can allow them to expire from // the cache by unlocking them here. When the decode finishes, it will send an // invalidation that will cause all instances of this image to redraw. If this // image is locked, any surfaces that are still useful will become locked // again when LookupFrame touches them, and the remainder will eventually // expire. SurfaceCache::UnlockEntries(ImageKey(this)); // Determine which flags we need to decode this image with. DecoderFlags decoderFlags = DefaultDecoderFlags(); if (aFlags & FLAG_ASYNC_NOTIFY) { decoderFlags |= DecoderFlags::ASYNC_NOTIFY; } if (mTransient) { decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT; } if (mHasBeenDecoded) { decoderFlags |= DecoderFlags::IS_REDECODE; } SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags); if (IsOpaque()) { // If there's no transparency, it doesn't matter whether we premultiply // alpha or not. surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA; } // Create a decoder. RefPtr<IDecodingTask> task; if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) { task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, decoderFlags, surfaceFlags); } else { task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, aSize, decoderFlags, surfaceFlags, mRequestedSampleSize); } // Make sure DecoderFactory was able to create a decoder successfully. if (!task) { return NS_ERROR_FAILURE; } mDecodeCount++; // We're ready to decode; start the decoder. LaunchDecodingTask(task, this, aFlags, mHasSourceData); return NS_OK; }