OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2011 Google Inc. | 3 * Copyright 2011 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 #include "GrContext.h" | 9 #include "GrContext.h" |
10 | 10 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 | 48 |
49 #include "batches/GrBatch.h" | 49 #include "batches/GrBatch.h" |
50 | 50 |
51 #include "effects/GrConfigConversionEffect.h" | 51 #include "effects/GrConfigConversionEffect.h" |
52 #include "effects/GrDashingEffect.h" | 52 #include "effects/GrDashingEffect.h" |
53 #include "effects/GrSingleTextureEffect.h" | 53 #include "effects/GrSingleTextureEffect.h" |
54 | 54 |
55 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this) | 55 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this) |
56 #define RETURN_IF_ABANDONED if (fDrawingMgr.abandoned()) { return; } | 56 #define RETURN_IF_ABANDONED if (fDrawingMgr.abandoned()) { return; } |
57 #define RETURN_FALSE_IF_ABANDONED if (fDrawingMgr.abandoned()) { return false; } | 57 #define RETURN_FALSE_IF_ABANDONED if (fDrawingMgr.abandoned()) { return false; } |
58 #define RETURN_NULL_IF_ABANDONED if (fDrawingMgr.abandoned()) { return NULL; } | 58 #define RETURN_NULL_IF_ABANDONED if (fDrawingMgr.abandoned()) { return nullptr;
} |
59 | 59 |
60 | 60 |
61 //////////////////////////////////////////////////////////////////////////////// | 61 //////////////////////////////////////////////////////////////////////////////// |
62 | 62 |
63 void GrContext::DrawingMgr::init(GrContext* context) { | 63 void GrContext::DrawingMgr::init(GrContext* context) { |
64 fContext = context; | 64 fContext = context; |
65 | 65 |
66 #ifdef IMMEDIATE_MODE | 66 #ifdef IMMEDIATE_MODE |
67 fDrawTarget = new GrImmediateDrawTarget(context); | 67 fDrawTarget = new GrImmediateDrawTarget(context); |
68 #else | 68 #else |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 } | 107 } |
108 | 108 |
109 void GrContext::DrawingMgr::flush() { | 109 void GrContext::DrawingMgr::flush() { |
110 if (fDrawTarget) { | 110 if (fDrawTarget) { |
111 fDrawTarget->flush(); | 111 fDrawTarget->flush(); |
112 } | 112 } |
113 } | 113 } |
114 | 114 |
115 GrDrawContext* GrContext::DrawingMgr::drawContext(const SkSurfaceProps* surfaceP
rops) { | 115 GrDrawContext* GrContext::DrawingMgr::drawContext(const SkSurfaceProps* surfaceP
rops) { |
116 if (this->abandoned()) { | 116 if (this->abandoned()) { |
117 return NULL; | 117 return nullptr; |
118 } | 118 } |
119 | 119 |
120 const SkSurfaceProps props(SkSurfacePropsCopyOrDefault(surfaceProps)); | 120 const SkSurfaceProps props(SkSurfacePropsCopyOrDefault(surfaceProps)); |
121 | 121 |
122 SkASSERT(props.pixelGeometry() < kNumPixelGeometries); | 122 SkASSERT(props.pixelGeometry() < kNumPixelGeometries); |
123 if (!fDrawContext[props.pixelGeometry()][props.isUseDistanceFieldFonts()]) { | 123 if (!fDrawContext[props.pixelGeometry()][props.isUseDistanceFieldFonts()]) { |
124 fDrawContext[props.pixelGeometry()][props.isUseDistanceFieldFonts()] = | 124 fDrawContext[props.pixelGeometry()][props.isUseDistanceFieldFonts()] = |
125 new GrDrawContext(fContext, fDrawTarget, props); | 125 new GrDrawContext(fContext, fDrawTarget, props); |
126 } | 126 } |
127 | 127 |
128 return fDrawContext[props.pixelGeometry()][props.isUseDistanceFieldFonts()];
| 128 return fDrawContext[props.pixelGeometry()][props.isUseDistanceFieldFonts()];
|
129 } | 129 } |
130 | 130 |
131 //////////////////////////////////////////////////////////////////////////////// | 131 //////////////////////////////////////////////////////////////////////////////// |
132 | 132 |
133 | 133 |
134 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext)
{ | 134 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext)
{ |
135 GrContextOptions defaultOptions; | 135 GrContextOptions defaultOptions; |
136 return Create(backend, backendContext, defaultOptions); | 136 return Create(backend, backendContext, defaultOptions); |
137 } | 137 } |
138 | 138 |
139 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext, | 139 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext, |
140 const GrContextOptions& options) { | 140 const GrContextOptions& options) { |
141 GrContext* context = new GrContext; | 141 GrContext* context = new GrContext; |
142 | 142 |
143 if (context->init(backend, backendContext, options)) { | 143 if (context->init(backend, backendContext, options)) { |
144 return context; | 144 return context; |
145 } else { | 145 } else { |
146 context->unref(); | 146 context->unref(); |
147 return NULL; | 147 return nullptr; |
148 } | 148 } |
149 } | 149 } |
150 | 150 |
151 static int32_t gNextID = 1; | 151 static int32_t gNextID = 1; |
152 static int32_t next_id() { | 152 static int32_t next_id() { |
153 int32_t id; | 153 int32_t id; |
154 do { | 154 do { |
155 id = sk_atomic_inc(&gNextID); | 155 id = sk_atomic_inc(&gNextID); |
156 } while (id == SK_InvalidGenID); | 156 } while (id == SK_InvalidGenID); |
157 return id; | 157 return id; |
158 } | 158 } |
159 | 159 |
160 GrContext::GrContext() : fUniqueID(next_id()) { | 160 GrContext::GrContext() : fUniqueID(next_id()) { |
161 fGpu = NULL; | 161 fGpu = nullptr; |
162 fCaps = NULL; | 162 fCaps = nullptr; |
163 fResourceCache = NULL; | 163 fResourceCache = nullptr; |
164 fResourceProvider = NULL; | 164 fResourceProvider = nullptr; |
165 fPathRendererChain = NULL; | 165 fPathRendererChain = nullptr; |
166 fSoftwarePathRenderer = NULL; | 166 fSoftwarePathRenderer = nullptr; |
167 fBatchFontCache = NULL; | 167 fBatchFontCache = nullptr; |
168 fFlushToReduceCacheSize = false; | 168 fFlushToReduceCacheSize = false; |
169 } | 169 } |
170 | 170 |
171 bool GrContext::init(GrBackend backend, GrBackendContext backendContext, | 171 bool GrContext::init(GrBackend backend, GrBackendContext backendContext, |
172 const GrContextOptions& options) { | 172 const GrContextOptions& options) { |
173 SkASSERT(!fGpu); | 173 SkASSERT(!fGpu); |
174 | 174 |
175 fGpu = GrGpu::Create(backend, backendContext, options, this); | 175 fGpu = GrGpu::Create(backend, backendContext, options, this); |
176 if (!fGpu) { | 176 if (!fGpu) { |
177 return false; | 177 return false; |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 } else { | 301 } else { |
302 fDrawingMgr.flush(); | 302 fDrawingMgr.flush(); |
303 } | 303 } |
304 fResourceCache->notifyFlushOccurred(); | 304 fResourceCache->notifyFlushOccurred(); |
305 fFlushToReduceCacheSize = false; | 305 fFlushToReduceCacheSize = false; |
306 } | 306 } |
307 | 307 |
308 bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t
inRowBytes, | 308 bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t
inRowBytes, |
309 const void* inPixels, size_t outRowBytes, void* outPix
els) { | 309 const void* inPixels, size_t outRowBytes, void* outPix
els) { |
310 SkSrcPixelInfo srcPI; | 310 SkSrcPixelInfo srcPI; |
311 if (!GrPixelConfig2ColorAndProfileType(srcConfig, &srcPI.fColorType, NULL))
{ | 311 if (!GrPixelConfig2ColorAndProfileType(srcConfig, &srcPI.fColorType, nullptr
)) { |
312 return false; | 312 return false; |
313 } | 313 } |
314 srcPI.fAlphaType = kUnpremul_SkAlphaType; | 314 srcPI.fAlphaType = kUnpremul_SkAlphaType; |
315 srcPI.fPixels = inPixels; | 315 srcPI.fPixels = inPixels; |
316 srcPI.fRowBytes = inRowBytes; | 316 srcPI.fRowBytes = inRowBytes; |
317 | 317 |
318 SkDstPixelInfo dstPI; | 318 SkDstPixelInfo dstPI; |
319 dstPI.fColorType = srcPI.fColorType; | 319 dstPI.fColorType = srcPI.fColorType; |
320 dstPI.fAlphaType = kPremul_SkAlphaType; | 320 dstPI.fAlphaType = kPremul_SkAlphaType; |
321 dstPI.fPixels = outPixels; | 321 dstPI.fPixels = outPixels; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 SkAutoTUnref<const GrFragmentProcessor> fp; | 378 SkAutoTUnref<const GrFragmentProcessor> fp; |
379 SkMatrix textureMatrix; | 379 SkMatrix textureMatrix; |
380 textureMatrix.setIDiv(tempTexture->width(), tempTexture->height()); | 380 textureMatrix.setIDiv(tempTexture->width(), tempTexture->height()); |
381 GrPaint paint; | 381 GrPaint paint; |
382 if (applyPremulToSrc) { | 382 if (applyPremulToSrc) { |
383 fp.reset(this->createUPMToPMEffect(paint.getProcessorDataManager(),
tempTexture, | 383 fp.reset(this->createUPMToPMEffect(paint.getProcessorDataManager(),
tempTexture, |
384 tempDrawInfo.fSwapRAndB, textureM
atrix)); | 384 tempDrawInfo.fSwapRAndB, textureM
atrix)); |
385 // If premultiplying was the only reason for the draw, fall back to
a straight write. | 385 // If premultiplying was the only reason for the draw, fall back to
a straight write. |
386 if (!fp) { | 386 if (!fp) { |
387 if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPreference)
{ | 387 if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPreference)
{ |
388 tempTexture.reset(NULL); | 388 tempTexture.reset(nullptr); |
389 } | 389 } |
390 } else { | 390 } else { |
391 applyPremulToSrc = false; | 391 applyPremulToSrc = false; |
392 } | 392 } |
393 } | 393 } |
394 if (tempTexture) { | 394 if (tempTexture) { |
395 if (!fp) { | 395 if (!fp) { |
396 fp.reset(GrConfigConversionEffect::Create( | 396 fp.reset(GrConfigConversionEffect::Create( |
397 paint.getProcessorDataManager(), tempTexture, tempDrawInfo.f
SwapRAndB, | 397 paint.getProcessorDataManager(), tempTexture, tempDrawInfo.f
SwapRAndB, |
398 GrConfigConversionEffect::kNone_PMConversion, textureMatrix)
); | 398 GrConfigConversionEffect::kNone_PMConversion, textureMatrix)
); |
(...skipping 23 matching lines...) Expand all Loading... |
422 return false; | 422 return false; |
423 } | 423 } |
424 SkMatrix matrix; | 424 SkMatrix matrix; |
425 matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top)); | 425 matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top)); |
426 GrDrawContext* drawContext = this->drawContext(); | 426 GrDrawContext* drawContext = this->drawContext(); |
427 if (!drawContext) { | 427 if (!drawContext) { |
428 return false; | 428 return false; |
429 } | 429 } |
430 paint.addColorFragmentProcessor(fp); | 430 paint.addColorFragmentProcessor(fp); |
431 SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(hei
ght)); | 431 SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(hei
ght)); |
432 drawContext->drawRect(renderTarget, GrClip::WideOpen(), paint, matri
x, rect, NULL); | 432 drawContext->drawRect(renderTarget, GrClip::WideOpen(), paint, matri
x, rect, nullptr); |
433 | 433 |
434 if (kFlushWrites_PixelOp & pixelOpsFlags) { | 434 if (kFlushWrites_PixelOp & pixelOpsFlags) { |
435 this->flushSurfaceWrites(surface); | 435 this->flushSurfaceWrites(surface); |
436 } | 436 } |
437 } | 437 } |
438 } | 438 } |
439 if (!tempTexture) { | 439 if (!tempTexture) { |
440 if (applyPremulToSrc) { | 440 if (applyPremulToSrc) { |
441 size_t tmpRowBytes = 4 * width; | 441 size_t tmpRowBytes = 4 * width; |
442 tmpPixels.reset(width * height); | 442 tmpPixels.reset(width * height); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
516 SkAutoTUnref<const GrFragmentProcessor> fp; | 516 SkAutoTUnref<const GrFragmentProcessor> fp; |
517 if (unpremul) { | 517 if (unpremul) { |
518 fp.reset(this->createPMToUPMEffect( | 518 fp.reset(this->createPMToUPMEffect( |
519 paint.getProcessorDataManager(), src->asTexture(), tempDrawI
nfo.fSwapRAndB, | 519 paint.getProcessorDataManager(), src->asTexture(), tempDrawI
nfo.fSwapRAndB, |
520 textureMatrix)); | 520 textureMatrix)); |
521 if (fp) { | 521 if (fp) { |
522 unpremul = false; // we no longer need to do this on CPU aft
er the read back. | 522 unpremul = false; // we no longer need to do this on CPU aft
er the read back. |
523 } else if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPrefe
rence) { | 523 } else if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPrefe
rence) { |
524 // We only wanted to do the draw in order to perform the unp
remul so don't | 524 // We only wanted to do the draw in order to perform the unp
remul so don't |
525 // bother. | 525 // bother. |
526 temp.reset(NULL); | 526 temp.reset(nullptr); |
527 } | 527 } |
528 } | 528 } |
529 if (!fp && temp) { | 529 if (!fp && temp) { |
530 fp.reset(GrConfigConversionEffect::Create( | 530 fp.reset(GrConfigConversionEffect::Create( |
531 paint.getProcessorDataManager(), src->asTexture(), tempDrawI
nfo.fSwapRAndB, | 531 paint.getProcessorDataManager(), src->asTexture(), tempDrawI
nfo.fSwapRAndB, |
532 GrConfigConversionEffect::kNone_PMConversion, textureMatrix)
); | 532 GrConfigConversionEffect::kNone_PMConversion, textureMatrix)
); |
533 } | 533 } |
534 if (fp) { | 534 if (fp) { |
535 paint.addColorFragmentProcessor(fp); | 535 paint.addColorFragmentProcessor(fp); |
536 SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar
(height)); | 536 SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar
(height)); |
537 GrDrawContext* drawContext = this->drawContext(); | 537 GrDrawContext* drawContext = this->drawContext(); |
538 drawContext->drawRect(temp->asRenderTarget(), GrClip::WideOpen()
, paint, | 538 drawContext->drawRect(temp->asRenderTarget(), GrClip::WideOpen()
, paint, |
539 SkMatrix::I(), rect, NULL); | 539 SkMatrix::I(), rect, nullptr); |
540 surfaceToRead.reset(SkRef(temp.get())); | 540 surfaceToRead.reset(SkRef(temp.get())); |
541 left = 0; | 541 left = 0; |
542 top = 0; | 542 top = 0; |
543 didTempDraw = true; | 543 didTempDraw = true; |
544 } | 544 } |
545 } | 545 } |
546 } | 546 } |
547 | 547 |
548 if (GrGpu::kRequireDraw_DrawPreference == drawPreference && !didTempDraw) { | 548 if (GrGpu::kRequireDraw_DrawPreference == drawPreference && !didTempDraw) { |
549 return false; | 549 return false; |
550 } | 550 } |
551 GrPixelConfig configToRead = dstConfig; | 551 GrPixelConfig configToRead = dstConfig; |
552 if (didTempDraw) { | 552 if (didTempDraw) { |
553 this->flushSurfaceWrites(surfaceToRead); | 553 this->flushSurfaceWrites(surfaceToRead); |
554 // We swapped R and B while doing the temp draw. Swap back on the read. | 554 // We swapped R and B while doing the temp draw. Swap back on the read. |
555 if (tempDrawInfo.fSwapRAndB) { | 555 if (tempDrawInfo.fSwapRAndB) { |
556 configToRead = GrPixelConfigSwapRAndB(dstConfig); | 556 configToRead = GrPixelConfigSwapRAndB(dstConfig); |
557 } | 557 } |
558 } | 558 } |
559 if (!fGpu->readPixels(surfaceToRead, left, top, width, height, configToRead,
buffer, | 559 if (!fGpu->readPixels(surfaceToRead, left, top, width, height, configToRead,
buffer, |
560 rowBytes)) { | 560 rowBytes)) { |
561 return false; | 561 return false; |
562 } | 562 } |
563 | 563 |
564 // Perform umpremul conversion if we weren't able to perform it as a draw. | 564 // Perform umpremul conversion if we weren't able to perform it as a draw. |
565 if (unpremul) { | 565 if (unpremul) { |
566 SkDstPixelInfo dstPI; | 566 SkDstPixelInfo dstPI; |
567 if (!GrPixelConfig2ColorAndProfileType(dstConfig, &dstPI.fColorType, NUL
L)) { | 567 if (!GrPixelConfig2ColorAndProfileType(dstConfig, &dstPI.fColorType, nul
lptr)) { |
568 return false; | 568 return false; |
569 } | 569 } |
570 dstPI.fAlphaType = kUnpremul_SkAlphaType; | 570 dstPI.fAlphaType = kUnpremul_SkAlphaType; |
571 dstPI.fPixels = buffer; | 571 dstPI.fPixels = buffer; |
572 dstPI.fRowBytes = rowBytes; | 572 dstPI.fRowBytes = rowBytes; |
573 | 573 |
574 SkSrcPixelInfo srcPI; | 574 SkSrcPixelInfo srcPI; |
575 srcPI.fColorType = dstPI.fColorType; | 575 srcPI.fColorType = dstPI.fColorType; |
576 srcPI.fAlphaType = kPremul_SkAlphaType; | 576 srcPI.fAlphaType = kPremul_SkAlphaType; |
577 srcPI.fPixels = buffer; | 577 srcPI.fPixels = buffer; |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
701 if (!fDidTestPMConversions) { | 701 if (!fDidTestPMConversions) { |
702 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion); | 702 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion); |
703 fDidTestPMConversions = true; | 703 fDidTestPMConversions = true; |
704 } | 704 } |
705 GrConfigConversionEffect::PMConversion pmToUPM = | 705 GrConfigConversionEffect::PMConversion pmToUPM = |
706 static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion); | 706 static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion); |
707 if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) { | 707 if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) { |
708 return GrConfigConversionEffect::Create(procDataManager, texture, swapRA
ndB, pmToUPM, | 708 return GrConfigConversionEffect::Create(procDataManager, texture, swapRA
ndB, pmToUPM, |
709 matrix); | 709 matrix); |
710 } else { | 710 } else { |
711 return NULL; | 711 return nullptr; |
712 } | 712 } |
713 } | 713 } |
714 | 714 |
715 const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrProcessorDataManager
* procDataManager, | 715 const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrProcessorDataManager
* procDataManager, |
716 GrTexture* texture, | 716 GrTexture* texture, |
717 bool swapRAndB, | 717 bool swapRAndB, |
718 const SkMatrix& matrix
) { | 718 const SkMatrix& matrix
) { |
719 if (!fDidTestPMConversions) { | 719 if (!fDidTestPMConversions) { |
720 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion); | 720 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion); |
721 fDidTestPMConversions = true; | 721 fDidTestPMConversions = true; |
722 } | 722 } |
723 GrConfigConversionEffect::PMConversion upmToPM = | 723 GrConfigConversionEffect::PMConversion upmToPM = |
724 static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion); | 724 static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion); |
725 if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) { | 725 if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) { |
726 return GrConfigConversionEffect::Create(procDataManager, texture, swapRA
ndB, upmToPM, | 726 return GrConfigConversionEffect::Create(procDataManager, texture, swapRA
ndB, upmToPM, |
727 matrix); | 727 matrix); |
728 } else { | 728 } else { |
729 return NULL; | 729 return nullptr; |
730 } | 730 } |
731 } | 731 } |
732 | 732 |
733 bool GrContext::didFailPMUPMConversionTest() const { | 733 bool GrContext::didFailPMUPMConversionTest() const { |
734 // The PM<->UPM tests fail or succeed together so we only need to check one. | 734 // The PM<->UPM tests fail or succeed together so we only need to check one. |
735 return fDidTestPMConversions && | 735 return fDidTestPMConversions && |
736 GrConfigConversionEffect::kNone_PMConversion == fPMToUPMConversion; | 736 GrConfigConversionEffect::kNone_PMConversion == fPMToUPMConversion; |
737 } | 737 } |
738 | 738 |
739 ////////////////////////////////////////////////////////////////////////////// | 739 ////////////////////////////////////////////////////////////////////////////// |
(...skipping 14 matching lines...) Expand all Loading... |
754 ////////////////////////////////////////////////////////////////////////////// | 754 ////////////////////////////////////////////////////////////////////////////// |
755 | 755 |
756 void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) { | 756 void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) { |
757 fGpu->addGpuTraceMarker(marker); | 757 fGpu->addGpuTraceMarker(marker); |
758 } | 758 } |
759 | 759 |
760 void GrContext::removeGpuTraceMarker(const GrGpuTraceMarker* marker) { | 760 void GrContext::removeGpuTraceMarker(const GrGpuTraceMarker* marker) { |
761 fGpu->removeGpuTraceMarker(marker); | 761 fGpu->removeGpuTraceMarker(marker); |
762 } | 762 } |
763 | 763 |
OLD | NEW |