blob: 732fe4aeb33f56af461892b64b8032defe496f78 [file] [log] [blame]
reed@google.com5d4ba882012-07-31 15:45:27 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
bsalomonafe30052015-01-16 07:32:33 -08008#include "SkSurface_Gpu.h"
9
Greg Daniel7ef28f32017-04-20 16:41:55 +000010#include "GrBackendSurface.h"
robertphillips15c42ca2016-08-04 08:45:02 -070011#include "GrContextPriv.h"
Robert Phillips0ae6faa2017-03-21 16:22:00 -040012#include "GrRenderTargetContextPriv.h"
Robert Phillips646e4292017-06-13 12:44:56 -040013#include "GrTexture.h"
Robert Phillips0ae6faa2017-03-21 16:22:00 -040014
reed@google.com5d4ba882012-07-31 15:45:27 +000015#include "SkCanvas.h"
Brian Osman47299142017-03-07 13:22:22 -050016#include "SkColorSpace_Base.h"
robertphillips@google.com97b6b072012-10-31 14:48:39 +000017#include "SkGpuDevice.h"
bsalomonafe30052015-01-16 07:32:33 -080018#include "SkImage_Base.h"
reed8b26b992015-05-07 15:36:17 -070019#include "SkImage_Gpu.h"
bsalomonafe30052015-01-16 07:32:33 -080020#include "SkImagePriv.h"
21#include "SkSurface_Base.h"
reed@google.com5d4ba882012-07-31 15:45:27 +000022
reedf037e0b2014-10-30 11:34:15 -070023#if SK_SUPPORT_GPU
24
robertphillips24e91282016-04-29 06:46:36 -070025SkSurface_Gpu::SkSurface_Gpu(sk_sp<SkGpuDevice> device)
bsalomonafe30052015-01-16 07:32:33 -080026 : INHERITED(device->width(), device->height(), &device->surfaceProps())
robertphillips24e91282016-04-29 06:46:36 -070027 , fDevice(std::move(device)) {
Robert Phillips0ae6faa2017-03-21 16:22:00 -040028 SkASSERT(fDevice->accessRenderTargetContext()->asSurfaceProxy()->priv().isExact());
robertphillips@google.com97b6b072012-10-31 14:48:39 +000029}
reed@google.com5d4ba882012-07-31 15:45:27 +000030
robertphillips@google.com97b6b072012-10-31 14:48:39 +000031SkSurface_Gpu::~SkSurface_Gpu() {
reed@google.com5d4ba882012-07-31 15:45:27 +000032}
33
joshualitt81793412015-07-08 12:54:04 -070034static GrRenderTarget* prepare_rt_for_external_access(SkSurface_Gpu* surface,
35 SkSurface::BackendHandleAccess access) {
reedfa5e68e2015-06-29 07:37:01 -070036 switch (access) {
joshualitt81793412015-07-08 12:54:04 -070037 case SkSurface::kFlushRead_BackendHandleAccess:
reedfa5e68e2015-06-29 07:37:01 -070038 break;
joshualitt81793412015-07-08 12:54:04 -070039 case SkSurface::kFlushWrite_BackendHandleAccess:
40 case SkSurface::kDiscardWrite_BackendHandleAccess:
reed884200e2015-06-29 09:00:20 -070041 // for now we don't special-case on Discard, but we may in the future.
joshualitt81793412015-07-08 12:54:04 -070042 surface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
reedfa5e68e2015-06-29 07:37:01 -070043 break;
reedfa5e68e2015-06-29 07:37:01 -070044 }
fmalitae2639082015-08-06 07:04:51 -070045
46 // Grab the render target *after* firing notifications, as it may get switched if CoW kicks in.
robertphillipsea70c4b2016-07-20 08:54:31 -070047 surface->getDevice()->flush();
Brian Osman11052242016-10-27 14:47:55 -040048 GrRenderTargetContext* rtc = surface->getDevice()->accessRenderTargetContext();
49 return rtc->accessRenderTarget();
joshualitt81793412015-07-08 12:54:04 -070050}
51
52GrBackendObject SkSurface_Gpu::onGetTextureHandle(BackendHandleAccess access) {
53 GrRenderTarget* rt = prepare_rt_for_external_access(this, access);
Robert Phillips833dcf42016-11-18 08:44:13 -050054 if (!rt) {
55 return 0;
56 }
joshualitt81793412015-07-08 12:54:04 -070057 GrTexture* texture = rt->asTexture();
58 if (texture) {
59 return texture->getTextureHandle();
60 }
61 return 0;
62}
63
64bool SkSurface_Gpu::onGetRenderTargetHandle(GrBackendObject* obj, BackendHandleAccess access) {
65 GrRenderTarget* rt = prepare_rt_for_external_access(this, access);
Robert Phillips833dcf42016-11-18 08:44:13 -050066 if (!rt) {
67 return false;
68 }
joshualitt81793412015-07-08 12:54:04 -070069 *obj = rt->getRenderTargetHandle();
70 return true;
reedfa5e68e2015-06-29 07:37:01 -070071}
72
reed@google.com5d4ba882012-07-31 15:45:27 +000073SkCanvas* SkSurface_Gpu::onNewCanvas() {
reed4a8126e2014-09-22 07:29:03 -070074 SkCanvas::InitFlags flags = SkCanvas::kDefault_InitFlags;
bsalomonb5b49742016-02-10 10:41:01 -080075 flags = static_cast<SkCanvas::InitFlags>(flags | SkCanvas::kConservativeRasterClip_InitFlag);
reed4a8126e2014-09-22 07:29:03 -070076
robertphillips24e91282016-04-29 06:46:36 -070077 return new SkCanvas(fDevice.get(), flags);
reed@google.com5d4ba882012-07-31 15:45:27 +000078}
79
reede8f30622016-03-23 18:59:25 -070080sk_sp<SkSurface> SkSurface_Gpu::onNewSurface(const SkImageInfo& info) {
Brian Osman11052242016-10-27 14:47:55 -040081 int sampleCount = fDevice->accessRenderTargetContext()->numColorSamples();
82 GrSurfaceOrigin origin = fDevice->accessRenderTargetContext()->origin();
bsalomonafe30052015-01-16 07:32:33 -080083 // TODO: Make caller specify this (change virtual signature of onNewSurface).
bsalomon5ec26ae2016-02-25 08:33:02 -080084 static const SkBudgeted kBudgeted = SkBudgeted::kNo;
reede8f30622016-03-23 18:59:25 -070085 return SkSurface::MakeRenderTarget(fDevice->context(), kBudgeted, info, sampleCount,
robertphillips7e922762016-07-26 11:38:17 -070086 origin, &this->props());
reed@google.com5d4ba882012-07-31 15:45:27 +000087}
88
Robert Phillipsac6b1fa2017-03-20 08:38:50 -040089sk_sp<SkImage> SkSurface_Gpu::onNewImageSnapshot() {
Robert Phillipse2f7d182016-12-15 09:23:05 -050090 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
91 if (!rtc) {
Robert Phillipse60ad622016-11-17 10:22:48 -050092 return nullptr;
93 }
94
Robert Phillipse2f7d182016-12-15 09:23:05 -050095 GrContext* ctx = fDevice->context();
96
Robert Phillips0ae6faa2017-03-21 16:22:00 -040097 if (!rtc->asSurfaceProxy()) {
98 return nullptr;
99 }
100
101 SkBudgeted budgeted = rtc->asSurfaceProxy()->isBudgeted();
102
103 sk_sp<GrTextureProxy> srcProxy = rtc->asTextureProxyRef();
bsalomonb2c01332016-02-26 10:37:26 -0800104 // If the original render target is a buffer originally created by the client, then we don't
105 // want to ever retarget the SkSurface at another buffer we create. Force a copy now to avoid
106 // copy-on-write.
Mike Reed7eb01f82016-12-30 06:23:12 -0500107 if (!srcProxy || rtc->priv().refsWrappedObjects()) {
Robert Phillips81dd3e02017-06-23 11:59:24 -0400108 SkASSERT(rtc->origin() == rtc->asSurfaceProxy()->origin());
Robert Phillipse2f7d182016-12-15 09:23:05 -0500109
Robert Phillips81dd3e02017-06-23 11:59:24 -0400110 srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), budgeted);
bsalomonf47b9a32016-02-22 11:02:58 -0800111 }
Robert Phillipse2f7d182016-12-15 09:23:05 -0500112
reed8b26b992015-05-07 15:36:17 -0700113 const SkImageInfo info = fDevice->imageInfo();
reede8f30622016-03-23 18:59:25 -0700114 sk_sp<SkImage> image;
Robert Phillips0ae6faa2017-03-21 16:22:00 -0400115 if (srcProxy) {
116 // The renderTargetContext coming out of SkGpuDevice should always be exact and the
117 // above copy creates a kExact surfaceContext.
118 SkASSERT(srcProxy->priv().isExact());
119 image = sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
120 info.alphaType(), std::move(srcProxy),
121 info.refColorSpace(), budgeted);
reed8b26b992015-05-07 15:36:17 -0700122 }
reed4af267b2014-11-21 08:46:37 -0800123 return image;
reed@google.com5d4ba882012-07-31 15:45:27 +0000124}
125
kkinnunenabcfab42015-02-22 22:53:44 -0800126// Create a new render target and, if necessary, copy the contents of the old
127// render target into it. Note that this flushes the SkGpuDevice but
robertphillips@google.com97b6b072012-10-31 14:48:39 +0000128// doesn't force an OpenGL flush.
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000129void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
Robert Phillips602b79c2017-06-23 11:26:28 -0400130 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
131
132 // are we sharing our backing proxy with the image? Note this call should never create a new
bsalomoneaaaf0b2015-01-23 08:08:04 -0800133 // image because onCopyOnWrite is only called when there is a cached image.
Robert Phillipsac6b1fa2017-03-20 08:38:50 -0400134 sk_sp<SkImage> image(this->refCachedImage());
bsalomoneaaaf0b2015-01-23 08:08:04 -0800135 SkASSERT(image);
Robert Phillips602b79c2017-06-23 11:26:28 -0400136
137 GrSurfaceProxy* imageProxy = ((SkImage_Base*) image.get())->peekProxy();
138 SkASSERT(imageProxy);
139
140 if (rtc->asSurfaceProxy()->underlyingUniqueID() == imageProxy->underlyingUniqueID()) {
Robert Phillips6de99042017-01-31 11:31:39 -0500141 fDevice->replaceRenderTargetContext(SkSurface::kRetain_ContentChangeMode == mode);
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +0000142 } else if (kDiscard_ContentChangeMode == mode) {
143 this->SkSurface_Gpu::onDiscard();
robertphillips@google.com97b6b072012-10-31 14:48:39 +0000144 }
reed@google.com5d4ba882012-07-31 15:45:27 +0000145}
146
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +0000147void SkSurface_Gpu::onDiscard() {
Brian Osman11052242016-10-27 14:47:55 -0400148 fDevice->accessRenderTargetContext()->discard();
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +0000149}
150
Greg Danielc64ee462017-06-15 16:59:49 -0400151bool SkSurface_Gpu::onFlush(int numSemaphores, GrBackendSemaphore* signalSemaphores) {
152 return fDevice->flushAndSignalSemaphores(numSemaphores, signalSemaphores);
Greg Daniela5cb7812017-06-16 09:45:32 -0400153}
154
Greg Danielc64ee462017-06-15 16:59:49 -0400155bool SkSurface_Gpu::onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {
156 return fDevice->wait(numSemaphores, waitSemaphores);
ericrkf7b8b8a2016-02-24 14:49:51 -0800157}
158
reed@google.com5d4ba882012-07-31 15:45:27 +0000159///////////////////////////////////////////////////////////////////////////////
160
brianosman0e22eb82016-08-30 07:07:59 -0700161bool SkSurface_Gpu::Valid(const SkImageInfo& info) {
162 switch (info.colorType()) {
163 case kRGBA_F16_SkColorType:
Matt Sarettb6759dd2017-06-12 13:46:11 -0400164 return info.colorSpace() && info.colorSpace()->gammaIsLinear();
brianosman0e22eb82016-08-30 07:07:59 -0700165 case kRGBA_8888_SkColorType:
166 case kBGRA_8888_SkColorType:
167 return !info.colorSpace() || info.colorSpace()->gammaCloseToSRGB();
168 default:
169 return !info.colorSpace();
170 }
171}
172
173bool SkSurface_Gpu::Valid(GrContext* context, GrPixelConfig config, SkColorSpace* colorSpace) {
174 switch (config) {
175 case kRGBA_half_GrPixelConfig:
Matt Sarettb6759dd2017-06-12 13:46:11 -0400176 return colorSpace && colorSpace->gammaIsLinear();
brianosman0e22eb82016-08-30 07:07:59 -0700177 case kSRGBA_8888_GrPixelConfig:
178 case kSBGRA_8888_GrPixelConfig:
Matt Sarettf3880932017-03-24 10:06:03 -0400179 return context->caps()->srgbSupport() && colorSpace && colorSpace->gammaCloseToSRGB();
brianosman0e22eb82016-08-30 07:07:59 -0700180 case kRGBA_8888_GrPixelConfig:
181 case kBGRA_8888_GrPixelConfig:
182 // If we don't have sRGB support, we may get here with a color space. It still needs
183 // to be sRGB-like (so that the application will work correctly on sRGB devices.)
184 return !colorSpace ||
Matt Sarettf3880932017-03-24 10:06:03 -0400185 (colorSpace->gammaCloseToSRGB() && !context->caps()->srgbSupport());
brianosman0e22eb82016-08-30 07:07:59 -0700186 default:
187 return !colorSpace;
188 }
189}
190
reede8f30622016-03-23 18:59:25 -0700191sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrContext* ctx, SkBudgeted budgeted,
192 const SkImageInfo& info, int sampleCount,
robertphillips7e922762016-07-26 11:38:17 -0700193 GrSurfaceOrigin origin, const SkSurfaceProps* props) {
brianosman0e22eb82016-08-30 07:07:59 -0700194 if (!SkSurface_Gpu::Valid(info)) {
195 return nullptr;
196 }
197
robertphillips24e91282016-04-29 06:46:36 -0700198 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(
robertphillips7e922762016-07-26 11:38:17 -0700199 ctx, budgeted, info, sampleCount, origin, props, SkGpuDevice::kClear_InitContents));
bsalomonafe30052015-01-16 07:32:33 -0800200 if (!device) {
halcanary96fcdcc2015-08-27 07:41:13 -0700201 return nullptr;
reed@google.com5d4ba882012-07-31 15:45:27 +0000202 }
robertphillips24e91282016-04-29 06:46:36 -0700203 return sk_make_sp<SkSurface_Gpu>(std::move(device));
reed@google.com5d4ba882012-07-31 15:45:27 +0000204}
reedf037e0b2014-10-30 11:34:15 -0700205
reede8f30622016-03-23 18:59:25 -0700206sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context,
207 const GrBackendTextureDesc& desc,
brianosmandfe4f2e2016-07-21 13:28:36 -0700208 sk_sp<SkColorSpace> colorSpace,
reede8f30622016-03-23 18:59:25 -0700209 const SkSurfaceProps* props) {
robertphillips15c42ca2016-08-04 08:45:02 -0700210 if (!context) {
halcanary96fcdcc2015-08-27 07:41:13 -0700211 return nullptr;
bsalomone4579ad2015-04-08 08:38:40 -0700212 }
Greg Daniel7ef28f32017-04-20 16:41:55 +0000213 GrBackendTexture tex(desc, context->contextPriv().getBackend());
214 return MakeFromBackendTexture(context, tex, desc.fOrigin, desc.fSampleCnt, colorSpace, props);
215}
216
217sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context, const GrBackendTexture& tex,
218 GrSurfaceOrigin origin, int sampleCnt,
219 sk_sp<SkColorSpace> colorSpace,
220 const SkSurfaceProps* props) {
221 if (!context) {
halcanary96fcdcc2015-08-27 07:41:13 -0700222 return nullptr;
bsalomone4579ad2015-04-08 08:38:40 -0700223 }
Greg Daniel7ef28f32017-04-20 16:41:55 +0000224 if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) {
brianosman0e22eb82016-08-30 07:07:59 -0700225 return nullptr;
226 }
robertphillips15c42ca2016-08-04 08:45:02 -0700227
Brian Osman11052242016-10-27 14:47:55 -0400228 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeBackendTextureRenderTargetContext(
Greg Daniel7ef28f32017-04-20 16:41:55 +0000229 tex,
230 origin,
231 sampleCnt,
robertphillips15c42ca2016-08-04 08:45:02 -0700232 std::move(colorSpace),
Brian Osmanc1e37052017-03-09 14:19:20 -0500233 props));
Brian Osman11052242016-10-27 14:47:55 -0400234 if (!rtc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700235 return nullptr;
bsalomone4579ad2015-04-08 08:38:40 -0700236 }
robertphillips15c42ca2016-08-04 08:45:02 -0700237
Greg Daniel7ef28f32017-04-20 16:41:55 +0000238 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(),
robertphillips24e91282016-04-29 06:46:36 -0700239 SkGpuDevice::kUninit_InitContents));
bsalomone4579ad2015-04-08 08:38:40 -0700240 if (!device) {
halcanary96fcdcc2015-08-27 07:41:13 -0700241 return nullptr;
bsalomone4579ad2015-04-08 08:38:40 -0700242 }
robertphillips24e91282016-04-29 06:46:36 -0700243 return sk_make_sp<SkSurface_Gpu>(std::move(device));
bsalomone4579ad2015-04-08 08:38:40 -0700244}
245
reede8f30622016-03-23 18:59:25 -0700246sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context,
247 const GrBackendRenderTargetDesc& desc,
brianosmandfe4f2e2016-07-21 13:28:36 -0700248 sk_sp<SkColorSpace> colorSpace,
reede8f30622016-03-23 18:59:25 -0700249 const SkSurfaceProps* props) {
robertphillips24e91282016-04-29 06:46:36 -0700250 if (!context) {
halcanary96fcdcc2015-08-27 07:41:13 -0700251 return nullptr;
bsalomond3e259a2015-06-30 12:04:40 -0700252 }
Greg Danielbcf612b2017-05-01 13:50:58 +0000253
254 GrBackendRenderTarget backendRT(desc, context->contextPriv().getBackend());
255 return MakeFromBackendRenderTarget(context, backendRT, desc.fOrigin,
256 std::move(colorSpace), props);
257
258}
259
260sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context,
261 const GrBackendRenderTarget& backendRT,
262 GrSurfaceOrigin origin,
263 sk_sp<SkColorSpace> colorSpace,
264 const SkSurfaceProps* props) {
265 if (!context) {
266 return nullptr;
267 }
268 if (!SkSurface_Gpu::Valid(context, backendRT.config(), colorSpace.get())) {
brianosman0e22eb82016-08-30 07:07:59 -0700269 return nullptr;
270 }
robertphillips15c42ca2016-08-04 08:45:02 -0700271
Brian Osman11052242016-10-27 14:47:55 -0400272 sk_sp<GrRenderTargetContext> rtc(
Greg Danielbcf612b2017-05-01 13:50:58 +0000273 context->contextPriv().makeBackendRenderTargetRenderTargetContext(backendRT,
274 origin,
Brian Osman11052242016-10-27 14:47:55 -0400275 std::move(colorSpace),
276 props));
277 if (!rtc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700278 return nullptr;
bsalomond3e259a2015-06-30 12:04:40 -0700279 }
robertphillips15c42ca2016-08-04 08:45:02 -0700280
Greg Danielbcf612b2017-05-01 13:50:58 +0000281 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc),
282 backendRT.width(), backendRT.height(),
robertphillips24e91282016-04-29 06:46:36 -0700283 SkGpuDevice::kUninit_InitContents));
bsalomond3e259a2015-06-30 12:04:40 -0700284 if (!device) {
halcanary96fcdcc2015-08-27 07:41:13 -0700285 return nullptr;
bsalomond3e259a2015-06-30 12:04:40 -0700286 }
robertphillips15c42ca2016-08-04 08:45:02 -0700287
robertphillips24e91282016-04-29 06:46:36 -0700288 return sk_make_sp<SkSurface_Gpu>(std::move(device));
bsalomond3e259a2015-06-30 12:04:40 -0700289}
290
reede8f30622016-03-23 18:59:25 -0700291sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context,
292 const GrBackendTextureDesc& desc,
brianosmandfe4f2e2016-07-21 13:28:36 -0700293 sk_sp<SkColorSpace> colorSpace,
reede8f30622016-03-23 18:59:25 -0700294 const SkSurfaceProps* props) {
robertphillips24e91282016-04-29 06:46:36 -0700295 if (!context) {
ericrkf7b8b8a2016-02-24 14:49:51 -0800296 return nullptr;
297 }
Greg Daniel7ef28f32017-04-20 16:41:55 +0000298 GrBackendTexture tex(desc, context->contextPriv().getBackend());
299 return MakeFromBackendTextureAsRenderTarget(context, tex, desc.fOrigin, desc.fSampleCnt,
300 std::move(colorSpace), props);
301}
302
303sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context,
304 const GrBackendTexture& tex,
305 GrSurfaceOrigin origin,
306 int sampleCnt,
307 sk_sp<SkColorSpace> colorSpace,
308 const SkSurfaceProps* props) {
309 if (!context) {
310 return nullptr;
311 }
312 if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) {
brianosman0e22eb82016-08-30 07:07:59 -0700313 return nullptr;
314 }
robertphillips15c42ca2016-08-04 08:45:02 -0700315
Brian Osman11052242016-10-27 14:47:55 -0400316 sk_sp<GrRenderTargetContext> rtc(
317 context->contextPriv().makeBackendTextureAsRenderTargetRenderTargetContext(
Greg Daniel7ef28f32017-04-20 16:41:55 +0000318 tex,
319 origin,
320 sampleCnt,
Brian Osman11052242016-10-27 14:47:55 -0400321 std::move(colorSpace),
322 props));
323 if (!rtc) {
ericrkf7b8b8a2016-02-24 14:49:51 -0800324 return nullptr;
325 }
robertphillips15c42ca2016-08-04 08:45:02 -0700326
Greg Daniel7ef28f32017-04-20 16:41:55 +0000327 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(),
robertphillips24e91282016-04-29 06:46:36 -0700328 SkGpuDevice::kUninit_InitContents));
ericrkf7b8b8a2016-02-24 14:49:51 -0800329 if (!device) {
330 return nullptr;
331 }
robertphillips24e91282016-04-29 06:46:36 -0700332 return sk_make_sp<SkSurface_Gpu>(std::move(device));
ericrkf7b8b8a2016-02-24 14:49:51 -0800333}
334
reedf037e0b2014-10-30 11:34:15 -0700335#endif