void UPnpCDSVideo::PopulateArtworkURIS(CDSObject* pItem, int nVidID, const QUrl& URIBase) { QUrl artURI = URIBase; artURI.setPath("/Content/GetVideoArtwork"); QUrlQuery artQuery; artQuery.addQueryItem("Id", QString::number(nVidID)); artURI.setQuery(artQuery); // Prefer JPEG over PNG here, although PNG is allowed JPEG probably // has wider device support and crucially the filesizes are smaller // which speeds up loading times over the network // We MUST include the thumbnail size, but since some clients may use the // first image they see and the thumbnail is tiny, instead return the // medium first. The large could be very large, which is no good if the // client is pulling images for an entire list at once! // Thumbnail // At least one albumArtURI must be a ThumbNail (TN) no larger // than 160x160, and it must also be a jpeg QUrl thumbURI = artURI; QUrlQuery thumbQuery(thumbURI.query()); if (pItem->m_sClass == "object.item.videoItem") // Show screenshot for TV, coverart for movies thumbQuery.addQueryItem("Type", "screenshot"); else thumbQuery.addQueryItem("Type", "coverart"); thumbQuery.addQueryItem("Width", "160"); thumbQuery.addQueryItem("Height", "160"); thumbURI.setQuery(thumbQuery); // Small // Must be no more than 640x480 QUrl smallURI = artURI; QUrlQuery smallQuery(smallURI.query()); smallQuery.addQueryItem("Type", "coverart"); smallQuery.addQueryItem("Width", "640"); smallQuery.addQueryItem("Height", "480"); smallURI.setQuery(smallQuery); // Medium // Must be no more than 1024x768 QUrl mediumURI = artURI; QUrlQuery mediumQuery(mediumURI.query()); mediumQuery.addQueryItem("Type", "coverart"); mediumQuery.addQueryItem("Width", "1024"); mediumQuery.addQueryItem("Height", "768"); mediumURI.setQuery(mediumQuery); // Large // Must be no more than 4096x4096 - for our purposes, just return // a fullsize image QUrl largeURI = artURI; QUrlQuery largeQuery(largeURI.query()); largeQuery.addQueryItem("Type", "fanart"); largeURI.setQuery(largeQuery); QList<Property*> propList = pItem->GetProperties("albumArtURI"); if (propList.size() >= 4) { Property *pProp = propList.at(0); if (pProp) { pProp->SetValue(mediumURI.toEncoded()); pProp->AddAttribute("dlna:profileID", "JPEG_MED"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } pProp = propList.at(1); if (pProp) { pProp->SetValue(thumbURI.toEncoded()); pProp->AddAttribute("dlna:profileID", "JPEG_TN"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } pProp = propList.at(2); if (pProp) { pProp->SetValue(smallURI.toEncoded()); pProp->AddAttribute("dlna:profileID", "JPEG_SM"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } pProp = propList.at(3); if (pProp) { pProp->SetValue(largeURI.toEncoded()); pProp->AddAttribute("dlna:profileID", "JPEG_LRG"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } } if (pItem->m_sClass.startsWith("object.item.videoItem")) { QString sProtocol; sProtocol = DLNA::ProtocolInfoString(UPNPProtocol::kHTTP, "image/jpeg", QSize(1024, 768)); pItem->AddResource( sProtocol, mediumURI.toEncoded()); sProtocol = DLNA::ProtocolInfoString(UPNPProtocol::kHTTP, "image/jpeg", QSize(160, 160)); pItem->AddResource( sProtocol, thumbURI.toEncoded()); sProtocol = DLNA::ProtocolInfoString(UPNPProtocol::kHTTP, "image/jpeg", QSize(640, 480)); pItem->AddResource( sProtocol, smallURI.toEncoded()); sProtocol = DLNA::ProtocolInfoString(UPNPProtocol::kHTTP, "image/jpeg", QSize(1920, 1080)); // Not the actual res, we don't know that pItem->AddResource( sProtocol, largeURI.toEncoded()); } }
Vec3f PhotonTracer::traceSample(Vec2u pixel, const KdTree<Photon> &surfaceTree, const KdTree<VolumePhoton> *mediumTree, PathSampleGenerator &sampler, float gatherRadius) { PositionSample point; if (!_scene->cam().samplePosition(sampler, point)) return Vec3f(0.0f); DirectionSample direction; if (!_scene->cam().sampleDirection(sampler, point, pixel, direction)) return Vec3f(0.0f); sampler.advancePath(); Vec3f throughput = point.weight*direction.weight; Ray ray(point.p, direction.d); ray.setPrimaryRay(true); IntersectionTemporary data; IntersectionInfo info; const Medium *medium = _scene->cam().medium().get(); Vec3f result(0.0f); int bounce = 0; bool didHit = _scene->intersect(ray, data, info); while ((medium || didHit) && bounce < _settings.maxBounces - 1) { if (medium) { if (mediumTree) { Vec3f beamEstimate(0.0f); mediumTree->beamQuery(ray.pos(), ray.dir(), ray.farT(), [&](const VolumePhoton &p, float t, float distSq) { Ray mediumQuery(ray); mediumQuery.setFarT(t); beamEstimate += (3.0f*INV_PI*sqr(1.0f - distSq/p.radiusSq))/p.radiusSq *medium->phaseFunction(p.pos)->eval(ray.dir(), -p.dir) *medium->transmittance(mediumQuery)*p.power; }); result += throughput*beamEstimate; } throughput *= medium->transmittance(ray); } if (!didHit) break; const Bsdf &bsdf = *info.bsdf; SurfaceScatterEvent event = makeLocalScatterEvent(data, info, ray, &sampler); Vec3f transparency = bsdf.eval(event.makeForwardEvent(), false); float transparencyScalar = transparency.avg(); Vec3f wo; if (sampler.nextBoolean(DiscreteTransparencySample, transparencyScalar)) { wo = ray.dir(); throughput *= transparency/transparencyScalar; } else { event.requestedLobe = BsdfLobes::SpecularLobe; if (!bsdf.sample(event, false)) break; wo = event.frame.toGlobal(event.wo); throughput *= event.weight; } bool geometricBackside = (wo.dot(info.Ng) < 0.0f); medium = info.primitive->selectMedium(medium, geometricBackside); ray = ray.scatter(ray.hitpoint(), wo, info.epsilon); if (std::isnan(ray.dir().sum() + ray.pos().sum())) break; if (std::isnan(throughput.sum())) break; sampler.advancePath(); bounce++; if (bounce < _settings.maxBounces) didHit = _scene->intersect(ray, data, info); } if (!didHit) { if (!medium && _scene->intersectInfinites(ray, data, info)) result += throughput*info.primitive->evalDirect(data, info); return result; } if (info.primitive->isEmissive()) result += throughput*info.primitive->evalDirect(data, info); int count = surfaceTree.nearestNeighbours(ray.hitpoint(), _photonQuery.get(), _distanceQuery.get(), _settings.gatherCount, gatherRadius); if (count == 0) return result; const Bsdf &bsdf = *info.bsdf; SurfaceScatterEvent event = makeLocalScatterEvent(data, info, ray, &sampler); Vec3f surfaceEstimate(0.0f); for (int i = 0; i < count; ++i) { event.wo = event.frame.toLocal(-_photonQuery[i]->dir); // Asymmetry due to shading normals already compensated for when storing the photon, // so we don't use the adjoint BSDF here surfaceEstimate += _photonQuery[i]->power*bsdf.eval(event, false)/std::abs(event.wo.z()); } float radiusSq = count == int(_settings.gatherCount) ? _distanceQuery[0] : gatherRadius*gatherRadius; result += throughput*surfaceEstimate*(INV_PI/radiusSq); return result; }