Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 5 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "GrallocImages.h" |
michael@0 | 8 | #include <stddef.h> // for size_t |
michael@0 | 9 | #include <stdint.h> // for int8_t, uint8_t, uint32_t, etc |
michael@0 | 10 | #include "nsDebug.h" // for NS_WARNING, NS_PRECONDITION |
michael@0 | 11 | #include "mozilla/layers/ImageBridgeChild.h" |
michael@0 | 12 | #include "mozilla/layers/GrallocTextureClient.h" |
michael@0 | 13 | #include "gfx2DGlue.h" |
michael@0 | 14 | #include "YCbCrUtils.h" // for YCbCr conversions |
michael@0 | 15 | |
michael@0 | 16 | #include <ColorConverter.h> |
michael@0 | 17 | #include <OMX_IVCommon.h> |
michael@0 | 18 | |
michael@0 | 19 | |
michael@0 | 20 | using namespace mozilla::ipc; |
michael@0 | 21 | using namespace android; |
michael@0 | 22 | |
michael@0 | 23 | #define ALIGN(x, align) ((x + align - 1) & ~(align - 1)) |
michael@0 | 24 | |
michael@0 | 25 | namespace mozilla { |
michael@0 | 26 | namespace layers { |
michael@0 | 27 | |
michael@0 | 28 | uint32_t GrallocImage::sColorIdMap[] = { |
michael@0 | 29 | HAL_PIXEL_FORMAT_YCbCr_420_P, OMX_COLOR_FormatYUV420Planar, |
michael@0 | 30 | HAL_PIXEL_FORMAT_YCbCr_422_P, OMX_COLOR_FormatYUV422Planar, |
michael@0 | 31 | HAL_PIXEL_FORMAT_YCbCr_420_SP, OMX_COLOR_FormatYUV420SemiPlanar, |
michael@0 | 32 | HAL_PIXEL_FORMAT_YCrCb_420_SP, -1, |
michael@0 | 33 | HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO, -1, |
michael@0 | 34 | HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, |
michael@0 | 35 | HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, |
michael@0 | 36 | HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar, |
michael@0 | 37 | 0, 0 |
michael@0 | 38 | }; |
michael@0 | 39 | |
michael@0 | 40 | struct GraphicBufferAutoUnlock { |
michael@0 | 41 | android::sp<GraphicBuffer> mGraphicBuffer; |
michael@0 | 42 | |
michael@0 | 43 | GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer) |
michael@0 | 44 | : mGraphicBuffer(aGraphicBuffer) { } |
michael@0 | 45 | |
michael@0 | 46 | ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); } |
michael@0 | 47 | }; |
michael@0 | 48 | |
michael@0 | 49 | GrallocImage::GrallocImage() |
michael@0 | 50 | : PlanarYCbCrImage(nullptr) |
michael@0 | 51 | { |
michael@0 | 52 | mFormat = ImageFormat::GRALLOC_PLANAR_YCBCR; |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | GrallocImage::~GrallocImage() |
michael@0 | 56 | { |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | void |
michael@0 | 60 | GrallocImage::SetData(const Data& aData) |
michael@0 | 61 | { |
michael@0 | 62 | MOZ_ASSERT(!mTextureClient, "TextureClient is already set"); |
michael@0 | 63 | NS_PRECONDITION(aData.mYSize.width % 2 == 0, "Image should have even width"); |
michael@0 | 64 | NS_PRECONDITION(aData.mYSize.height % 2 == 0, "Image should have even height"); |
michael@0 | 65 | NS_PRECONDITION(aData.mYStride % 16 == 0, "Image should have stride of multiple of 16 pixels"); |
michael@0 | 66 | |
michael@0 | 67 | mData = aData; |
michael@0 | 68 | mSize = aData.mPicSize; |
michael@0 | 69 | |
michael@0 | 70 | if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) { |
michael@0 | 71 | // Emulator does not support HAL_PIXEL_FORMAT_YV12. |
michael@0 | 72 | return; |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | RefPtr<GrallocTextureClientOGL> textureClient = |
michael@0 | 76 | new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(), |
michael@0 | 77 | gfx::SurfaceFormat::UNKNOWN, |
michael@0 | 78 | gfx::BackendType::NONE); |
michael@0 | 79 | bool result = |
michael@0 | 80 | textureClient->AllocateGralloc(mData.mYSize, |
michael@0 | 81 | HAL_PIXEL_FORMAT_YV12, |
michael@0 | 82 | GraphicBuffer::USAGE_SW_READ_OFTEN | |
michael@0 | 83 | GraphicBuffer::USAGE_SW_WRITE_OFTEN | |
michael@0 | 84 | GraphicBuffer::USAGE_HW_TEXTURE); |
michael@0 | 85 | sp<GraphicBuffer> graphicBuffer = textureClient->GetGraphicBuffer(); |
michael@0 | 86 | if (!result || !graphicBuffer.get()) { |
michael@0 | 87 | mTextureClient = nullptr; |
michael@0 | 88 | return; |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | mTextureClient = textureClient; |
michael@0 | 92 | |
michael@0 | 93 | void* vaddr; |
michael@0 | 94 | if (graphicBuffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, |
michael@0 | 95 | &vaddr) != OK) { |
michael@0 | 96 | return; |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | uint8_t* yChannel = static_cast<uint8_t*>(vaddr); |
michael@0 | 100 | gfx::IntSize ySize = aData.mYSize; |
michael@0 | 101 | int32_t yStride = graphicBuffer->getStride(); |
michael@0 | 102 | |
michael@0 | 103 | uint8_t* vChannel = yChannel + (yStride * ySize.height); |
michael@0 | 104 | gfx::IntSize uvSize = gfx::IntSize(ySize.width / 2, |
michael@0 | 105 | ySize.height / 2); |
michael@0 | 106 | // Align to 16 bytes boundary |
michael@0 | 107 | int32_t uvStride = ((yStride / 2) + 15) & ~0x0F; |
michael@0 | 108 | uint8_t* uChannel = vChannel + (uvStride * uvSize.height); |
michael@0 | 109 | |
michael@0 | 110 | // Memory outside of the image width may not writable. If the stride |
michael@0 | 111 | // equals to the image width then we can use only one copy. |
michael@0 | 112 | if (yStride == mData.mYStride && |
michael@0 | 113 | yStride == ySize.width) { |
michael@0 | 114 | memcpy(yChannel, mData.mYChannel, yStride * ySize.height); |
michael@0 | 115 | } else { |
michael@0 | 116 | for (int i = 0; i < ySize.height; i++) { |
michael@0 | 117 | memcpy(yChannel + i * yStride, |
michael@0 | 118 | mData.mYChannel + i * mData.mYStride, |
michael@0 | 119 | ySize.width); |
michael@0 | 120 | } |
michael@0 | 121 | } |
michael@0 | 122 | if (uvStride == mData.mCbCrStride && |
michael@0 | 123 | uvStride == uvSize.width) { |
michael@0 | 124 | memcpy(uChannel, mData.mCbChannel, uvStride * uvSize.height); |
michael@0 | 125 | memcpy(vChannel, mData.mCrChannel, uvStride * uvSize.height); |
michael@0 | 126 | } else { |
michael@0 | 127 | for (int i = 0; i < uvSize.height; i++) { |
michael@0 | 128 | memcpy(uChannel + i * uvStride, |
michael@0 | 129 | mData.mCbChannel + i * mData.mCbCrStride, |
michael@0 | 130 | uvSize.width); |
michael@0 | 131 | memcpy(vChannel + i * uvStride, |
michael@0 | 132 | mData.mCrChannel + i * mData.mCbCrStride, |
michael@0 | 133 | uvSize.width); |
michael@0 | 134 | } |
michael@0 | 135 | } |
michael@0 | 136 | graphicBuffer->unlock(); |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | void GrallocImage::SetData(const GrallocData& aData) |
michael@0 | 140 | { |
michael@0 | 141 | mTextureClient = static_cast<GrallocTextureClientOGL*>(aData.mGraphicBuffer.get()); |
michael@0 | 142 | mSize = aData.mPicSize; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | /** |
michael@0 | 146 | * Converts YVU420 semi planar frames to RGB565, possibly taking different |
michael@0 | 147 | * stride values. |
michael@0 | 148 | * Needed because the Android ColorConverter class assumes that the Y and UV |
michael@0 | 149 | * channels have equal stride. |
michael@0 | 150 | */ |
michael@0 | 151 | static void |
michael@0 | 152 | ConvertYVU420SPToRGB565(void *aYData, uint32_t aYStride, |
michael@0 | 153 | void *aUVData, uint32_t aUVStride, |
michael@0 | 154 | void *aOut, |
michael@0 | 155 | uint32_t aWidth, uint32_t aHeight) |
michael@0 | 156 | { |
michael@0 | 157 | uint8_t *y = (uint8_t*)aYData; |
michael@0 | 158 | int8_t *uv = (int8_t*)aUVData; |
michael@0 | 159 | |
michael@0 | 160 | uint16_t *rgb = (uint16_t*)aOut; |
michael@0 | 161 | |
michael@0 | 162 | for (size_t i = 0; i < aHeight; i++) { |
michael@0 | 163 | for (size_t j = 0; j < aWidth; j++) { |
michael@0 | 164 | int8_t d = uv[j | 1] - 128; |
michael@0 | 165 | int8_t e = uv[j & ~1] - 128; |
michael@0 | 166 | |
michael@0 | 167 | // Constants taken from https://en.wikipedia.org/wiki/YUV |
michael@0 | 168 | int32_t r = (298 * y[j] + 409 * e + 128) >> 11; |
michael@0 | 169 | int32_t g = (298 * y[j] - 100 * d - 208 * e + 128) >> 10; |
michael@0 | 170 | int32_t b = (298 * y[j] + 516 * d + 128) >> 11; |
michael@0 | 171 | |
michael@0 | 172 | r = r > 0x1f ? 0x1f : r < 0 ? 0 : r; |
michael@0 | 173 | g = g > 0x3f ? 0x3f : g < 0 ? 0 : g; |
michael@0 | 174 | b = b > 0x1f ? 0x1f : b < 0 ? 0 : b; |
michael@0 | 175 | |
michael@0 | 176 | *rgb++ = (uint16_t)(r << 11 | g << 5 | b); |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | y += aYStride; |
michael@0 | 180 | if (i % 2) { |
michael@0 | 181 | uv += aUVStride; |
michael@0 | 182 | } |
michael@0 | 183 | } |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | TemporaryRef<gfx::SourceSurface> |
michael@0 | 187 | GrallocImage::GetAsSourceSurface() |
michael@0 | 188 | { |
michael@0 | 189 | if (!mTextureClient) { |
michael@0 | 190 | return nullptr; |
michael@0 | 191 | } |
michael@0 | 192 | android::sp<GraphicBuffer> graphicBuffer = |
michael@0 | 193 | mTextureClient->GetGraphicBuffer(); |
michael@0 | 194 | |
michael@0 | 195 | void *buffer; |
michael@0 | 196 | int32_t rv = |
michael@0 | 197 | graphicBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &buffer); |
michael@0 | 198 | |
michael@0 | 199 | if (rv) { |
michael@0 | 200 | NS_WARNING("Couldn't lock graphic buffer"); |
michael@0 | 201 | return nullptr; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | GraphicBufferAutoUnlock unlock(graphicBuffer); |
michael@0 | 205 | |
michael@0 | 206 | uint32_t format = graphicBuffer->getPixelFormat(); |
michael@0 | 207 | uint32_t omxFormat = 0; |
michael@0 | 208 | |
michael@0 | 209 | for (int i = 0; sColorIdMap[i]; i += 2) { |
michael@0 | 210 | if (sColorIdMap[i] == format) { |
michael@0 | 211 | omxFormat = sColorIdMap[i + 1]; |
michael@0 | 212 | break; |
michael@0 | 213 | } |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | if (!omxFormat) { |
michael@0 | 217 | NS_WARNING("Unknown color format"); |
michael@0 | 218 | return nullptr; |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | RefPtr<gfx::DataSourceSurface> surface |
michael@0 | 222 | = gfx::Factory::CreateDataSourceSurface(GetSize(), gfx::SurfaceFormat::R5G6B5); |
michael@0 | 223 | |
michael@0 | 224 | uint32_t width = GetSize().width; |
michael@0 | 225 | uint32_t height = GetSize().height; |
michael@0 | 226 | |
michael@0 | 227 | gfx::DataSourceSurface::MappedSurface mappedSurface; |
michael@0 | 228 | if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) { |
michael@0 | 229 | NS_WARNING("Could not map DataSourceSurface"); |
michael@0 | 230 | return nullptr; |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO) { |
michael@0 | 234 | // The Adreno hardware decoder aligns image dimensions to a multiple of 32, |
michael@0 | 235 | // so we have to account for that here |
michael@0 | 236 | uint32_t alignedWidth = ALIGN(width, 32); |
michael@0 | 237 | uint32_t alignedHeight = ALIGN(height, 32); |
michael@0 | 238 | uint32_t uvOffset = ALIGN(alignedHeight * alignedWidth, 4096); |
michael@0 | 239 | uint32_t uvStride = 2 * ALIGN(width / 2, 32); |
michael@0 | 240 | uint8_t* buffer_as_bytes = static_cast<uint8_t*>(buffer); |
michael@0 | 241 | ConvertYVU420SPToRGB565(buffer, alignedWidth, |
michael@0 | 242 | buffer_as_bytes + uvOffset, uvStride, |
michael@0 | 243 | mappedSurface.mData, |
michael@0 | 244 | width, height); |
michael@0 | 245 | |
michael@0 | 246 | surface->Unmap(); |
michael@0 | 247 | return surface; |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { |
michael@0 | 251 | uint32_t uvOffset = height * width; |
michael@0 | 252 | ConvertYVU420SPToRGB565(buffer, width, |
michael@0 | 253 | buffer + uvOffset, width, |
michael@0 | 254 | mappedSurface.mData, |
michael@0 | 255 | width, height); |
michael@0 | 256 | |
michael@0 | 257 | surface->Unmap(); |
michael@0 | 258 | return surface; |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | if (format == HAL_PIXEL_FORMAT_YV12) { |
michael@0 | 262 | gfx::ConvertYCbCrToRGB(mData, |
michael@0 | 263 | surface->GetFormat(), |
michael@0 | 264 | mSize, |
michael@0 | 265 | surface->GetData(), |
michael@0 | 266 | surface->Stride()); |
michael@0 | 267 | surface->Unmap(); |
michael@0 | 268 | return surface; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat, |
michael@0 | 272 | OMX_COLOR_Format16bitRGB565); |
michael@0 | 273 | |
michael@0 | 274 | if (!colorConverter.isValid()) { |
michael@0 | 275 | NS_WARNING("Invalid color conversion"); |
michael@0 | 276 | surface->Unmap(); |
michael@0 | 277 | return nullptr; |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | rv = colorConverter.convert(buffer, width, height, |
michael@0 | 281 | 0, 0, width - 1, height - 1 /* source crop */, |
michael@0 | 282 | mappedSurface.mData, width, height, |
michael@0 | 283 | 0, 0, width - 1, height - 1 /* dest crop */); |
michael@0 | 284 | |
michael@0 | 285 | surface->Unmap(); |
michael@0 | 286 | |
michael@0 | 287 | if (rv) { |
michael@0 | 288 | NS_WARNING("OMX color conversion failed"); |
michael@0 | 289 | return nullptr; |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | return surface; |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | android::sp<android::GraphicBuffer> |
michael@0 | 296 | GrallocImage::GetGraphicBuffer() const |
michael@0 | 297 | { |
michael@0 | 298 | if (!mTextureClient) { |
michael@0 | 299 | return nullptr; |
michael@0 | 300 | } |
michael@0 | 301 | return mTextureClient->GetGraphicBuffer(); |
michael@0 | 302 | } |
michael@0 | 303 | |
michael@0 | 304 | void* |
michael@0 | 305 | GrallocImage::GetNativeBuffer() |
michael@0 | 306 | { |
michael@0 | 307 | if (!mTextureClient) { |
michael@0 | 308 | return nullptr; |
michael@0 | 309 | } |
michael@0 | 310 | android::sp<android::GraphicBuffer> graphicBuffer = |
michael@0 | 311 | mTextureClient->GetGraphicBuffer(); |
michael@0 | 312 | if (!graphicBuffer.get()) { |
michael@0 | 313 | return nullptr; |
michael@0 | 314 | } |
michael@0 | 315 | return graphicBuffer->getNativeBuffer(); |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | TextureClient* |
michael@0 | 319 | GrallocImage::GetTextureClient(CompositableClient* aClient) |
michael@0 | 320 | { |
michael@0 | 321 | return mTextureClient; |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | } // namespace layers |
michael@0 | 325 | } // namespace mozilla |